/** * 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: 'needsArgs', type:'boolean', defaultValue: false}, {name: 'predefinedArgs', type:'boolean', defaultValue: false}, {name: 'disable', type: 'boolean', defaultValue: false, persist: false}, {name: 'last_update', type: 'int', defaultValue: 0} ], 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); } }); } }, onReady : null, constructor : function(config) { this.callParent(arguments); this.set('nodeType',this.self.nodeType); this.set('ownerTreeId',amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); if (this.get('id')) { // TODO why sometimes (delete in remote interoper tree) this.get('id') = undefined ? // 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, onReady) { // 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(onReady); }); }, /** * 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.data.parentId, 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); }, /** * 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'); if(opt.notDisplayMsg || opt.callback ){ //do nothing }else{ 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 (res.last_update) { this.set('last_update', res.last_update); this.get('object').set('last_update', res.last_update); } // myDataParam and Parameter special update this.specialUpdate(res, false); // reload object into the view of corresponding Module var me = this; myDesktopApp.getLoadedModule(this.get('moduleId'), true, function (module) { if (!opt || !opt.plot) { module.getUiContent().setObject(me.get('object')); } else { module.getUiContent().reloadPlot(me); } }); } 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') || (this.get('contextNode').data.id == 'sharedcatalog-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); } var reloadObject = false; if (res.last_update) { this.set('last_update', res.last_update); this.get('object').set('last_update', res.last_update); reloadObject = true; } //set globalStart & global Stop to be used for time selection this.specialUpdate(res, true); // reload object into the view of corresponding Module var me = this; if (reloadObject) { myDesktopApp.getLoadedModule(this.get('moduleId'), true, function (module) { if (!opt || !opt.plot) { module.getUiContent().setObject(me.get('object')); } else { module.getUiContent().reloadPlot(me); } }); } //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()); } if(opt && opt.notDisplayMsg){ //do nothing }else{ 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); } // 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); }, specialUpdate : function() {}, /** * 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(false); break; case 'editLeaf': this.editLeaf(); break; case 'deleteMulti': this.deleteMulti(); break; case 'plotParam': this.createPlot(this); break; case 'downParam': this.createDownload(this); break; case 'generateCatalog': this.generateCatalog(this); break; case 'generateTT': this.generateTT(this); break; case 'plotTT': this.createPlot(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 H:i:s'), stop: Ext.Date.format(stopDate, 'Y/m/d H:i:s')}; var timeObj = {start: Ext.Date.format(startTime, 'Y/m/d'), stop: Ext.Date.format(stopDate, 'Y/m/d')}; } else { var timeObj = {start: node.get('globalStart'), stop: node.get('globalStop')}; } return timeObj; }, createPlot: function(node) { if (node.get('disable')) return; myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.plot.id, true, function (module) { module.addParameter(node); }); }, createDownload: function(node) { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.download.id, true, function (module) { module.addParameter(node); }); }, 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 win = myDesktopApp.getDesktop().getWindow(moduleId); // if window is really open if (win) { 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; // node was created BEFORE tree loading; node.id are different ; but it is the same node if ( editedNode && editedNode.get('id') == me.get('id') && editedNode !== me ) { module.setLinkedNode(me); 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 : [], parentId: me.get('id') }); // insert the new node as a child of node 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(true); }); }, /* * */ 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(deleteOnFailure) { 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 var item = me.myGetOwnerTree().getSelectionModel().selected.items[0]; item.deleteOnFailure = deleteOnFailure; me.myGetOwnerTree().fireEvent('edition', me.myGetOwnerTree().view, item); }); } else { myDesktopApp.errorMsg('tree is undefined'); } }, /* * load the rootNode and recursively all its child nodes * to know all names of DerivedParameters */ editLeaf: function(onReady) { var me = this; amdaModel.InteractiveNode.preloadNodes(this.getRootNode(), function() { if (me.get('object')) { // launch edition of parameter into parameter module me.editInModule(null, onReady); } else { // call the ext method to get the details of parameter // the edition of real parameter is done into callback method getObjectCallback if (onReady) me.onReady = onReady; AmdaAction.getObject(me.get('id'), me.get('nodeType'), me.getObjectCallback, me); } }); }, /* * */ getObjectCallback : function(result,remoteEvent) { var t = remoteEvent.getTransaction(); if (result) { var paramObj = Ext.create(this.get('objectDataModel'), result); // set parameter into node this.set('object',paramObj); // Edition of parameter into parameter Module this.editInModule(null, this.onReady); this.onReady = null; } else { // EXCEPTION : parameter not found !? myDesktopApp.errorMsg(t.action + "." + t.method + " : No parameter '" + this.get('name') + "' found!"); } }, generateCatalog: function(node){ id = node.get('id'); name = node.get('text'); Ext.Msg.prompt('Create catalog', 'Enter the number of columns:', function (btn, text) { if (btn == 'ok') { var catObj = Ext.create('amdaModel.Catalog'); var catNode = Ext.create('amdaModel.CatalogNode', {leaf: true}); catObj.set('relatedTimeTableId', id); //creatDate = new Date(this.object.get('created')); //date = Ext.Date.format(creatDate, 'Y-m-d\\TH:i:s'); descr = 'Generated by CDPP/Amda Time Table Module \n' + 'From Time Table: ' + name; catObj.set('description', descr ); var nbParam = parseInt(text); if ((nbParam <= 0) || (nbParam > 100)) { nbParam = 1; } catObj.set('nbParameters', nbParam); catNode.set('object', catObj); var explorerTree = Ext.getCmp(amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); var catRootNode = explorerTree.getRootNode().findChild('id', 'catalog-treeRootNode', true); amdaModel.InteractiveNode.preloadNodes(catRootNode.getRootNode(), function () { catNode.editInModule(); }); } }, this); }, generateTT: function(node){ var catObj = Ext.create('amdaModel.TimeTable'); var timeTabNode = Ext.create('amdaModel.TimeTableNode',{leaf : true}); catObj.set('relatedCatalogId', node.get('id')); desc = 'Generated by CDPP/Amda Catalog Module \n'; if (node.get('description')) desc = desc + node.get('description'); catObj.set('description', desc); timeTabNode.set('object',catObj); var explorerTree = Ext.getCmp(amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); var ttRootNode = explorerTree.getRootNode().findChild('id', 'timeTable-treeRootNode', true); amdaModel.InteractiveNode.preloadNodes(ttRootNode.getRootNode(), function() { timeTabNode.editInModule(); }); }, });