/** * 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 */ /* global AmdaAction, amdaModel, myDesktopApp, amdaUI, MyTreeEditor */ 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 imgHtml, toolCol = view.getHeaderAtIndex(colIdx); if (!toolCol.toolIsVisible(record)) { return value; } imgHtml = Ext.DomHelper.markup({ id: 'tool-' + rowIdx + '-' + colIdx, tag: 'img', tooltype: toolCol.tool, src: toolCol.tools[toolCol.tool], style: 'cursor:hand;' }); return value + ' ' + imgHtml; }, processEvent: function (type, view, cell, recordIndex, cellIndex, e) { var toolType; if (type === 'click' && e.target.tagName === 'IMG') { 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"); * } * * @returns {boolean} False */ toolIsVisible: function () { 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), myConf; 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) { if (e.status) { if (result && 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 () { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.filters.id, true, function (module) { module.createWindow(); }); } }, { text: '', iconCls: 'icon-remover', tooltip: { text: 'Reset Filter', align: 'bl-tl' }, handler: function () { 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) { var menu, store, tree, treeId; 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; } 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')}; } } }); menu = new Ext.menu.Menu(); 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) { var forceHide = false, 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) { Ext.each(records, function (rec) { tree.applyFilterToNode(rec); tree.applyDisableToNode(rec); }); }); view.on('afteritemexpand', function (record) { var crtRec, forceHide; forceHide = false; 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; default: 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) { Ext.get(item).down('td > div').setStyle('cursor', record.get('isParameter') ? 'pointer' : 'crosshair'); }, itemcontextmenu: function (view, rec, item, index, e) { var menuItems; // Add record to selection model view.ownerCt.getSelectionModel().select(rec); // Block other events e.stopEvent(); // Clear menu items menu.removeAll(); // 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) { 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) { var zmgr, winId; 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()) { zmgr = myDesktopApp.desktop.getDesktopZIndexManager(); winId = zmgr.getActive().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') { // IsInteractive = true, isNewTab = false record.editNode(true, false); } else { myDesktopApp.infoMsg('Job Status: ' + record.get('status')); } break; default: break; } } }, beforeselect: function (view, node) { // 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) { var info; if (view.getRecord(tip.triggerElement)) { 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 }, // Must define a column with a field to enable editor columns: [ { xtype: 'treetoolcolumn', text: 'Name', flex: 1, dataIndex: 'text', // This references the "tools" object on the prototype tool: 'info', toolHandler: function (view, cell, recordIndex, cellIndex, e) { var record = view.store.getAt(recordIndex); switch (e.target.getAttribute('tooltype')) { case 'info': myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.info.id, true, function (module) { module.createWindow(record.get('help'), record.get('text')); }); break; default: break; } }, toolIsVisible: function (record) { switch (record.get('nodeType')) { case 'localParam': case 'remoteParam': case 'remoteSimuParam': return record.get('help') != ''; default: break; } return false; }, field: { validFlag: true, listeners: { change: function (field, newValue) { var editedNode, explModule; explModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id); editedNode = explModule.getUiContent().getActiveTab().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.markInvalid(validFlag); } field.validFlag = validFlag; }); } } }, scope: this } } ], // Add our custom editor plugin plugins: [ new MyTreeEditor({ pluginId: 'treeEditor', listeners: { beforeedit: function (editor, e) { e.column.field.fireEvent('change', e.column.field, e.value, ''); }, validateedit: function (editor, e) { if (e.column.field.validFlag !== true) { if (e.record.deleteOnFailure) { e.record.remove(true); } else { return false; } } return true; } }, 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) { var filter, isFiltered, pos; if (!node) { return; } 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; } // Depth from local param root node pos = node.get('depth') - 3; if (pos < 0 || pos > 2) { node.set('filtered', false); return; } isFiltered = true; for (var i = 0; i < filter.param.length; i++) { if (node.get('id') == filter.param[i].split(';')[pos]) { isFiltered = false; break; } } node.set('filtered', isFiltered); break; case 'remoteSimuParam': if (!filter || !filter.simu) { // No filter applied node.set('filtered', false); return; } // Depth from remote param root node pos = node.get('depth') - 3; if (pos < 0 || pos > 5) { node.set('filtered', false); return; } isFiltered = true; for (var i = 0; i < filter.simu.length; i++) { if (node.get('id') == filter.simu[i].split(';')[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: } }, applyFilterToNodes: function (node) { node.eachChild(function (child) { tree.applyFilterToNodes(child); }); tree.applyFilterToNode(node); }, applyDisableToNode: function (node) { var crtNode, disable, viewNode; crtNode = node; disable = false; do { if (crtNode.get('disable')) { disable = true; break; } crtNode = crtNode.parentNode; } while (crtNode); 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 aliasNode, filter, keys, localNode, remoteNode, tree; filter = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.explorer.id).filter; keys = []; for (var f in filter) { if (hasOwnProperty.call(filter, f)) { keys.push(f); } } 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 localNode = tree.getRootNode().findChild('id', 'myLocalData-treeRootNode', true); tree.applyFilterToNodes(localNode); tree.setNodesVisibility(localNode, false); tree.applyDisableToNode(localNode); // Apply filter to aliases 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) remoteNode = tree.getRootNode().findChild('id', 'myRemoteSimuData-treeRootNode', true); tree.applyFilterToNodes(remoteNode); tree.setNodesVisibility(remoteNode, false); tree.applyDisableToNode(remoteNode); break; default: 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. * @returns {void} */ 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 * @returns {void} */ 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, colIdx, 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 }; } });