/** * 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 }; } });