ares-ide
Version:
A browser-based code editor and UI designer for Enyo 2 projects
1,003 lines (903 loc) • 33.8 kB
JavaScript
/*global ProjectKindsModel, enyo, ares, setTimeout */
enyo.kind({
name: "Deimos",
classes: "enyo-unselectable onyx",
debug: false,
published: {
projectData: null // All the project data shared mainly between phobos and deimos
},
components: [
{name: "actionPopup", kind:"PaletteComponentActionPopup", centered: true, floating: true, autoDismiss: false, modal: true},
{kind: "FittableRows", classes: "enyo-fit", components: [
{name: "body", fit: true, classes: "deimos_panel_body", kind: "FittableColumns", components: [
{name: "palette", classes:"ares_deimos_left", kind: "Palette", ondragstart: "paletteDragStart"},
{name: "middle", classes: "ares-deimos-middle", fit: true, kind: "FittableRows", components: [
{kind: "onyx.MoreToolbar", classes: "deimos-toolbar", components: [
{kind: "onyx.Button", name: "reloadDesignerButton", classes: "deimos-designer-toolbar-spacing", content: "Reload", ontap: "reloadDesigner"},
{content: "Fit"},
{kind: "onyx.Checkbox", onchange: "autoZoomDesigner"}, //checkbox is here as a workaround for ENYO-3648
{content: "Size:"},
{kind: "onyx.PickerDecorator", classes: "deimos-device-picker deimos-designer-toolbar-spacing", components: [
{style: "width:100%;"},
{kind: "onyx.Picker", name: "devicePicker", ontap: "deviceChosen", components: [
{content: "(600 x 800) Default", value: { height: 800, width: 600 }},
{content: "(1024 x 600) HP Slate7", value: { height: 1024, width: 600, ppi: 170, dpr: 1 }},
{content: "(1920 x 1080) HDTV", value: { height: 1080, width: 1920 }},
{content: "(320 x 480) iPhone\u2122", value: { height: 480, width: 320 }},
{content: "(320 x 573) iPhone\u2122 5", value: { height: 573, width: 320 }},
{content: "(1024 x 768) iPad\u2122", value: { height: 768, width: 1024 }},
{content: "Custom"}
]}
]},
{content: "Width:"},
{kind: "onyx.InputDecorator", components: [
{kind: "onyx.Input", name: "designerWidthInput", classes: "deimos-designer-input", placeholder: "Auto", onchange: "updateWidth"}
]},
{kind: "onyx.Button", name: "swapDesignerDimensionsButton", classes: "deimos-swap-dimensions-button", allowHtml: true, content: "←<br/>→", ontap: "swapDesignerDimensions"},
{content: "Height:"},
{kind: "onyx.InputDecorator", components: [
{kind: "onyx.Input", name: "designerHeightInput", classes: "deimos-designer-input", placeholder: "Auto", onchange: "updateHeight"}
]},
{content: "Zoom"},
{kind: "onyx.PickerDecorator", components: [
{name: "zoomPickerButton"},
{name: "zoomPicker", kind: "onyx.Picker", onSelect: "zoomDesigner", components: []}
]}
]},
{kind: "Scroller", classes: "deimos-designer-wrapper", fit: true, components: [
{kind: "Designer", name: "designer",
onSelect: "designerSelect",
onSelected: "designerSelected",
onMoveItem: "moveItem",
onCreateItem: "createItem",
onSyncDropTargetHighlighting: "syncComponentViewDropTargetHighlighting",
onReloadComplete: "reloadComplete",
onResizeItem: "resizeItem",
onReturnPositionValue: "designerReturnPositionValue"
}
]}
]},
{name: "right", classes:"ares_deimos_right", kind: "FittableRows", components: [
{kind: "onyx.MoreToolbar", classes: "deimos-toolbar deimos-toolbar-margined-buttons", components: [
{name:"deleteButton", kind: "onyx.Button", content: "Delete", classes: "btn-danger", ontap: "deleteAction"},
{name:"undoButton", kind: "onyx.Button", content: "Undo", classes: "btn-danger", ontap: "undoAction"},
{name:"redoButton", kind: "onyx.Button", content: "Redo", classes: "btn-danger", ontap: "redoAction"}
]},
{kind: "ComponentView", classes: "deimos_panel ares_deimos_componentView",
onSelect: "componentViewSelect",
onHighlightDropTarget: "highlightDesignerDropTarget",
onUnHighlightDropTargets: "unhighlightDesignerDropTargets",
onMoveItem: "moveItem",
onCreateItem: "createItem",
onHoldOver: "holdOver"
},
{kind: "Inspector", fit: true, classes: "deimos_panel",
onModify: "inspectorModify",
onRequestPositionValue: "inspectorRequestPositionValue",
onPositionDataUpdated: "inspectorPositionDataUpdated"
}
]}
]}
]}
],
/**
Select the first item in _this.$.picker_ to iniitalize (do this after
rendering to avoid error with setting values of width/height fields)
*/
events: {
onChildRequest: "",
onRegisterMe: "",
onError:""
},
handlers:{
onPaletteComponentAction: "runPaletteComponentAction",
onScaleChange: "displayZoomValue"
},
kinds: [],
index: null,
previousContent: "",
fileName: "",
selectFromComponentView: false,
zoomValues: [25, 50, 100, 125, 150, 200, 400],
initZoomIndex: 2,
create: function() {
ares.setupTraceLogger(this);
this.trace("Creating Deimos");
this.inherited(arguments);
this.addHandlers();
},
rendered: function() {
this.inherited(arguments);
this.initializeDesignerToolbar();
},
//* Initialize _devicePicker_ in the toolbar at render time
initializeDesignerToolbar: function() {
this.trace("called");
var initItem = this.$.devicePicker.getClientControls()[0];
this.$.devicePicker.setSelected(initItem);
this.deviceChosen(null, {selected: initItem});
var i, z;
for (i = 0; (z = this.zoomValues[i]); i++) {
this.$.zoomPicker.createComponent({content: z+"%", value: z, active: z === this.zoomValues[this.initZoomIndex]});
}
this.zoomDesigner(null, {selected: this.$.zoomPicker.getSelected()});
},
/**
* Loads the first kind passed thru the data parameter.
* This function acts on pallete, inspector, kindPicker and (may be) sends
* to designerFrame serialisation options extracted from .design
* files. No ack message is expected from designerFrame
*
* @param {Object} data: contains kinds declaration (enyo.kind format)
* and project information such as the analyzer output
* for all the .js files of the project and for enyo/onyx.
* @param {Function} next
*/
loadDesignerUI: function(data, next) {
this.trace("called with", data);
ares.assertCb(next);
this.enableDesignerActionButtons(false);
var what = data.kinds;
this.index = null;
this.kinds = what;
this.fileName = data.fileIndexer.name;
// Pass the project information (analyzer output, ...) to the inspector and palette
this.setProjectData(data.projectData);
this.doChildRequest({task: [ "initKindPicker", what ]}) ;
// selectKind is designed to be used in a waterfall, it calls
// next with `next(err,kindName)`. This extra parameter needs
// to be removed:
var bareNext = function(err) { next(err);} ;
// preselect the first kind. This will lead to an action in
// DesignerFrame.
this.selectKind(0, bareNext) ;
},
selectKind: function(index, next) {
ares.assertCb(next);
var kind = this.kinds[index];
var components = this.getSingleKind(index);
this.trace("selected kind ", kind);
// prepare next used by async.waterfall
var nextWithName = function(err) { next(err, kind.name); };
if (index !== this.index) {
this.addAresIds(components);
this.addAresKindOptions(components);
this.$.inspector.initUserDefinedAttributes(components);
this.previousContent = this.formatContent(enyo.json.codify.to(this.cleanUpComponents(components)));
this.$.inspector.inspect(null);
this.$.inspector.setCurrentKindName(kind.name);
this.$.designer.renderKind(this.fileName, components[0], null, nextWithName);
this.index = index;
} else {
setTimeout(nextWithName, 0);
}
},
/**
* Receive the project data reference which allows to access the analyzer
* output for the project's files, enyo/onyx and all the other project
* related information shared between phobos and deimos.
* @param oldProjectData
* @protected
*/
projectDataChanged: function(oldProjectData) {
// *ProjectData are backbone obbject mixed with events. This is defined in ProjectCrtl...
// *ProjectData are kept in ProjectCtrl
this.trace("called. New ", this.projectData, " old ", oldProjectData);
if (oldProjectData) {
// unbind former project data from Deimos
oldProjectData.off('change:project-indexer', this.projectIndexReady);
oldProjectData.off('update:project-indexer', this.projectIndexUpdated);
}
ProjectKindsModel.resetInformation();
if (this.projectData) {
this.trace("projectData", this.projectData);
// bind new project data in Deimos, any change in Project data triggers these callbacks
this.projectData.on('change:project-indexer', this.projectIndexReady, this);
this.projectData.on('update:project-indexer', this.projectIndexUpdated, this);
this.projectIndexer = this.projectData.getProjectIndexer();
this.projectIndexUpdated();
}
},
/**
* The project analyzer output has changed
* @param value the new analyzer output
* @protected
*/
projectIndexReady: function(model, indexer, options) {
this.trace("projectIndexReady: ", indexer);
this.projectIndexer = indexer;
this.projectIndexUpdated();
},
//* @protected
getSingleKind: function(inIndex) {
var kind = [],
len = this.kinds.length;
for (var i=0; i<len; i++) {
if (i === inIndex) {
kind[0] = this.kinds[inIndex];
break;
}
}
return kind;
},
projectIndexUpdated: function() {
var indexer = this.projectIndexer;
this.trace("projectIndexUpdated: for projectIndexer: ", indexer);
this.$.inspector.setProjectIndexer(indexer);
this.$.palette.setProjectIndexer(indexer);
ProjectKindsModel.buildInformation(indexer);
// no return message is expected
this.$.designer.sendSerializerOptions(ProjectKindsModel.serializerOptions);
},
//* Rerender current kind
rerenderKind: function(inSelectId) {
this.$.designer.renderKind(
this.fileName,
this.getSingleKind(this.index)[0],
inSelectId,
function () {}
);
},
refreshInspector: function() {
enyo.job("inspect", enyo.bind(this, function() {
this.$.inspector.inspect(this.$.designer.selection);
}), 200);
},
refreshComponentView: function(inComponents) {
this.$.componentView.visualize(inComponents);
},
// New selected item triggered in designerFrame. Synchronize component view and refresh inspector.
designerSelect: function(inSender, inEvent) {
var c = inSender.selection;
this.refreshInspector();
var haveToScroll = !this.selectFromComponentView;
this.$.componentView.setSelected(c, haveToScroll);
this.selectFromComponentView = false;
return true;
},
// Select event triggered by component view was completed. Refresh inspector.
designerSelected: function(inSender, inEvent) {
this.refreshInspector();
return true;
},
componentViewSelect: function(inSender, inEvent) {
this.$.designer.select(inEvent.component);
this.selectFromComponentView = true;
return true;
},
syncComponentViewDropTargetHighlighting: function(inSender, inEvent) {
this.$.componentView.syncDropTargetHighlighting(inEvent.component);
},
highlightDesignerDropTarget: function(inSender, inEvent) {
this.$.designer.highlightDropTarget(inEvent.component);
return true;
},
unhighlightDesignerDropTargets: function(inSender, inEvent) {
this.$.designer.unHighlightDropTargets();
return true;
},
inspectorModify: function(inSender, inEvent) {
if (inEvent.name === "layoutKind" && this.layoutKindUpdated(inEvent.value)) {
return true;
}
this.$.designer.modifyProperty(inEvent.name, inEvent.value);
return true;
},
inspectorRequestPositionValue: function(inSender, inEvent) {
this.$.designer.requestPositionValue(inEvent.prop);
},
inspectorPositionDataUpdated: function(inSender, inEvent) {
var item = this.getItemById(this.$.designer.selection.aresId, this.getSingleKind(this.index)),
prop,
val
;
for (prop in inEvent.props) {
val = inEvent.props[prop];
this.addReplaceStyleProp(item, prop, inEvent.props[prop]);
}
this.rerenderKind(item.aresId);
},
layoutKindUpdated: function(inLayoutKind) {
var kind = this.getSingleKind(this.index);
var item = this.getItemById(this.$.designer.selection.aresId, kind);
if (inLayoutKind !== "AbsolutePositioningLayout" && item.layoutKind !== "AbsolutePositioningLayout") {
return false;
}
if (inLayoutKind === "AbsolutePositioningLayout") {
item.layoutKind = inLayoutKind;
this.updateStyleForAbsolutePositioningLayoutKind(item);
} else {
if (item.layoutKind) {
delete item.layoutKind;
}
this.updateStyleForNonAbsolutePositioningLayoutKind(item);
}
this.addAresKindOptions(kind);
this.rerenderKind(item.aresId);
return true;
},
//* Add relative positioning to top item, and absolute positioning to it's children
updateStyleForAbsolutePositioningLayoutKind: function(inComponent) {
this.addReplaceStyleProp(inComponent, "position", "relative");
if (inComponent.components) {
for (var i = 0, comp; (comp = inComponent.components[i]); i++) {
this.addAbsolutePositioning(comp);
}
}
},
//* Set position: absolute; and layoutKind = "AbsolutePositioningLayout" on _inComponent_
addAbsolutePositioning: function(inComponent) {
this.addReplaceStyleProp(inComponent, "position", "absolute");
inComponent.layoutKind = "AbsolutePositioningLayout";
this.$.inspector.userDefinedAttributes[inComponent.aresId].layoutKind = inComponent.layoutKind;
},
//* Add static positioning to top item and to it's children
updateStyleForNonAbsolutePositioningLayoutKind: function(inComponent) {
this.addReplaceStyleProp(inComponent, "position", "");
if (inComponent.components) {
for (var i = 0, comp; (comp = inComponent.components[i]); i++) {
this.removeAbsolutePositioning(comp);
}
}
},
//* Set position: ""; and layoutKind = "" on _inComponent_
removeAbsolutePositioning: function(inComponent) {
this.addReplaceStyleProp(inComponent, "position", "");
this.addReplaceStyleProp(inComponent, "top", "");
this.addReplaceStyleProp(inComponent, "right", "");
this.addReplaceStyleProp(inComponent, "bottom", "");
this.addReplaceStyleProp(inComponent, "left", "");
if (inComponent.layoutKind) {
delete inComponent.layoutKind;
}
if (this.$.inspector.userDefinedAttributes[inComponent.aresId].layoutKind) {
delete this.$.inspector.userDefinedAttributes[inComponent.aresId].layoutKind;
}
},
//* Update _inComponent.style.inProp_ to be _inValue_
addReplaceStyleProp: function(inComponent, inProp, inValue) {
var currentStyle = inComponent.style || "",
styleProps = {}
;
// Convert css string to hash
enyo.Control.cssTextToDomStyles(this.trimWhitespace(currentStyle), styleProps);
// Add/replace inProp with inValue
styleProps[inProp] = inValue;
// Convert back to a string
inComponent.style = enyo.Control.domStylesToCssText(styleProps);
this.$.inspector.userDefinedAttributes[inComponent.aresId].style = inComponent.style;
},
prepareUpdatedKindList: function() {
if (this.index !== null) {
// create a list containing code for each kind managed by
// the designer, I.e. for each kind contained in the
// edited file. this list will be used by the code editor
var kindsAsCode = [];
for(var i = 0 ; i < this.kinds.length ; i++) {
kindsAsCode[i] = (i === this.index) ? this.formatContent(enyo.json.codify.to(this.cleanUpComponents([this.kinds[i]]))) : null;
}
// the length of the returned event array is significant for the undo/redo operation.
// event.contents.length must match this.kinds.length even if it contains only null values
// so the returned structure return may be [null] or [null, content, null] or [ null, null, null]...
if (kindsAsCode[this.index] === this.previousContent) {
// except when undo/redo would not bring any change...
kindsAsCode = [];
}
return kindsAsCode;
}
},
formatContent: function(inContent) {
// Strip opening [ bracket
inContent = inContent.replace(/\[\n\t\t\{/, "{\n\t");
// Strip closing }] brackets
inContent = inContent.replace(/\}\s([^}]+)$/, "");
return inContent;
},
closeDesigner: function() {
this.$.designer.cleanUp();
this.updateCodeInEditor(this.fileName);
this.setProjectData(null);
this.doChildRequest({task: "switchToCodeMode" });
return true;
},
// When the designer finishes rendering, re-build the components view
buildComponentView: function(msg) {
var components = enyo.json.codify.from(msg.val);
this.refreshComponentView(components);
// Recreate this kind's components block based on components in Designer and user-defined properties in Inspector.
this.kinds[this.index] = this.cleanUpComponents(components, true)[0];
this.enableDesignerActionButtons(true);
},
//* Send dragData type to Designer and Component View during ondragstart within Palette
paletteDragStart: function(inSender, inEvent) {
var dragType = enyo.json.codify.from(inEvent.dataTransfer.getData("text")).type;
this.$.designer.sendDragType(dragType);
this.$.componentView.setDragType(dragType);
},
//* Create item from palette (via drag-and-drop from Palette into Designer or Component View)
createItem: function(inSender, inEvent) {
var config = inEvent.config,
options = inEvent.options,
targetId = inEvent.targetId,
beforeId = inEvent.beforeId,
target = (targetId)
? this.getItemById(targetId, this.getSingleKind(this.index))
: this.kinds[this.index];
if (!config) {
enyo.warn("Could not create new item - bad data: ", inEvent);
return true;
}
// Give the new component (and any children) a fresh _aresId_
config.aresId = this.generateNewAresId();
if (config.components) {
this.addAresIds(config.components);
}
//if component has a "isViewTemplate" option, Designer show action popup
if(options && options.isViewTemplate){
this.showActionPopup(options, config, target);
} else {
this.performCreateItem(config, target, beforeId);
}
return true;
},
//* Move item with _inEvent.itemId_ into item with _inEvent.targetId_
moveItem: function (inSender, inEvent) {
var kind = this.getSingleKind(this.index),
movedItem = this.getItemById(inEvent.itemId, kind),
clone = enyo.clone(movedItem),
beforeId = inEvent.beforeId || null,
target = (inEvent.targetId)
? this.getItemById(inEvent.targetId, kind)
: this.kinds[this.index]
;
// If moving item onto itself or before itself, do nothing
if ((target === movedItem) || (beforeId !== null && beforeId === inEvent.itemId)) {
return true;
}
// Remove existing item
this.removeItemById(inEvent.itemId, this.kinds[this.index].components);
// Apply any special layout rules
clone = this.applyLayoutKindRules(inEvent.layoutData, clone);
// Copy clone style props to inspector
if(clone.style !== undefined){
this.$.inspector.userDefinedAttributes[clone.aresId].style = clone.style;
}
this.addAresKindOptions(kind);
if (beforeId && (beforeId !== target.aresId)) {
if (!this.insertItemBefore(clone, target, beforeId)) {
return true;
}
} else {
this.insertItem(clone, target);
}
this.rerenderKind(inEvent.itemId);
return true;
},
resizeItem: function(inSender, inEvent) {
var item = this.getItemById(this.$.designer.selection.aresId, this.getSingleKind(this.index));
for (var prop in inEvent.sizeData) {
this.addReplaceStyleProp(item, prop, inEvent.sizeData[prop]);
}
this.rerenderKind(item.aresId);
return true;
},
//* Called when the designerFrame has retrieved a requested absolute position value
designerReturnPositionValue: function(inSender, inEvent) {
this.$.inspector.setRequestedPositionValue(inEvent.prop, inEvent.value);
return true; //TODO See if the code behind the return is useful
/*
var item = this.getItemById(this.$.designer.selection.aresId, this.kinds[this.index].components);
this.addReplaceStyleProp(item, inEvent.prop, inEvent.value + "px");
this.rerenderKind(item.aresId);
*/
},
applyLayoutKindRules: function(inLayoutData, inControl) {
var layoutKind = inLayoutData && inLayoutData.layoutKind;
switch (layoutKind) {
case "AbsolutePositioningLayout":
inControl.style = this.addAbsolutePositioningStyle(inLayoutData, inControl);
break;
default:
if(inControl.style !== undefined){
inControl.style = this.removeAbsolutePositioningStyle(inControl);
}
break;
}
return inControl;
},
//* Add absolute positioning styling
addAbsolutePositioningStyle: function(inLayoutData, inControl) {
var currentStyle = inControl.style || "",
styleProps = {}
;
// Convert css string to hash
enyo.Control.cssTextToDomStyles(this.trimWhitespace(currentStyle), styleProps);
// Add absolute positioning styles (default to top and left)
styleProps.position = "absolute";
// If only top property, or no top or bottom property, add top
if (styleProps.top || (!styleProps.top && !styleProps.bottom)) {
styleProps.top = inLayoutData.bounds.top + "px";
}
// If bottom add bottom
if (styleProps.bottom) {
styleProps.bottom = inLayoutData.bounds.bottom + "px";
}
// If only left property, or no left or right property, add left
if (styleProps.left || (!styleProps.left && !styleProps.right)) {
styleProps.left = inLayoutData.bounds.left + "px";
}
// If right add right
if (styleProps.right) {
styleProps.right = inLayoutData.bounds.right + "px";
}
// Convert back to a string and return
return enyo.Control.domStylesToCssText(styleProps);
},
removeAbsolutePositioningStyle: function(inControl) {
var currentStyle = inControl.style || "",
styleProps = {};
// Convert css string to hash
enyo.Control.cssTextToDomStyles(this.trimWhitespace(currentStyle), styleProps);
// Remove absolute positioning styles
styleProps.position = styleProps.top = styleProps.right = styleProps.bottom = styleProps.left = "";
// Convert back to a string and return
return enyo.Control.domStylesToCssText(styleProps);
},
trimWhitespace: function(inStr) {
inStr = inStr || "";
return inStr.replace(/\s/g, "");
},
//* Holdover event from ComponentView - simulate drop in designer
holdOver: function(inSender, inEvent) {
this.$.designer.prerenderDrop(inEvent.targetId, inEvent.beforeId);
},
insertItem: function(inItem, inTarget) {
inTarget.components = inTarget.components || [];
inTarget.components.push(inItem);
},
insertItemBefore: function(inItem, inTarget, inBeforeId) {
var beforeIndex = -1,
component,
i;
inTarget.components = inTarget.components || [];
for (i = 0; (component = inTarget.components[i]); i++) {
if(component.aresId === inBeforeId) {
beforeIndex = i;
break;
}
}
if (beforeIndex === -1) {
enyo.warn("Couldn't find id: "+inBeforeId+" in ", inTarget);
return false;
}
inTarget.components.splice(beforeIndex, 0, inItem);
return true;
},
getItemById: function(inId, inComponents) {
if (inComponents.length === 0) {
return;
}
for (var i = 0, component, item; (component = inComponents[i]); i++) {
if (component.aresId === inId) {
item = inComponents[i];
} else if (component.components) {
item = this.getItemById(inId, component.components);
}
if(item) {
return item;
}
}
},
getParentOfId: function(inChildId, inParent) {
for (var i = 0, component, item; (component = inParent.components[i]); i++) {
if (component.aresId === inChildId) {
item = inParent;
} else if (component.components) {
item = this.getParentOfId(inChildId, component);
}
if(item) {
return item;
}
}
},
//* Look through _inComponents_ recursively and splice out the item with an _aresId_ matching _inId_
removeItemById: function(inId, inComponents) {
for (var i = 0, component; (component = inComponents[i]); i++) {
if (component.aresId === inId) {
inComponents.splice(i, 1);
return;
}
if (component.components) {
this.removeItemById(inId, component.components);
}
}
},
cleanUpComponents: function(inComponents, inKeepAresIds) {
var component,
ret = [],
i;
for (i=0; (component = inComponents[i]); i++) {
ret.push(this.cleanUpComponent(component, inKeepAresIds));
}
return ret;
},
cleanUpComponent: function(inComponent, inKeepAresIds) {
var aresId = inComponent.aresId,
childComponents = [],
cleanComponent = {},
atts,
att,
i;
if (!aresId) {
return cleanComponent;
}
atts = this.$.inspector.userDefinedAttributes[aresId];
if (!atts) {
return cleanComponent;
}
// Copy each user-defined property from _atts_ to the cleaned component
for (att in atts) {
if ((inKeepAresIds && att === "aresId") || (att !== "aresId" && att !== "components" && att !== "__aresOptions")) {
cleanComponent[att] = atts[att];
}
}
// If this component has any child components, add them to components[] block
if (inComponent.components) {
// Recurse through child components
for (i=0; i<inComponent.components.length; i++) {
childComponents.push(this.cleanUpComponent(inComponent.components[i], inKeepAresIds));
}
if (childComponents.length > 0) {
cleanComponent.components = childComponents;
}
}
return cleanComponent;
},
cleanUpViewComponent: function(inComponent, inKeepAresIds) {
var aresId = inComponent.aresId,
childComponents = [],
cleanComponent = {},
att,
i;
if (!aresId) {
return cleanComponent;
}
for(att in inComponent){
if ((inKeepAresIds && att === "aresId") || (att !== "aresId" && att !== "components" && att !== "__aresOptions")) {
cleanComponent[att] = inComponent[att];
}
}
if (inComponent.components) {
for (i=0; i<inComponent.components.length; i++) {
childComponents.push(this.cleanUpViewComponent(inComponent.components[i], inKeepAresIds));
}
if (childComponents.length > 0) {
cleanComponent.components = childComponents;
}
}
return cleanComponent;
},
undoAction: function(inSender, inEvent) {
this.enableDesignerActionButtons(false);
this.doChildRequest({task: [ "undo", ares.noNext ] });
return true;
},
redoAction: function(inSender, inEvent) {
this.enableDesignerActionButtons(false);
this.doChildRequest({task: [ "redo", ares.noNext ] });
return true;
},
deleteAction: function(inSender, inEvent) {
if(!this.$.designer.selection) {
return true;
}
this.$.inspector.inspect(null);
this.enableDesignerActionButtons(false);
var kind = this.getSingleKind(this.index);
this.deleteComponentByAresId(this.$.designer.selection.aresId, kind);
this.addAresKindOptions(kind);
this.rerenderKind();
return true;
},
deleteComponentByAresId: function(inAresId, inComponents) {
for (var i = 0; i < inComponents.length; i++) {
if (inComponents[i].aresId === inAresId) {
inComponents.splice(i, 1);
return;
}
if (inComponents[i].components) {
this.deleteComponentByAresId(inAresId, inComponents[i].components);
}
}
},
updateCodeInEditor: function(inFilename) {
var kindList = this.prepareUpdatedKindList();
if (inFilename === this.fileName) {
var kind = this.getSingleKind(this.index);
this.previousContent = this.formatContent(enyo.json.codify.to(this.cleanUpComponents(kind)));
this.doChildRequest({ task: [ "updateComponentsCode", kindList ] });
} else {
this.log("skipped code update of stale file ", inFilename);
}
},
/**
* Called when ProjectView has new project selected
* @param {Object} inProject
* @param {Function} next
*/
projectSelected: function(inProject, next) {
this.trace("called with ", inProject);
this.$.designer.updateSource(inProject, next);
},
/**
* triggered by 'Reload' button
*/
reloadDesigner: function() {
this.enableDesignerActionButtons(false);
this.$.designer.reload();
this.$.inspector.inspect(null);
},
reloadComplete: function() {
this.rerenderKind();
},
syncFile: function(project, filename, code) {
this.$.designer.syncFile(project, filename, code);
},
addAresIds: function(inComponents) {
for(var i = 0; i < inComponents.length; i++) {
if (!inComponents[i].aresId) {
inComponents[i].aresId = this.generateNewAresId();
}
if (inComponents[i].components) {
this.addAresIds(inComponents[i].components);
}
}
},
addAresKindOptions: function(inComponents) {
for(var i = 0; i < inComponents.length; i++) {
this.addOptionsToComponent(inComponents[i]);
if (inComponents[i].components) {
this.addAresKindOptions(inComponents[i].components);
}
}
},
addOptionsToComponent: function(inComponent) {
var options = ProjectKindsModel.getKindOptions(inComponent.kind);
if (options) {
inComponent.__aresOptions = options;
}
},
//* Generate new ares id using timestamp
generateNewAresId: function() {
return "ares_"+Math.floor((Math.random()*new Date().getTime())+1);
},
//* When a device is chosen in the designer toolbar, set the appropriate heights/widths
deviceChosen: function() {
var selected = this.$.devicePicker.getSelected();
if(!selected.value) {
return;
}
// Update fields with predefined values
this.$.designerWidthInput.setValue(selected.value.width);
this.$.designerHeightInput.setValue(selected.value.height);
// Force height/width value updates (change event isn't triggered)
this.$.designer.setWidth(selected.value.width);
this.$.designer.setHeight(selected.value.height);
return true;
},
updateHeight: function(inSender, inEvent) {
this.$.designer.setHeight(inSender.getValue());
this.findDeviceDimensionMatch();
},
updateWidth: function(inSender, inEvent) {
this.$.designer.setWidth(inSender.getValue());
this.findDeviceDimensionMatch();
},
findDeviceDimensionMatch: function() {
var items = this.$.devicePicker.getClientControls(),
item,
h = this.$.designerHeightInput.getValue(),
w = this.$.designerWidthInput.getValue(),
i;
for(i=0, item; (item = items[i]); i++) {
if(item.value && ((h == item.value.height && w == item.value.width) || (h == item.value.width && w == item.value.height))) {
this.$.devicePicker.setSelected(item);
return;
}
}
// If no match, set selected item to custom
for(i=0, item; (item = items[i]); i++) {
if(item.value) {
continue;
}
this.$.devicePicker.setSelected(item);
}
},
// Swap width and height values
swapDesignerDimensions: function(inSender, inEvent) {
var h = this.$.designerHeightInput.getValue(),
w = this.$.designerWidthInput.getValue();
this.$.designerHeightInput.setValue(w);
this.$.designerWidthInput.setValue(h);
// Force height/width value updates (change event isn't triggered)
this.$.designer.setWidth(this.$.designerWidthInput.getValue());
this.$.designer.setHeight(this.$.designerHeightInput.getValue());
},
zoomDesigner: function(inSender, inEvent) {
this.$.designer.zoom(inEvent.selected.value);
},
autoZoomDesigner: function(inSender, inEvent) {
var active = inSender.getValue();
this.$.zoomPickerButton.setDisabled(active);
this.$.designer.setAutoZoom(active);
if(active){
var scale = this.$.designer.zoomFromWidth();
this.$.zoomPickerButton.setContent(scale+"%");
} else{
this.$.zoomPickerButton.setContent(this.zoomValues[this.initZoomIndex]+"%");
this.$.zoomPicker.setSelected(this.$.zoomPicker.getClientControls()[this.initZoomIndex]);
this.zoomDesigner(null, {selected: this.$.zoomPicker.getSelected()});
}
},
displayZoomValue: function(inSender, inEvent){
this.$.zoomPickerButton.setContent(inEvent.scale+"%");
},
//* Add dispatch for native drag events
addHandlers: function(inSender, inEvent) {
document.ondragstart = enyo.dispatch;
document.ondrag = enyo.dispatch;
document.ondragenter = enyo.dispatch;
document.ondragleave = enyo.dispatch;
document.ondragover = enyo.dispatch;
document.ondrop = enyo.dispatch;
document.ondragend = enyo.dispatch;
},
showActionPopup: function(options, config, target){
if(options.isViewTemplate){
this.$.actionPopup.setActionShowing("vtAction");
} else {
//FIXME: for other palette component actions
this.$.actionPopup.setActionShowing(null);
}
this.$.actionPopup.setConfigComponent(config);
this.$.actionPopup.setTargetComponent(target);
this.$.actionPopup.show();
},
// @protected
runPaletteComponentAction: function(inSender,inEvent){
var config = this.$.actionPopup.getConfigComponent(config);
var configData = this.formatContent(enyo.json.codify.to(this.cleanUpViewComponent(config)));
if(inEvent.getName() === "addtoKind"){
var target = this.$.actionPopup.getTargetComponent(target);
var beforeId = inEvent.beforeId;
this.performCreateItem(config, target, beforeId);
} else if (inEvent.getName() === "replaceKind"){
this.doChildRequest({task: [ "replaceKind", this.index, configData ]});
} else if (inEvent.getName() === "addNewKind"){
this.doChildRequest({ task: [ "addNewKind", configData ] });
}
else {
this.log("unexpected event: " + inEvent.getName() );
}
this.$.actionPopup.hide();
return true;
},
// @protected
performCreateItem: function(config, target, beforeId){
var kind = this.getSingleKind(this.index);
if (beforeId && (beforeId !== target.aresId)) {
this.insertItemBefore(config, target, beforeId);
} else {
this.insertItem(config, target);
}
this.$.inspector.initUserDefinedAttributes(kind);
this.addAresKindOptions(kind);
this.rerenderKind(config.aresId);
},
enableDesignerActionButtons: function(condition) {
this.$.deleteButton.setAttribute("disabled", !condition);
this.$.undoButton.setAttribute("disabled", !condition);
this.$.redoButton.setAttribute("disabled", !condition);
this.$.reloadDesignerButton.setAttribute("disabled", !condition);
}
});