/** * Project  : AMDA-NG4 * Name : ExplorerUI.js * @class amdaUI.ExplorerUI * @extends Ext.tab.Panel * @class amdaUI.TreeToolColumn * @extends Ext.tree.Column * @class MyTreeEditor * @extends Ext.grid.plugin.CellEditing * @brief Explorer View * @author CDA */ Ext.define('amdaUI.TreeToolColumn', { extend: 'Ext.tree.Column', alias: 'widget.treetoolcolumn', /** * Add more tools here. These will be on the prototype for all TreeToolColumns */ tools: { 'info': 'js/resources/images/16x16/info_mini.png' }, initComponent: function() { var me = this; me.addEvents('toolclick'); me.callParent(); me.on('toolclick', me.toolHandler, me); }, renderer: function(value, metaData, record, rowIdx, colIdx, store, view) { var toolCol = view.getHeaderAtIndex(colIdx); if (!toolCol.toolIsVisible(record)) return value; var toolId = 'tool-' + rowIdx + '-' + colIdx, toolImg = toolCol.tools[toolCol.tool], imgHtml = Ext.DomHelper.markup({ id : toolId, tag : 'img', tooltype: toolCol.tool, src : toolImg, style : 'cursor:hand;' }); return value + ' ' + imgHtml; }, processEvent: function(type, view, cell, recordIndex, cellIndex, e) { if(type === "click" && e.target.tagName === "IMG") { var tooltype = e.target.getAttribute("tooltype"); if(tooltype) return this.fireEvent("toolclick", view, cell, recordIndex, cellIndex, e); } return this.fireEvent.apply(this, arguments); }, /** * Override this when you add columns to the tree... see example below */ // toolHandler: function() { // alert("override this"); // }, toolIsVisible : function(record) { return false; } }); // ExplorerUI Ext.define('amdaUI.ExplorerUI', { extend: 'Ext.tab.Panel', alias : 'widget.panelExplorer', statics: { RESRC_TAB : { TREE_ID:'resourcesTree', TREE_TYPE:'resources' }, OPE_TAB : { TREE_ID:'operationsTree', TREE_TYPE:'operations' }, JOB_TAB : { TREE_ID:'jobsTree', TREE_TYPE:'jobs' }, CAT_SUFFIX: '-treeBase', ROOT_SUFFIX: '-treeRootNode', SUB_ROOT_SUFFIX : 'RootNode', ITEM_KIND_BASE : 'base', ITEM_KIND_ROOT : 'root', ITEM_KIND_LEAF : 'leaf', ITEM_KIND_DIRE : 'dire', ITEM_KIND_PARA : 'para', ITEM_KIND_MISS : 'miss' }, initComponent : function (config) { var explorerModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id); var myConf = { split : true, width : '100%', height : '100%', autoScroll : true, border : true, header : false, defaults : { // applied to each contained panel containerScroll : true }, stateful: true, //stateId: 'tp1', stateEvents: ['tabchange'], getState: function() { return { activeTab: this.items.findIndex('id',this.getActiveTab().id) }; }, applyState: function(s) { this.setActiveTab(s.activeTab); }, items: [ this.initTree(amdaUI.ExplorerUI.RESRC_TAB.TREE_TYPE), this.initTree(amdaUI.ExplorerUI.OPE_TAB.TREE_TYPE), this.initTree(amdaUI.ExplorerUI.JOB_TAB.TREE_TYPE) ], tbar : [ { xtype : 'combo', fieldLabel: 'Filter', labelWidth: 25, width: 140, store: explorerModule.filtersStore, queryMode: 'local', displayField: 'name', valueField: 'id', listeners : { scope : this, select: function(combo, records) { AmdaAction.setCrtFilterId({id : records[0].get('id')}, function (result, e) { var t = e.getTransaction(); if (e.status) { if (result) { var explorerModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id); if (explorerModule) explorerModule.setCrtFilter(); } else Ext.Msg.show( { title : 'Filter', msg : 'Cannot apply filter', modal : true, icon : Ext.Msg.ERROR, buttons : Ext.Msg.OK }); } else { // FAILURE Ext.Msg.show({title:'Error System', msg: e.message, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK}); } },this); } } }, { text: '', iconCls : 'icon-parameters', tooltip: { text: 'Edit Filter', align: 'bl-tl' }, handler: function(t){ myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.filters.id, true, function (module) { module.createWindow(); }); } }, { text: '', iconCls : 'icon-remover', tooltip: { text: 'Reset Filter', align: 'bl-tl' }, handler: function(t){ var explorerModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id); explorerModule.resetFilter(); } }, '-', { xtype: 'displayfield', fieldLabel: 'SortBy', width: 40 }, { text: 'Name', scope : this, tooltip: { text: 'Sort out AMDA DataBase Data by Mission Name', align: 'bl-tl' }, pressed: true, enableToggle : true, toggleGroup: 'sorting', handler: function(){ var tree = Ext.getCmp(amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); tree.getStore().sort([ { sorterFn: function(o1, o2){ if (o1.get('nodeType') !== 'localParam') return; return o1.get('text').toUpperCase() < o2.get('text').toUpperCase() ? -1 : 1; } }]); this.updateFilter(); } }, { text: 'Target', scope : this, tooltip: {text: 'Sort out AMDA DataBase Data by Mission Main Target', align: 'bl-tl'}, enableToggle : true, toggleGroup: 'sorting', handler: function(){ var tree = Ext.getCmp(amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); tree.getStore().sort([{ property : 'rank' }]); this.updateFilter(); } }] }; Ext.apply (this , Ext.apply (arguments, myConf)); this.callParent(arguments); }, initTree : function(treeType) { switch (treeType) { case amdaUI.ExplorerUI.RESRC_TAB.TREE_TYPE: treeId = amdaUI.ExplorerUI.RESRC_TAB.TREE_ID; break; case amdaUI.ExplorerUI.OPE_TAB.TREE_TYPE: treeId = amdaUI.ExplorerUI.OPE_TAB.TREE_ID; break; case amdaUI.ExplorerUI.JOB_TAB.TREE_TYPE: treeId = amdaUI.ExplorerUI.JOB_TAB.TREE_ID; break; default: treeId = amdaUI.ExplorerUI.RESRC_TAB.TREE_ID; break; } var store = Ext.create('Ext.data.TreeStore', { root: { expanded: true, nodeType : treeType }, model: 'amdaModel.AmdaNode', sorters:[{ direction: 'ASC' , sorterFn: function(o1, o2){ if (o1.get('nodeType') !== 'localParam') return; return o1.get('text').toUpperCase() < o2.get('text').toUpperCase() ? -1 : 1; } }], listeners: { beforeload: function(store, operation){ store.proxy.extraParams = { nodeType: operation.node.get('nodeType') }; } } }); var menu = new Ext.menu.Menu(); var tree = Ext.create('Ext.tree.Panel', { id : treeId, title: treeType, store: store, rootVisible: false, animate: false, hideHeaders : true, selModel: Ext.create('Ext.selection.TreeModel', { // ignoreRightMouseSelection: true, mode: 'MULTI' }), viewConfig: { plugins: { ptype: 'treeviewdragdrop', enableDrag:true, enableDrop:true, //TODO - BRE - Wait a fix for drag&drop issue ddGroup:'explorerTree', pluginId : 'ddplugin', isValidDropPoint : function (node, position, dragZone, e, data) { if (!node || !data.item) { return false; } var view = this.view, targetNode = view.getRecord(node), draggedRecords = data.records, dataLength = draggedRecords.length, ln = draggedRecords.length, i, record; // No drop position, or dragged records: invalid drop point if (!(targetNode && position && dataLength)) { return false; } // If the targetNode is within the folder we are dragging for (i = 0; i < ln; i++) { record = draggedRecords[i]; if (record.isNode && record.contains(targetNode)) { return false; } } // Respect the allowDrop field on Tree nodes if (position === 'append' && targetNode.get('allowDrop') === false) { return false; } else if (position != 'append' && targetNode.parentNode.get('allowDrop') === false) { return false; } // If the target record is in the dragged dataset, then invalid drop if (Ext.Array.contains(draggedRecords, targetNode)) { return false; } // if (dataLength > 1) return false; var draggedRecord = draggedRecords[0]; // switch (targetNode.data.nodeType) { case 'localParam' : case 'remoteParam' : case 'remoteSimuParam' : case 'myData' : return false; default : if (draggedRecord.data.id == targetNode.data.nodeType+'-treeRootNode') return false; if ((position == 'before') && (targetNode.data.id == targetNode.data.nodeType+'-treeRootNode')) return false; return (draggedRecord.data.nodeType == targetNode.data.nodeType); } return false; }, onViewRender : function(view) { var me = this; view.on('itemupdate', function(record,index,node,opts) { var forceHide = false; var crtRec = record.parentNode; while (crtRec && !forceHide) { if (crtRec.get('filtered')) forceHide = crtRec.get('filtered'); crtRec = crtRec.parentNode; } tree.setNodesVisibility(record,forceHide); tree.applyDisableToNode(record); }); view.on('itemadd', function(records,index,node,opts) { Ext.each(records,function (rec) { tree.applyFilterToNode(rec); tree.applyDisableToNode(rec); }); }); view.on('afteritemexpand', function(record,index,node,opts) { var forceHide = false; var crtRec = record.parentNode; while (crtRec && !forceHide) { if (crtRec.get('filtered')) forceHide = crtRec.get('filtered'); crtRec = crtRec.parentNode; } tree.setNodesVisibility(record,forceHide); tree.applyDisableToNode(record); }); if (me.enableDrag) { me.dragZone = Ext.create('Ext.tree.ViewDragZone', { view: view, ddGroup: me.dragGroup || me.ddGroup, dragText: me.dragText, repairHighlightColor: me.nodeHighlightColor, repairHighlight: me.nodeHighlightOnRepair }); } if (me.enableDrop) { me.dropZone = Ext.create('Ext.tree.ViewDropZone', { view: view, ddGroup: me.dropGroup || me.ddGroup, allowContainerDrops: me.allowContainerDrops, appendOnly: me.appendOnly, allowParentInserts: me.allowParentInserts, expandDelay: me.expandDelay, dropHighlightColor: me.nodeHighlightColor, dropHighlight: me.nodeHighlightOnDrop, isValidDropPoint : me.isValidDropPoint }); } } }, listeners : { beforedrop : function(node, data, overModel, dropPosition) { var parentId; switch(dropPosition) { case 'append' : if (overModel.isLeaf()) parentId = overModel.parentNode.get('id'); else parentId = overModel.get('id'); if(!overModel.isExpanded() && overModel.isExpandable()) { myDesktopApp.warningMsg('Please open the folder before node adding'); return false; } break; case 'before' : case 'after' : parentId = overModel.parentNode.get('id'); break; } Ext.each(data.records, function(rec) { rec.renameDD(parentId,function(result) { if (result) { if (!result.id) { Ext.Msg.show({ title : 'Drop is impossible', msg : result.error, buttons : Ext.Msg.OK, icon : Ext.MessageBox.WARNING }); return false; } } else { Ext.Msg.show({ title : 'Drop is impossible', msg : 'Cannot connect to the server', buttons : Ext.Msg.OK, icon : Ext.MessageBox.WARNING }); return false; } return true; }); }); } } }, listeners: { itemmouseenter: function(view, record, item){ if(record.get('isParameter')){ var el = Ext.get(item), td = el.down('td > div'); td.setStyle('cursor', 'crosshair'); } else { var el = Ext.get(item), td = el.down('td > div'); td.setStyle('cursor', 'pointer'); } }, itemcontextmenu: function(view, rec, item, index, e){ // Add record to selection model view.ownerCt.getSelectionModel().select(rec); // block other events e.stopEvent(); // clear menu items menu.removeAll(); var menuItems; // if it's a single selection if (view.ownerCt.getSelectionModel().selected.length === 1) { // get items menu corresponding to right clicked record menuItems = rec.getContextMenuItems(this); } else if (view.ownerCt.getSelectionModel().selected.length > 1) { // get items menu corresponding to right clicked record menuItems = rec.getContextMenuMultiItems(this); } // if there's at least one item menu if (menuItems && menuItems.length){ // add the items menu.add(menuItems); // add listener on right clicked record var onRecordClick = function (menu, item, e, eOpts) { if (this.myGetOwnerTree().getSelectionModel().isSelected(this)) { //Dispatch click event to the record this.onMenuItemClick(menu,item,e); } //Remove old click listener menu.removeListener('click',onRecordClick,this); }; menu.addListener('click',onRecordClick,rec); // then show menu menu.showAt(e.getXY()); } }, itemdblclick: function(view, record, item, index, event){ event.stopEvent(); // first check if it is for SAVE-START-STOP plugin... if (Ext.PluginManager.getCount() > 0 && record.get('nodeType') == amdaModel.TimeTableNode.nodeType && record.isLeaf()) { var zmgr = myDesktopApp.desktop.getDesktopZIndexManager(); var winActive = zmgr.getActive(); var winId = winActive.getId(); if (winId == 'explorer-win') { zmgr.eachTopDown(function(win) { var id = win.getId(); if (id !== 'explorer-win') { winId = id; return false; } }); } } if (record.get('nodeType') == 'remoteParam' && !record.isLeaf() && !record.get('isParameter')) { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.interop.id, true, function (module) { module.createWindow(record.getBaseId()); }); } if (record.isLeaf() || record.data.isParameter) switch (record.get('nodeType')) { case 'myData' : case 'myDataParam' : case 'derivedParam' : case 'timeTable' : case 'sharedtimeTable' : case 'sharedcatalog' : case 'catalog' : case 'request' : case 'condition' : record.editLeaf(); break; case 'localParam' : case 'remoteParam': case 'remoteSimuParam': record.createAlias(record); break; case 'bkgWorks' : if (!record.get('object')) { AmdaAction.getObject(record.get('id'), record.get('nodeType'), record.getObjectCallback, record); } else { if (record.get('status') == 'done') { var isInteractive = false; var isNewTab = true; record.editNode(isNewTab, isInteractive); } else { myDesktopApp.infoMsg('Job Status: ' + record.get('status')); } } break; } }, beforeselect: function(view,node,index,options){ // if there's at least one node already selected if(view.selected.length //AND (the node which is beeing selected has a different nodeType than the first selected node OR the first selected node isn't a leaf && ( node.get('nodeType')!== view.selected.items[0].get('nodeType') || !view.selected.items[0].isLeaf()) // OR the node which is beeing selected has no nodeType OR it isn't a leaf OR || !node.get('nodeType') || !node.isLeaf() ){ // clear old selection view.deselectAll(); } }, afterrender: function(comp){ var view = comp.getView(); view.tip = Ext.create('Ext.tip.ToolTip', { // The overall target element. target: view.el, // Each grid row causes its own seperate show and hide. delegate: view.itemSelector, dismissDelay : 0, // showDelay: 100, // anchor: 'left', // Moving within the row should not hide the tip. trackMouse: true, autoRender: true, listeners: { // Change content dynamically depending on which element triggered the show. beforeshow: function updateTipBody(tip) { if (view.getRecord(tip.triggerElement)) { var info = view.getRecord(tip.triggerElement).get('info'); if (!info || info == '') { tip.addCls('hide'); } else { tip.removeCls('hide'); tip.update(info); } } } } }); }, // if remote base is empty - open interoperability module itemexpand: function(node) { if ( node.get('nodeType') == amdaModel.RemoteParamNode.nodeType && node.getDepth() == 3 && !node.hasChildNodes()) { node.addData(); } }, scope: this }, hideHeaders: true, // must define a column with a field to enable editor columns: [{ xtype : 'treetoolcolumn', text : 'Name', flex : 1, dataIndex: 'text', tool: 'info', // this references the "tools" object on the prototype toolHandler: function(view, cell, recordIndex, cellIndex, e) { var tooltype = e.target.getAttribute("tooltype"); var record = view.store.getAt(recordIndex); switch (tooltype) { case 'info' : myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function (module) { module.createWindow(record.get('help'), record.get('text')); }); break; } }, toolIsVisible : function(record) { switch (record.get('nodeType')) { case 'localParam' : case 'remoteParam' : case 'remoteSimuParam' : return record.get('help') != ''; } return false; }, field: { validFlag: true, listeners: { change : function( field, newValue, oldValue, eOpts ) { var explModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id); var explUI = explModule.getUiContent(); var activeTreePanel = explUI.getActiveTab(); var editedNode = activeTreePanel.getSelectionModel().selected.items[0]; if (editedNode) { editedNode.isValidName(newValue, function (res) { var validFlag = true; if(newValue === amdaModel.AmdaNode.NEW_DIR_NAME) { validFlag = 'Field is not modified' } else if (!res) { validFlag = 'Error during object validation'; } else if (!res.valid) { if (res.error) { validFlag = res.error; } else { validFlag = 'Invalid object name'; } } if (validFlag === true) { field.lastValid = newValue; } else { field.markInvalid(validFlag); } field.validFlag = validFlag; }); } } }, scope: this } }], //add our custom editor plugin plugins: [ new MyTreeEditor({ pluginId: 'treeEditor', listeners: { 'edit': function (editor, e) { if(e.column.field.validFlag !== true) { if(e.column.field.lastValid) { e.record.data[e.field] = e.column.field.lastValid; } else { e.record.remove(true); } } }, 'beforeedit': function (editor, e) { e.column.field.fireEvent('change', e.column.field, e.value, ''); } }, scope: this })], setNodesVisibility : function(node,forceHide) { var isFiltered = node.get('filtered'); for (var i = 0; i < node.childNodes.length; i++) this.setNodesVisibility(node.childNodes[i],forceHide || isFiltered); this.setNodeVisibility(node,!(forceHide || isFiltered)); }, setNodeVisibility : function(node,isVisible) { var record = store.getNodeById(node.internalId); var viewNode = Ext.fly(tree.getView().getNode(record)); if (viewNode) { viewNode.setVisibilityMode(Ext.Element.DISPLAY); if (isVisible) { viewNode.show(); this.applyDisableToNode(record); } else viewNode.hide(); } }, applyFilterToNode : function(node) { if (!node) return; var filter = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id).filter; switch (node.get('nodeType')) { case 'localParam' : if (!filter || !filter['param']) { //no filter applied node.set('filtered',false); return; } var pos = node.get('depth') - 3; //depth from local param root node if (pos < 0 || pos > 2) { node.set('filtered',false); return; } var isFiltered = true; for (var i = 0; i < filter['param'].length; i++) { s = filter['param'][i].split(';'); if (node.get('id') == s[pos]) { isFiltered = false; break; } } node.set('filtered',isFiltered); break; case 'remoteSimuParam' : if (!filter || !filter['simu'] ) { //no filter applied node.set('filtered',false); return; } var pos = node.get('depth') - 3; //depth from remote param root node if (pos < 0 || pos > 5) { node.set('filtered',false); return; } var isFiltered = true; for (var i = 0; i < filter['simu'].length; i++) { s = filter['simu'][i].split(';'); if (node.get('id') == s[pos]) { isFiltered = false; break; } } node.set('filtered',isFiltered); break; /*case 'alias' : if (!this.localParamFilter.result || this.localParamFilter.id == "" || !node.isLeaf()) { //no filter applied node.set('filtered',false); return; } var crtParam = node.get('id'); crtParam = crtParam.replace('alias_',''); crtParam = crtParam.replace(/_/g,':'); var isFiltered = true; for (var i = 0; i < this.localParamFilter.result.length; i++) { s = this.localParamFilter.result[i].split(';'); console.log(s[2]); if (crtParam == s[2]) { isFiltered = false; break; } } node.set('filtered',isFiltered); break;*/ default : return; } }, applyFilterToNodes : function(node) { node.eachChild(function (child){ tree.applyFilterToNodes(child); }); tree.applyFilterToNode(node); }, applyDisableToNode : function(node) { var crtNode = node; var disable = false; do { if (crtNode.get('disable')) { disable = true; break; } crtNode = crtNode.parentNode; } while (crtNode); var viewNode = Ext.fly(tree.getView().getNode(node)); if (disable) { node.set('disable',true); viewNode.setStyle('opacity',0.5); } } }); tree.addEvents('edition'); return tree; }, updateFilter : function() { var filter = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id).filter; var keys = []; for (var f in filter) { if (hasOwnProperty.call(filter, f)) keys.push(f); } var tree = this.query('#'+amdaUI.ExplorerUI.RESRC_TAB.TREE_ID)[0]; tree.getView().refresh(); for (var i = 0; i < keys.length; i++) { if (keys[i] == "_empty_") continue; switch (keys[i]) { case 'param' : //apply filter to local datasets var localNode = tree.getRootNode().findChild('id','myLocalData-treeRootNode',true); tree.applyFilterToNodes(localNode); tree.setNodesVisibility(localNode,false); tree.applyDisableToNode(localNode); //apply filter to aliases var aliasNode = tree.getRootNode().findChild('id','alias-treeRootNode',true); tree.applyFilterToNodes(aliasNode); tree.setNodesVisibility(aliasNode,false); tree.applyDisableToNode(aliasNode); break; case 'simu' : //apply filter to simulation datasets (in remote data) var remoteNode = tree.getRootNode().findChild('id','myRemoteSimuData-treeRootNode',true); tree.applyFilterToNodes(remoteNode); tree.setNodesVisibility(remoteNode,false); tree.applyDisableToNode(remoteNode); break; } } this.dockedItems.getAt(1).items.items[0].select(filter['name']); } }); // MyTreeEditor Ext.define( 'MyTreeEditor', { extend: 'Ext.grid.plugin.CellEditing', alias: 'editing.treeeditor', // initialization method of plugin init: function(cmp) { var me = this; me.hostCmp = cmp; // on parent event me.hostCmp.on({ // on edition event edition : { delay: 50, fn : function(view, record, item, index, e){ // view.getHeaderAtIndex(0).field.validFlag = 'Not modified'; // call the start edition method me.startEdit(record, view.getHeaderAtIndex(0)); }, scope: me } }); me.callParent(arguments); }, /** * Cancel any active editing. */ cancelEdit: function() { var me = this, activeEd = me.getActiveEditor(), viewEl = me.grid.getView().getEl(me.getActiveColumn()); me.setActiveEditor(null); me.setActiveColumn(null); me.setActiveRecord(null); if (activeEd) { activeEd.cancelEdit(); viewEl.focus(); this.fireEvent('canceledit', activeEd, me.context); } }, /** * overwrite the initEditTriggers to disable edition on click/dblclick * and to add custom */ initEditTriggers: function() { var me = this, view = me.view; me.on({ edit: function(editor,event){ // if there is a modification if (event.originalValue !== event.value) { // delegate rename action on model event.record.rename(event.value,function(result){ // if a result has been returned : success if(result) { // delegate commit action to delete modification flag event.record.commit(); var rec = event.record.data; // in case of directory if (!rec.leaf){ // set folder's ID returned by server rec.id = result.id; } } else { // in case of transaction error // reset originalValue event.record.value = event.originalValue; event.record.set('text', event.originalValue); event.record.commit(); } }); } } }); // enable Enter key and Esc Key view.on('render', function() { me.keyNav = Ext.create('Ext.util.KeyNav', view.el, { enter: me.onEnterKey, esc: me.onEscKey, scope: me }); }, me, { single: true }); }, //overwrite the getEditing context because we do not need the rowId getEditingContext: function(record, columnHeader) { var me = this, grid = me.grid, store = grid.store, colIdx, view = grid.getView(), value; // getting colIdx and real columnHeader if (Ext.isNumber(columnHeader)) { colIdx = columnHeader; columnHeader = grid.headerCt.getHeaderAtIndex(colIdx); } else { colIdx = columnHeader.getIndex(); } // getting current value value = record.get(columnHeader.dataIndex); // return editing context return { grid: grid, record: record, field: columnHeader.dataIndex, value: value, column: columnHeader, colIdx: colIdx, view: columnHeader.getOwnerHeaderCt().view }; } });