UNPKG

ares-ide

Version:

A browser-based code editor and UI designer for Enyo 2 projects

984 lines (884 loc) 29.8 kB
/*global ComponentsRegistry, ares, Ares, async, enyo, $L, setTimeout */ enyo.kind({ name:"Ares.EnyoEditor", kind:"FittableRows", components:[ {kind: "onyx.MoreToolbar", name:"toolbar", classes: "ares-top-toolbar ares-designer-panels", layoutKind: "FittableColumnsLayout", noStretch: true, components: [ {kind: "onyx.Grabber", classes: "ares-grabber ares-icon", ontap: "activePanel", components:[ {kind: "aresGrabber", name: "aresGrabberDirection", classes:"lleftArrow"} ]}, {name:"editorControls", kind: "FittableColumns", fit:true, classes: "onyx-toolbar-inline", components:[ {name: "editorFileMenu", kind: "Ares.FileMenu", onSelect: "fileMenuItemSelected"}, {name: "newKindDecorator", kind: "onyx.TooltipDecorator", components: [ {name: "newKindButton", kind: "onyx.IconButton", src: "assets/images/new_kind.png", ontap: "newKindAction"}, {kind: "onyx.Tooltip", content: $L("New Kind")} ]}, {fit: true}, {name: "editorSettingDecorator", kind: "onyx.TooltipDecorator", components: [ {name: "editorButton", kind: "onyx.IconButton", src: "assets/images/editor_settings.png", ontap: "editorSettings"}, {kind: "onyx.Tooltip", content: "Editor Settings"} ]}, {name: "designerDecorator", kind: "onyx.TooltipDecorator", components: [ {name: "designerButton", kind: "onyx.IconButton", src: "assets/images/designer.png", ontap: "designerAction"}, {name: "designerButtonBroken", kind: "onyx.IconButton", src: "assets/images/designer_broken.png", ontap: "doDesignerBroken"}, {name: "designerTooltipBroken", kind: "Ares.ErrorTooltip", content: $L("Designer")} ]} ]}, {name:"deimosControls", kind: "FittableColumns", fit:true, classes: "onyx-toolbar-inline", components:[ {name: "designerFileMenu", kind: "Ares.FileMenu", onSelect: "fileMenuItemSelected"}, {name: "docLabel", content: "Deimos", classes: "ares-left-margin"}, {kind: "onyx.PickerDecorator", classes: "ares-right-margin", components: [ {name: "kindButton", classes:"ares-toolbar-picker", kind: "onyx.PickerButton"}, {name: "kindPicker", kind: "onyx.Picker", onChange: "kindSelected", components: [ ]} ]}, {fit: true}, {name: "codeEditorDecorator", kind: "onyx.TooltipDecorator", classes: "ares-icon", components: [ {kind: "onyx.IconButton", src: "assets/images/code_editor.png", ontap: "closeDesigner"}, {kind: "onyx.Tooltip", content: "Code editor"} ]} ]}, {name: "codePreviewDecorator", kind: "onyx.TooltipDecorator", classes: "ares-icon", components: [ {kind: "onyx.IconButton", src: "../project-view/assets/images/project_view_preview.png", ontap: "requestPreview"}, {kind: "onyx.Tooltip", name:"previewTooltip", content: "Preview"} ]}, {classes:"ares-logo-container", components:[ {name:"logo", kind:"Ares.Logo"} ]} ]}, { name: "savePopup", kind: "Ares.ActionPopup", allowHtmlMsg: true}, { name: "saveAsPopup", kind: "Ares.FileChooser", classes:"ares-masked-content-popup", showing: false, headerText: $L("Save as..."), folderChooser: false, onFileChosen: "saveAsFileChosen", allowCreateFolder: true, allowNewFile: true, allowToolbar: true }, { name: "overwritePopup", kind: "Ares.ActionPopup", title: $L("Overwrite"), message: $L("Overwrite existing file?"), actionButton: $L("Overwrite") }, { name: "docToolBar", kind: "DocumentToolbar", onTabRemoveRequested: "handleCloseDocument", onTabChangeRequested: 'handleSwitchDoc', classes: "ares-bottom-bar" }, { kind: "Panels", arrangerKind: "CarouselArranger", draggable: false, classes:"enyo-fit ares-panels", onTransitionStart : "stopPanelEvent", onTransitionFinish: "stopPanelEvent", ondragstart : "stopPanelEvent", ondrag : "stopPanelEvent", ondragfinish : "stopPanelEvent", components: [ {components: [ {kind: "Phobos"} ]}, {components: [ {kind: "Deimos"} ]} ] } ], events: { onShowWaitPopup: "", onHideWaitPopup: "", onAllDocumentsAreClosed: "", onRegisterMe: "", onMovePanel:"", onDesignerBroken: "", onFileEdited:"_fileEdited", onError: "" }, published: { // harcoded until ENYO-2755 is fixed panelIndex: 2, aceActive: true }, handlers: { onErrorTooltip: "showErrorTooltip", onErrorTooltipReset: "resetErrorTooltip", onAceGotFocus: "switchProjectToCurrentDoc", onChildRequest: "handleCall", onAceFocus: "aceFocus" }, debug: false, create: function() { this.inherited(arguments); // Setup this.trace() function according to this.debug value ares.setupTraceLogger(this); this.doRegisterMe({name:"enyoEditor", reference:this}); }, /** * handle a Call from children. * @param {Object} inSender * @param {Object} inEvent: inEvent.task: array [ method_name_to_call, arg1, arg2 , ...] * @returns {true} */ handleCall: function(inSender, inEvent){ var data = inEvent.task; var task = typeof data === 'object' ? data : [ data ]; var method = task.shift(); this[method].apply(this, task); return true; }, activePanel : function(){ // my index within the main Ares panels, not index phobos and deimos panels this.doMovePanel({panelIndex:this.panelIndex}); }, showDeimosPanel: function () { this.$.panels.setIndex(1) ; }, showPhobosPanel: function () { this.$.panels.setIndex(0) ; }, stopPanelEvent: function(){ return true; }, fileMenuItemSelected: function(inSender, inEvent) { var method = inEvent.selected.value; this[method](); }, editorSettings: function(){ this.$.phobos.editorSettings(); }, applySettings: function(settings){ this.$.phobos.applySettings(settings); }, changeRightPane: function(editorSettings){ this.$.phobos.changeRightPane(editorSettings); }, newKindAction: function() { this.$.phobos.newKindAction(); }, requestSelectedText: function() { return this.$.phobos.requestSelectedText(); }, updateComponentsCode: function(kindList) { return this.$.phobos.updateComponentsCode(kindList); }, replaceKind: function(kindIndex, config) { return this.$.phobos.replaceKind(kindIndex, config); }, addNewKind: function(config) { return this.$.phobos.addNewKind(config); }, /** * event handler for kind Picker * @param {Object} inSender * @param {Object} inEvent * @returns {true} */ kindSelected: function(inSender, inEvent) { var index = inSender.getSelected().index; var deimos = this.$.deimos; async.waterfall([ deimos.selectKind.bind(deimos, index), (function(name,next) { this.$.kindButton.setContent(name); this.$.toolbar.reflow(); next(); }).bind(this) ]); return true; }, /** * kindPicker let user select the top kind to be designed. By * default, the first one is selected * @param {array} kinds */ initKindPicker: function(kinds) { var maxLen ; this.$.kindPicker.destroyClientControls(); for (var i = 0; i < kinds.length; i++) { var k = kinds[i]; this.$.kindPicker.createComponent({ content: k.name, index: i }); maxLen = Math.max(k.name.length, maxLen); } this.$.kindButton.setContent(kinds[0].name); this.$.kindButton.applyStyle("width", (maxLen+2) + "em"); this.$.kindPicker.render(); this.resized(); }, designerAction: function() { if(this.$.phobos.editorUserSyntaxError() !== 0) { this.userSyntaxErrorPop(); } else { this.$.phobos.designerAction(); this.manageControls(true); } }, enableDesignerButton: function(enable) { this.$.designerButton.setShowing(enable); this.$.designerButtonBroken.setShowing(! enable); }, userSyntaxErrorPop: function(){ this.doError({msg: $L("Designer cannot work on a file with a syntax error. Please fix the error highlighted in code editor before launching the designer."), title: $L("Syntax Error")}); }, closeDesigner: function(inSender, inEvent){ this.$.deimos.closeDesigner(); return true; }, switchToCodeMode: function() { this.trace(); this.$.panels.setIndex(this.phobosViewIndex); this.activeDocument.setCurrentIF('code'); this.manageControls(false); this.aceFocus(); }, /** * Change controls on the panel top toolbar * * @private * @param {boolean} designer, designer = true if designer's controls should be available */ manageControls: function(designer){ this.setAceActive(!designer); this.$.editorControls.setShowing(!designer); this.$.deimosControls.setShowing(designer); this.$.toolbar.resized(); }, switchGrabberDirection: function(active){ this.$.aresGrabberDirection.switchGrabberDirection(active); }, addPreviewTooltip: function(message){ this.$.previewTooltip.setContent(message); }, updateDeimosLabel: function(edited) { if (edited) { this.$.docLabel.setContent("Deimos *"); } else { this.$.docLabel.setContent("Deimos"); } this.$.toolbar.resized(); }, aceFocus: function(){ if(this.getAceActive()){ this.$.phobos.focusEditor(); } }, showErrorTooltip: function(inSender, inEvent){ this.$.designerTooltipBroken.reset("Designer"); this.$.designerButtonBroken.setDisabled(false); this.$.designerTooltipBroken.raise(inEvent); }, resetErrorTooltip: function(){ this.$.designerTooltipBroken.reset("Designer"); this.$.designerButtonBroken.setDisabled(true); }, getErrorFromDesignerBroken: function(){ return this.$.designerTooltipBroken.error; }, _fileEdited: function() { this.updateDeimosLabel(this.activeDocument.getEdited()); }, // document currently shown by Ace activeDocument: null, showWaitPopup: function(inMessage) { this.doShowWaitPopup({msg: inMessage}); }, hideWaitPopup: function() { this.doHideWaitPopup(); this.aceFocus(); }, /** * apply action on each doc of passed project (or project's doc) * or current project (if param is falsy) * @param {Function} action: function called on each project doc * @param {Object} param: project (defaults to activeProject) */ foreachProjectDocs: function(action, param) { var project = param instanceof Ares.Model.Project ? param : param instanceof Ares.Model.File ? param.getProjectData() : Ares.Workspace.projects.getActiveProject(); var projectName = project.getName(); function isProjectDoc(model) { return model.getProjectData().getName() === projectName ; } // backbone collection Ares.Workspace.files.filter(isProjectDoc).forEach(action, this); }, /** * request to save project files one by one and then launch * preview */ requestPreview: function() { var previewer = ComponentsRegistry.getComponent("projectView"); var project = Ares.Workspace.projects.getActiveProject(); var serialSaver = [] ; this.trace("preview requested on project " + project.getName()); // build the serie of functions to be fed to async.series this.foreachProjectDocs( function(doc) { serialSaver.push( this.requestSave.bind(this, doc) ); } ); // the real work is done below async.series( serialSaver, function(err) { if (! err) { previewer.launchPreview(project); } } ); }, // Save actions saveProjectDocs: function() { this.foreachProjectDocs(this.saveDoc.bind(this)); }, saveCurrentDoc: function() { this.saveDoc(this.activeDocument); }, saveDoc: function(doc) { var content; if (doc === this.activeDocument) { content = this.$.phobos.getEditorContent(); } else { content = doc.getEditedData(); } var where = { service: doc.getProjectData().getService(), fileId: doc.getFileId() }; this.saveFile(doc.getName(), content, where, ares.noNext); }, saveFile: function(name, content, where, next){ ares.assertCb(next); var req; if (where.fileId) { // plain save this.trace("Saving doc: " + name + " id " + where.fileId); req = where.service.putFile(where.fileId, content); } else { // used with saveAs this.trace("Saving doc: " + name + " in dir id " + where.folderId); req = where.service.createFile(where.folderId, where.name, content); } // hide is done in the callbacks below this.showWaitPopup($L("Saving ") + name + " ..."); req.response(this, function(inSender, inData) { this.trace('saveFile response ', inData); var savedFile = inData[0]; // only one file was saved savedFile.service = where.service; var docDataId = Ares.Workspace.files.computeId(savedFile); if (! docDataId) { this.error("cannot find docDataId from ", savedFile, ' where ', where); } var docData = Ares.Workspace.files.get(docDataId); this.trace('saveFile response ok for ', name, savedFile, docDataId, " => ", docData); if(docData){ docData.setData(content); docData.setEditedData(content); // update deimos label with edited status which is // actually "not-edited" ... docData.setEdited(false); this._fileEdited(); } this.hideWaitPopup(); this.analyseData(docData); next(null, savedFile); }).error(this, function(inSender, inErr) { this.trace('saveFile response failed with ' , inErr , ' for ', name, where); this.hideWaitPopup(); this.doError({msg: "Unable to save the file: " + inErr }); next(inErr); }); }, analyseData: function(inDocData) { var codeLooksGood = false ; var phobos = this.$.phobos; if (this.activeDocument === inDocData) { // current file was just saved codeLooksGood = phobos.reparseUsersCode(); } else { this.trace("skipping reparse user code"); } // successful analysis will enable designer button this.enableDesignerButton(false); // Global analysis is always triggered even if local analysis // reports an error. This way, errors are reported from a // single place wherever the error is. The alternative is to // report error during local analysis, which often lead to // error reported twice (i.e on first file edit after a // project load) this.trace("triggering full analysis after file save"); phobos.projectCtrl.forceFullAnalysis(); this.trace("done. codeLooksGood: ", codeLooksGood); }, requestSaveDocAs: function() { var file = this.activeDocument.getFile(); var projectData = Ares.Workspace.projects.getActiveProject(); var buildPopup = function() { var path = file.path; var relativePath = path.substring( path.indexOf(projectData.id) + projectData.id.length, path.length ); this.$.saveAsPopup.pointSelectedName(relativePath, true); // saveAsPopup may invoke saveAsFileChosen this.$.saveAsPopup.show(); }; this.$.saveAsPopup.connectProject(projectData, buildPopup.bind(this)); }, saveAsFileChosen: function(inSender, inEvent) { this.trace(inEvent); if (inEvent.file) { this.$.saveAsPopup.$.hermesFileTree .checkNodeName(inEvent.name, this.requestOverwrite.bind(this, inEvent)); } else { // no file or folder chosen this.aceFocus(); } }, requestOverwrite: function(param,willClobber) { var owp = this.$.overwritePopup; if (willClobber) { owp.setActionCallback(this.saveAs.bind(this, param)); owp.setCancelCallback(this.aceFocus.bind(this)); owp.show(); } else { this.saveAs(param); } }, saveAs: function(param){ this.trace( param); var relativePath = param.name.split("/"); var name = relativePath[relativePath.length-1]; var doc = this.activeDocument; var projectData = Ares.Workspace.projects.getActiveProject(); var file= param.file; var content= this.$.phobos.getEditorContent(); var myNext = (function(err,result) { this.trace("err:", err); this.hideWaitPopup(); }).bind(this); if (!file) { myNext(new Error("Internal error: missing file/folder description")); return; } var aresInstance = ComponentsRegistry.getComponent('ares'); async.waterfall([ this.closeDoc.bind(this, doc), _prepareNewLocation.bind(this), this.saveFile.bind(this, name, content), _refreshFileTree.bind(this), aresInstance._openDocument.bind(aresInstance, projectData) ], myNext.bind(this) ); function _prepareNewLocation(next) { var where, err; if (file.isDir && name) { // create given file in given dir where = { service: file.service, folderId: file.id, name: name }; } else if (!file.isDir && !name) { // overwrite the given file where = { service: file.service, fileId: file.id }; } else if (!file.isDir && name) { // create a new file in the same folder as the // given file where = { service: file.service, folderId: file.parent.id, name: name }; } else { err = new Error("Internal error: wrong file/folder description"); } next(err, where); } function _refreshFileTree(file, next) { this.trace(file); // refreshFileTree is async, there's no need to wait before opening // the document ComponentsRegistry.getComponent("harmonia").refreshFileTree(file.id); next(null, file); } }, // close actions closeActiveDoc: function() { var doc = this.activeDocument; this.trace("close document:", doc.getName()); this.$.phobos.closeSession(); this.activeDocument = null; this.forgetDoc(doc) ; }, forgetDoc: function(doc) { // remove Doc from cache var docId = doc.getId(); Ares.Workspace.files.removeEntry(docId); this.$.docToolBar.removeTab(docId); if (! Ares.Workspace.files.length ) { this.doAllDocumentsAreClosed(); } }, /** * close a document * @param {(String|Object)} param: docId or doc * @param {[Function]} next */ closeDoc: function(param, next) { ares.assertCb(next); var doc = typeof param === 'object' ? param : Ares.Workspace.files.get(param) ; var docId = doc ? doc.getId() : undefined; if (docId && this.activeDocument && this.activeDocument.getId() === docId) { this.closeActiveDoc(); } else if (docId) { this.trace("closing a doc different from current one: ", doc.getName()); this.forgetDoc(doc); } else { this.trace("called without doc to close"); } next(); }, switchToNewTabAndDoc: function(projectData, file, inContent,next) { ares.assertCb(next); this.trace("projectData:", projectData.getName(), ", file:", file.name); var fileData = Ares.Workspace.files.newEntry(file, inContent, projectData); ComponentsRegistry.getComponent("documentToolbar") .createDocTab(file.name, fileData.getId(), file.path); this.switchToDocument(fileData, $L("Opening..."), next); }, switchProjectToCurrentDoc: function(inSender, inEvent) { var pl = ComponentsRegistry.getComponent("projectList") ; if (! this.switching && this.activeDocument) { pl.selectProject( this.activeDocument.getProjectData(), ares.noNext ); } return true; }, /** * Handle switch doc event * @param {Object} inSender * @param {Object} inEvent * @returns {true} */ handleSwitchDoc: function(inSender, inEvent) { var newDoc = Ares.Workspace.files.get(inEvent.userId); this.trace(inEvent.id, newDoc); this.switchToDocument(newDoc, $L("Switching files..."), inEvent.next); return true; }, /** * Switch file *and* project (if necessary) * @param {Object} newDoc * @param {String} popupMsg: message to display in popup message * @param {Code} next * @throws {String} throw an error when File ID is not found in cache */ switchToDocument: function(newDoc, popupMsg, next) { ares.assertCb(next); // safety net if ( ! newDoc ) { if (this.debug) { throw("File ID " + newDoc + " not found in cache!");} setTimeout( function() { next(new Error("File ID not found in cache!")); }, 0); return; } var oldDoc = this.activeDocument ; // may be undef when a project is closed var oldProject = Ares.Workspace.projects.getActiveProject(); // may be undef before opening the first file var safeNext = next; // function parameter is not a closure // don't open an already opened doc if ( oldDoc && newDoc.getId() === oldDoc.getId()) { setTimeout( function() { next(); }, 0); return ; } var newName = newDoc.getProjectData().getName() ; this.trace("switch " + (oldDoc ? "from " + oldDoc.getName() + " " : "") + "to " + newDoc.getName() ); var serial = []; // used to block onFocus event coming from text editor this.switching = true ; // select project if the document comes from a different // project compared to the project of the previous document if (!oldProject || oldProject.getName() !== newName){ var pname = oldProject ? "from " + oldProject.getName() + " " : "" ; this.trace("also switch project " + pname + ' to ' + newDoc.getProjectData().getName()); var project = Ares.Workspace.projects.get(newDoc.getProjectData().id); var projectList = ComponentsRegistry.getComponent("projectList"); var deimos = this.$.deimos; this.doShowWaitPopup({msg: $L("Switching project...")}); serial.push( projectList.selectProject.bind(projectList, project), deimos.projectSelected.bind(deimos, project) ); } var that = this ; serial.push( function(_next) { that.doShowWaitPopup({msg: popupMsg}); _next();}, this._switchDoc.bind(this, newDoc), function(_next) { that.aceFocus(); _next();} ); // no need to handle error, call outer next without params async.series( serial, function(err){ that.doHideWaitPopup(); that.switching = false ; safeNext(); }); }, // FIXME ENYO-3624: this function must trigger a reload of the designer // to take into account code modification discarded by user reloadDoc: function(doc,next) { ares.assertCb(next); var reloadedDoc = this.activeDocument ; this.activeDocument = null;// reset to trigger reload this._switchDoc(reloadedDoc, next); }, /** * switch Phobos or Deimos to new document * @param {Object} newDoc * @param {Function} next */ _switchDoc: function(newDoc,next) { ares.assertCb(next); var newProject; var phobos = this.$.phobos; var oldDoc = this.activeDocument ; var currentIF = newDoc.getCurrentIF(); var oldName = oldDoc ? "from " + oldDoc.getName() + " " : " " ; this.trace("switch " + oldName + ' to ' + newDoc.getName() + " IF is " , currentIF ); if (oldDoc && newDoc === oldDoc) { // no actual switch setTimeout(next,0); return; } // save current data, this is needed to save project files // without switching ace and deimos if (oldDoc) { oldDoc.setEditedData(phobos.getEditorContent()); } // open ace session (or image viewer) phobos.openDoc(newDoc); this.$.toolbar.resized(); this.activeDocument = newDoc; newProject = newDoc.getProjectData() ; Ares.Workspace.projects.setActiveProject( newProject.getName() ); this.addPreviewTooltip("Preview " + newProject.id); if (currentIF === 'code') { this.$.panels.setIndex(this.phobosViewIndex); this.manageControls(false); } else { phobos.designerAction(); this.manageControls(true); } this._fileEdited(); this.$.docToolBar.activateDocWithId(newDoc.getId()); setTimeout(next,0) ; }, /** * handle request close doc events * Request to save doc and close if user agrees * @param {Object} inSender * @param {Object} inEvent * @returns {true} */ handleCloseDocument: function(inSender, inEvent) { // inEvent.next callback is ditched. Ares will call removeTab // when file is closed no matter where the tab removal request // comes from. var doc = Ares.Workspace.files.get(inEvent.userId); async.waterfall([ this.requestSave.bind(this, doc), this.closeDoc.bind(this) ]); return true; // Stop the propagation of the event }, requestCloseCurrentDoc: function(inSender, inEvent) { async.waterfall([ this.requestSave.bind(this, this.activeDocument), this.closeDoc.bind(this) ]); return true; // Stop the propagation of the event }, /** * Close all files of a project * @param {[Object]} project, defaults to current project * @param {[Function]} next */ requestCloseProject: function (project, next) { this.trace("close project requested"); var serial = [] ; // build the serie of functions to be fed to async.series this.foreachProjectDocs( this._chainSaveClose.bind(this, serial), project ); // the real work is done here async.series( serial, next ) ; }, _chainSaveClose: function(serial,doc) { this.trace("close project will close file " + doc.getName(), doc); var close = this.closeDoc.bind(this,doc) ; var save = this.requestSave.bind(this,doc); // save and close active doc the last to avoid switch var method = this.activeDocument === doc ? 'push' : 'unshift' ; serial[method](save, close); }, requestCloseCurrentProject: function(inSender, inEvent) { this.requestCloseProject(); return true ; }, requestCloseAll: function(inSender, inEvent) { this.trace("close project requested"); var serial = [] ; // build the serie of functions to be fed to async.series Ares.Workspace.files.forEach( this._chainSaveClose.bind(this, serial) ); // the real work is done here async.series( serial ) ; return true ; }, /** * query the user for save before performing next action * @param {Object} doc to save * @param {Function} next */ requestSave: function(doc, next) { ares.assertCb(next); var popup = this.$.savePopup ; if (doc.getEdited() === true) { this.trace("request save doc on ", doc.getName()); popup.setMessage('"' + doc.getFile().path + '" was modified.<br/><br/>Save it before closing?') ; popup.setTitle($L("Document was modified!")); popup.setActionButton($L("Don't Save")); popup.setActionCallback( function() {next(null, doc);}); popup.setAction1Button($L("Save")); popup.setAction1Callback( (function() { this.saveDoc(doc); next(null, doc); }).bind(this) ); popup.setCancelCallback( (function() { this.reloadDoc(doc, ares.noNext); this.aceFocus(); next(new Error('canceled')); }).bind(this) ) ; popup.show(); } else { setTimeout( next.bind(null,null, doc), 0); } }, designDocument: function(inData) { this.trace(); // send all files being edited to the designer, this will send code to designerFrame this.syncEditedFiles(inData.projectData); // then load palette and inspector, and tune serialiser behavior sends option data to designerFrame this.$.deimos.loadDesignerUI( inData, (function(err) { this.trace("designDocument -> loadDesignerUI done, err is ",err); }).bind(this) ); // switch to Deimos editor this.showDeimosPanel(); // update an internal variable this.activeDocument.setCurrentIF('designer'); }, /** * Update code running in designer * @param {Ares.Model.Project} project, backbone object defined in WorkspaceData.js */ syncEditedFiles: function(project) { var projectName = project.getName(); this.trace("update all edited files on project", projectName); function isProjectFile(model) { return model.getFile().name !== "package.js" && model.getProjectData().getName() === projectName ; } // backbone collection Ares.Workspace.files.filter(isProjectFile).forEach(this.updateCode, this); }, /** * * @param {Ares.Model.File} inDoc is a backbone object defined in FileData.js */ updateCode: function(inDoc) { var filename = inDoc.getFile().path, aceSession = inDoc.getAceSession(), code = aceSession && aceSession.getValue(); // project is a backbone Ares.Model.Project defined in WorkspaceData.js var projectName = inDoc.getProjectData().getName(); this.trace('code update on file', filename,' project ' , projectName); this.$.deimos.syncFile(projectName, filename, code); }, undo: function(next) { ares.assertCb(next); this.$.phobos.undoAndUpdate(next) ; }, redo: function(next) { ares.assertCb(next); this.$.phobos.redoAndUpdate(next) ; }, loadDesignerUI: function(inData, next) { this.$.deimos.loadDesignerUI(inData, next); } }); /** Ares.FileMenu extends _onyx.MenuDecorator_. This contains the various drop-down options (save, close, etc.) in our file menu */ enyo.kind({ name: "Ares.FileMenu", kind: "onyx.MenuDecorator", classes:"aresmenu ares-right-margin ares-left-margin", events: { onAceFocus: "" }, // value is the name of an event that will be sent to Phobos // through onSelect event -> EnyoEditor.fileMenuItemSelected -> Phobos.fileMenuItemSelected components: [ {tag:"button", content: "File"}, {kind: "onyx.Menu", floating: true, classes:"sub-aresmenu", maxHeight: "100%", components: [ {name: "saveButton", value: "saveCurrentDoc", classes:"aresmenu-button", components: [ {kind: "onyx.IconButton", src: "$phobos/assets/images/menu-icon-save-darken.png"}, {content: $L("Save")} ]}, {name: "saveAsButton", value: "requestSaveDocAs", classes:"aresmenu-button", components: [ {kind: "onyx.IconButton", src: "$phobos/assets/images/menu-icon-save-darken.png"}, {content: $L("Save as...")} ]}, {name: "saveProjectButton", value: "saveProjectDocs", classes:"aresmenu-button", components: [ {kind: "onyx.IconButton", src: "$phobos/assets/images/menu-icon-save-darken.png"}, {content: $L("Save Project")} ]}, {classes: "onyx-menu-divider"}, {name: "closeButton", value: "requestCloseCurrentDoc", classes:"aresmenu-button", components: [ {kind: "onyx.IconButton", src: "$phobos/assets/images/menu-icon-stop.png"}, {content: $L("Close")} ]}, {name: "closeProjectButton", value: "requestCloseCurrentProject", classes:"aresmenu-button", components: [ {kind: "onyx.IconButton", src: "$phobos/assets/images/menu-icon-stop.png"}, {content: $L("Close Project")} ]}, {name: "closeAllButton", value: "requestCloseAll", classes:"aresmenu-button", components: [ {kind: "onyx.IconButton", src: "$phobos/assets/images/menu-icon-stop.png"}, {content: $L("Close All")} ]} ]} ] });