/** * Project : AMDA-NG4 * Name : InteractiveNode.js * @class amdaModel.InteractiveNode * @extends amdaModel.AmdaNode * @brief Generic Model of Interactive Node * @author CDA * @version $Id: InteractiveNode.js 2683 2014-12-02 10:58:53Z elena $ */ Ext.define('amdaModel.InteractiveNode', { extend: 'amdaModel.AmdaNode', fields: [ {name: 'contextNode', type: 'amdaModel.AmdaNode', persist: false}, {name: 'objectDataModel', type: 'string', persist: false}, {name: 'object', type: 'object', persist: false}, {name: 'moduleId', type: 'string', persist: false}, {name: 'filtered', type: 'boolean', defaultValue: false, persist: false}, {name: 'disable', type: 'boolean', defaultValue: false, persist: false} ], statics: { preloadNodes : function(node,onready) { var me = this; var nodesToLoad = new Array(); nodesToLoad.push(node); this.preloadTreeNode(node, nodesToLoad, function (node) { var isFinish = true; nodesToLoad.forEach(function (element, index, array) { if (!element.isLoaded()) isFinish = false; }); if (isFinish && onready) onready.call(); }); }, preloadTreeNode : function(node, nodesToLoad, onloaded) { var me = this; if (node.isLoaded()) { node.eachChild(function(n) { if (!n.isLoaded() && !n.isLeaf()) { nodesToLoad.push(n); me.preloadTreeNode(n,nodesToLoad,onloaded); } }); if (onloaded) onloaded.call(me,node); return; } node.store.load({ node : node, callback : function(records, operation, successful) { records.forEach(function (record) { if (!record.isLoaded() && !record.isLeaf()) { nodesToLoad.push(record); me.preloadTreeNode(record,nodesToLoad,onloaded); } }); if (onloaded) onloaded.call(me,node); } }); } }, constructor : function(config) { this.callParent(arguments); this.set('nodeType',this.self.nodeType); this.set('ownerTreeId',amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); // if id of this node have root category suffix if (Ext.util.Format.substr(this.get('id'), -(amdaUI.ExplorerUI.CAT_SUFFIX.length), this.get('id').length) === amdaUI.ExplorerUI.CAT_SUFFIX) { // set the expanded property to true this.set('expanded',true); } }, /** * this method is overriden into ExecutableNode to return true */ isExecutable: function() { return false; }, /** * open Module with THIS NODE */ editInModule : function (contextNode) { // set the contextNode of this node this.set('contextNode',contextNode); // parameter module var me = this; myDesktopApp.getLoadedModule(this.get('moduleId'),true, function (module) { // If the node to edit is not already linked to this module if (module.getLinkedNode() != me) { // set relative node into parameter Module module.setLinkedNode(me); if (contextNode==null) { // set the root node as contextNode contextNode = me.getRootNode(); } module.setContextNode(contextNode); } else if (module.getLinkedNode() != null){ //TODO the node to edit is already edited // myDesktopApp.warningMsg('This object is being edited'); //Sol1: msg alert: "warning this node is already edited! If you want to get the original, please press the 'reset' button"->'OK' //Sol2: msg with user choice: "warning this node is already edited! Would you confirm this action and lost your modification?"->'Confirm','Cancel' } // Opening parameter window module.createWindow(); }); }, /** * Method to rename the workspace node */ rename: function(value,callBackFn) { var dataToSend = {id : this.get('id'), old_name: this.modified.text, name: value, parent : this.parentNode.get('id'), leaf: this.isLeaf(), nodeType: this.get('nodeType')}; AmdaAction.renameObject(dataToSend, callBackFn); }, /** * Method to rename the workspace node when D&D */ renameDD: function(parentId, callBackFn) { var dataToSend = {id : this.get('id'), old_name: this.get('name'), name: this.get('name'), parent : parentId, leaf: this.isLeaf(), nodeType: this.get('nodeType')}; AmdaAction.renameObject(dataToSend, callBackFn); }, /** * validation method on name (used in module forms) * @param name the name to validate * @returns */ isValidName : function(name, callBackFn) { var dataToSend = {name: name, nodeType: this.get('nodeType'), leaf: this.isLeaf()}; AmdaAction.validNameObject(dataToSend, callBackFn); /*if (name.match(/[a-z,0-9]/gi)) { var subTreeRoot = this.getRootNode(); //TODO - check to be sure that preload is already applied before to use the isValidName function (normally, this is the case) var nodeWithSameName = subTreeRoot.findChild('text', name, true); if (nodeWithSameName !== null && this !== nodeWithSameName) { return "This name already exists in this subtree!
Do you want to overwrite it?"; } else return true; } else { return "Name is required"; }*/ }, /** * Method to persist modifications of an AmdaObject by Server side and update the workspace * node linked to a Module */ update : function(opt) { AmdaAction.modifyObject(this.get('object').getJsonValues(true), function(res,e){ if(e.status) { if (res.id) { if (!this.get('contextNode')) { // set the root node of 'Derived Parameters' tree as contextNode this.set('contextNode',this.getRootNode()); } this.get('contextNode').expand(false,false); this.myGetOwnerTree().getSelectionModel().select(this); if (opt) { var scope = opt.scope ? opt.scope : this; if (opt.callback) opt.callback.call(scope,'update'); } Ext.Msg.alert('Complete', 'Object '+this.get('object').get('name')+' has been modified'); // fix the modifications for object this.get('object').commit(); if (res.info) { this.set('info',res.info); } if (this.get('nodeType') == 'myDataParam') { if (res.isSpectra) { this.set('iconCls', 'icon-spectra'); } else { if (res.size > 1) this.set('iconCls', 'icon-unknowntype'); } } // update my data on possibble mask change if (res.updateMyData) { this.updateMyData(); this.updateMask(res.mask); } // reload object into the view of corresponding Module var me = this; myDesktopApp.getLoadedModule(this.get('moduleId'), true, function (module) { module.getUiContent().setObject(me.get('object')); }); } else { //TODO proper error message handling // error code from server; but e.status==true // revert all modifications since last load or commit this.get('object').reject(); myDesktopApp.errorMsg(res.error); } } else { // revert all modifications since last load or commit this.get('object').reject(); myDesktopApp.errorMsg(e.message); } },this); }, /** * Method to create a new AmdaObject by server side and create the workspace node linked to a Module * under its contextNode or the root node corresponding to this nodeType category */ create : function(opt) { if (!this.get('contextNode') || (this.get('contextNode').data.id == 'sharedtimeTable-treeRootNode')) { // set the root node of 'Derived Parameters' tree as contextNode this.set('contextNode',this.getRootNode()); } // call the Ext.Direct method to create parameter AmdaAction.createObject(this.get('object').getJsonValues(false), this.get('contextNode').get('id'), function(res,e){ //success if(e.status) { // if correct response received if (res.id) { //if (res.id || res.error == 'NAME_EXISTS') { // 'save as' case ; delete old node if it exists if (this.toRename) { this.toRename = false; var myRoot = this.getRootNode(); // search the same named node to override var updateNode = myRoot.findChild('text',this.get('object').get('name'),true); // destroy the overrided node updateNode.parentNode.removeChild(updateNode);//TODO ??if destroy==true => too many recursions.... updateNode.destroy(); } // set text of this node this.set('text',this.get('object').get('name')); //set id of this node this.set('id',res.id); this.internalId = res.id; // set id of node's object this.get('object').set('id',res.id); if (res.created){ // set the created date this.get('object').set('created',res.created); } if (res.info){ // set the tooltip this.set('info',res.info); //set globalStart & global Stop to be used for time selection if (this.get('nodeType') == 'myDataParam') { var startStop = res.info.split("
"); var globalStart = startStop[1].substr(0,16); var globalStop = startStop[1].substr(17,16); this.set('globalStart', globalStart); this.set('globalStop', globalStop); if (res.mask) this.set('linkedMask', res.mask); if (res.size) this.set('size', res.size); if (res.isSpectra) { this.set('iconCls', 'icon-spectra'); } else { if (res.size > 1) this.set('iconCls', 'icon-unknowntype'); } } } //TODO do we need this commission ??? // fix the modifications for object this.get('object').commit(); // if ownerTree panel is not active if (this.myGetOwnerTree().ownerCt.getActiveTab()!==this.myGetOwnerTree()) { // set ownerTree panel as the active tab - to enable selection of this node his ownerTree must have a view this.myGetOwnerTree().ownerCt.setActiveTab(this.myGetOwnerTree()); } Ext.Msg.alert('Complete', 'New object '+this.get('object').get('name')+' has been created'); // expand the contextNode this.get('contextNode').expand(false, function() { if (!this.get('contextNode').findChild('text',this.get('text'))) { // create node in tree as child of contextNode this.get('contextNode').appendChild(this); } // select the new node this.myGetOwnerTree().getSelectionModel().select(this); if (opt) { var scope = opt.scope ? opt.scope : this; if (opt.callback) opt.callback.call(scope,'create'); } }, this); // myDataParamNode - update MyData subtree //TODO put this in mydataparamnode if (res.updateMyData) { this.updateMyData(); this.updateMask(res.mask); } } // error code from server; but e.status==true else { myDesktopApp.errorMsg(res.error); // revert all modifications since last load or commit this.get('object').reject(); } } // failure: e.status == false else { // revert all modifications since last load or commit this.get('object').reject(); //TODO: this.destroy(); myDesktopApp.errorMsg(e.message); } },this); }, /** * Generic part of Context Menu * */ allMenuItems : function() { var src = this.self.objectName; var menuItems = [ { fnId : 'root-createLeaf', text : 'Create '+ src }, { fnId : 'root-createDir', text : 'Create Folder' }, { fnId : 'dire-createLeaf', text : 'Create ' + src }, { fnId : 'dire-createDir', text : 'Create Folder' }, { fnId : 'dire-renameNode', text : 'Rename Folder' }, { fnId : 'dire-deleteNode', text : 'Delete Folder' }, { fnId : 'leaf-editLeaf', text : 'Edit ' + src }, { fnId : 'leaf-renameNode', text : 'Rename ' + src }, { fnId : 'leaf-deleteNode', text : 'Delete '+ src } ]; return menuItems; }, allMenuMultiItems : function() { var menuMulti = [ { fnId : 'mult-deleteMulti', text : 'Delete selected ' + this.self.objectName + 's' } ]; return menuMulti; }, getAllContextMenuItems: function(){ return this.allMenuItems(); }, getMultiContextMenuItems: function(){ return this.allMenuMultiItems(); }, /** * default implementation * no menu display if there's no override of this function */ getMultiContextMenuItems: function(){ return null; }, /** * Context Menu Actions * */ onMenuItemClick : function(menu,item,event) { // fnId parsing : var fnId = Ext.util.Format.substr(item.fnId, 5, item.fnId.length); switch (fnId) { case 'deleteNode': this.deleteNode(); break; case 'createDir': this.createDir(); break; case 'createLeaf': this.createLeaf(this); break; case 'renameNode': this.renameNode(); break; case 'editLeaf': this.editLeaf(this); break; case 'deleteMulti': this.deleteMulti(); break; case 'plotParam': this.createPlot(this); break; case 'downParam': this.createDownload(this); break; default: break; } // switch end }, getTimeFromNode: function(node) { var startString = new String(node.get('globalStart')); var stopString = new String(node.get('globalStop')); var startDate = new Date(startString.replace(/\-/g,'\/').replace(/[T|Z]/g,' ')); var stopDate = new Date(stopString.replace(/\-/g,'\/').replace(/[T|Z]/g,' ')); if (stopDate - startDate > 86400000 ) { var startTime = Ext.Date.add(stopDate, Ext.Date.DAY, -1); var timeObj = {start: Ext.Date.format(startTime, 'Y/m/d'), stop: node.get('globalStop')}; } else { var timeObj = {start: node.get('globalStart'), stop: node.get('globalStop')}; } return timeObj; }, createPlot: function(node) { if (node.get('disable')) return; var me = this; myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.plot.id, true, function (module) { if (!myDesktopApp.desktop.getWindow(myDesktopApp.dynamicModules.plot.id)) { var request = Ext.create(amdaPlotObj.PlotRequestObject.$className); var newNode = Ext.create(amdaModel.PlotNode.$className, { object : request }); // edit newNode into Plot Module with node as contextNode newNode.editInModule(); if(node.get('globalStart') != 'depending on mission' && ( node.get('nodeType') == 'localParam' || node.get('nodeType') == 'myDataParam')) { module.getUiContent().setTimeFromData(me.getTimeFromNode(node)); } } module.getUiContent().addParameter(node); }); }, createDownload: function(node) { if (node.get('disable')) return; if (node.get('notyet')) { myDesktopApp.warningMsg('Sorry! access to this parameter is restricted.'); return; } var me = this; myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.download.id, true, function (module) { if (!myDesktopApp.desktop.getWindow(myDesktopApp.dynamicModules.download.id)) { var request = Ext.create(amdaModel.Download.$className); amdaModel.DownloadNode.set('object',request); // singleton! amdaModel.DownloadNode.editInModule(); if(node.get('globalStart') != 'depending on mission' && ( node.get('nodeType') == 'localParam' || node.get('nodeType') == 'myDataParam')) { module.getUiContent().setTimeFromData(me.getTimeFromNode(node)); } } if (node.get('needsArgs') && !node.get('isSpectra')) { module.getUiContent().fireEvent('openParamEditor',node.get('id')); } else { var paramName; switch (node.$className) { case 'amdaModel.AliasNode' : paramName = "#"+node.get('text'); break; case 'amdaModel.DerivedParamNode' : paramName = "ws_"+node.get('text'); break; case 'amdaModel.MyDataParamNode' : paramName = 'wsd_'+node.get('text'); break; default : if (node.get('alias')!= "" ) paramName = "#"+node.get('alias'); else paramName = node.get('id'); } module.addParam(paramName,true); } }); }, deleteNode: function() { // if the target is a directory if (!this.isLeaf()) { // determine if this directory is empty before launching the delete confirmation method this.isNotEmptyDir(this.confirmDirectoryDeletion); // else (the target is a leaf) } else { // no confirmation prompt for leaves this.confirmDirectoryDeletion(false); } }, /** * this method return if node has Childs even if it was not already loaded */ isNotEmptyDir : function(callbackFn) { var hasChilds; // if node not already loaded if (!this.isLoaded()){ // call directFunction to load this node AmdaAction.getTree({node:this.get('id'),nodeType:this.get('nodeType')},function(res,e){ callbackFn.call(this,res.length>0?true:false); },this); } else { callbackFn.call(this,this.hasChildNodes()); } }, /** * this method is used to display a confirmation message */ confirmDirectoryDeletion : function(isNotEmptyDir) { // if this is a non-empty directory if (isNotEmptyDir) { // Prompt to the user if he also wants to delete its content Ext.Msg.confirm('non-empty directory', 'The target is a non-empty directory!
Do you want to continue and also delete its content?', function(btn, text){ if (btn == 'yes'){ // do delete this.realDelete(); } },this); } else { this.realDelete(); } }, /* * Call the extDirect method to delete parameter * Callback method needed to execute node deletion in tree if id in result or to show error msg */ realDelete : function() { AmdaAction.deleteObject({id: this.get('id'), leaf: this.isLeaf(), nodeType: this.get('nodeType')}, function(res,e){ //TODO proper errors handling // node deletion in tree if (res) { // if success if (res.id) { //Ext.Msg.show({title:'Warning', msg: 'Requests with parameter '+node.data.text+' are deleted', icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK}); if (this.parentNode) { if (this.isLeaf()){ var moduleId = this.get('moduleId'); // if really interactive node if (moduleId) { var me = this; myDesktopApp.getLoadedModule(moduleId, true, function (module) { var editedNode = module.getLinkedNode(); // file node is not linked directly to the module var isThisFile = false; if (editedNode && editedNode.$className == 'amdaModel.MyDataParamNode') if (editedNode.get('fileObject').get('fileName') == me.get('text')) isThisFile = true; if (me.$className == 'amdaModel.DerivedParamNode') { var obj = { paramId : 'ws_'+me.get('text') }; AmdaAction.compilParamDelete(obj); } if (editedNode === me || isThisFile){ var newNode = Ext.ModelManager.create({leaf : true}, me.$className); // several tabs could be connected to one node if (moduleId === myDesktopApp.dynamicModules.plot.id) { var linkedNodes = module.linkedNodes; if (linkedNodes) { linkedNodes.each(function(key, value){ if (value === me) { linkedNodes.replace(key,newNode); var tabPanel = module.getUiContent().tabPanel.items.getByKey(key); tabPanel.setObject(Ext.create(amdaModel.Plot.$className, {})); } }, me); } } newNode.editInModule(); } }); } } //update mask info in myData if (res.maskDesc && !res.maskDeleted) { this.parentNode.set('info', res.maskDesc); this.updateMyDataParam(res.mask, res.maskDesc); } this.remove(); } //TODO Several special node-dependent actions - to move to node functions.. // nodes of another nodeType to be deleted as they depend on deleted node if (res.params) { this.deleteDependence(res.params); //TODO reset } // mask was deleted or updated - to update mydata tree if (res.maskDeleted) { this.updateMyData(); } } else { myDesktopApp.warningMsg(res.error); } } else { myDesktopApp.errorMsg(e.message); } }, this); }, /* * Delete musti selection */ deleteMulti: function() { var selection = this.myGetOwnerTree().getSelectionModel().selected.items; alert(selection.length +' to delete!'); Ext.Array.each(selection,function(item,index,allItems){ item.deleteNode(); }) }, /* * Create Folder */ createDir: function() { var me = this; amdaModel.InteractiveNode.preloadNodes(this.getRootNode(), function() { var newNode = Ext.create(me.$className, { leaf : false, nodeType : me.get('nodeType'), text : amdaModel.AmdaNode.NEW_DIR_NAME, children : [] }); // insert the new node as a child of node newNode = me.insertChild(0, newNode); // start text edition on this new directory node me.expand(false); newNode.expand(false); // select the new node me.myGetOwnerTree().getSelectionModel().select(newNode); // call the renameNode method for this new node newNode.renameNode(); }); }, /* * */ createLeaf: function(contextNode) { // create new node with the same type than the contextNode var newNode = Ext.create(contextNode.$className, {leaf : true}); // load the rootNode and recursively all its child nodes amdaModel.InteractiveNode.preloadNodes(contextNode.getRootNode(), function() { // edit newNode into Parameter Module with node as contextNode newNode.editInModule(contextNode); }); }, renameNode: function() { if (this.myGetOwnerTree()) { // load the rootNode and recursively all its child nodes if not already loaded var me = this; amdaModel.InteractiveNode.preloadNodes(this.getRootNode(), function() { // fire the edition event on tree me.myGetOwnerTree().fireEvent('edition',me.myGetOwnerTree().view, me.myGetOwnerTree().getSelectionModel().selected.items[0]); }); } else { myDesktopApp.errorMsg('tree is undefined'); //TODO: log error... tree is undefined } }, /* * load the rootNode and recursively all its child nodes * to know all names of DerivedParameters */ editLeaf: function() { var me = this; amdaModel.InteractiveNode.preloadNodes(this.getRootNode(), function() { if (me.get('object')) { // launch edition of parameter into parameter module me.editInModule(); } else { // call the ext method to get the details of parameter // the edition of real parameter is done into callback method getObjectCallback AmdaAction.getObject(me.get('id'), me.get('nodeType'), me.getObjectCallback, me); } }); }, /* * */ getObjectCallback : function(result,remoteEvent) { var t = remoteEvent.getTransaction(); if (result) { /*TODO why Caro used this??? var reader = Ext.create('Ext.data.reader.Json',{ model: this.get('objectDataModel') }); var resultSet = reader.read(result); var paramObj = resultSet.records[0];// */ var paramObj = Ext.create(this.get('objectDataModel'), result); // set parameter into node this.set('object',paramObj); // Edition of parameter into parameter Module this.editInModule(); } else { // EXCEPTION : parameter not found !? myDesktopApp.errorMsg(t.action + "." + t.method + " : No parameter '" + this.get('name') + "' found!"); } } });