UNPKG

webgme

Version:

Web-based Generic Modeling Environment

572 lines (469 loc) 19.7 kB
/*globals define, _, $, WebGMEGlobal, DEBUG, require*/ /*jshint browser: true*/ /** * @author rkereskenyi / https://github.com/rkereskenyi * @author nabana / https://github.com/nabana * @author pmeijer / https://github.com/pmeijer */ define(['js/logger', 'js/Loader/LoaderProgressBar', 'js/Constants', 'js/RegistryKeys', 'js/PanelBase/PanelBaseWithHeader', 'js/Panels/SplitPanel/SplitPanel', 'css!./styles/VisualizerPanel.css' ], function (Logger, LoaderProgressBar, CONSTANTS, REGISTRY_KEYS, PanelBaseWithHeader, SplitPanel) { 'use strict'; var VisualizerPanel, VisualizersJSON = WebGMEGlobal.allVisualizers; VisualizerPanel = function (layoutManager, params) { var options = {}; //set properties from options options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'Visualizer'; options[PanelBaseWithHeader.OPTIONS.HEADER_TITLE] = false; //call parent's constructor PanelBaseWithHeader.apply(this, [options]); this._client = params.client; this._layoutManager = layoutManager; /** * * @example * { * ModelEditor: { * id: "ModelEditor", * panel: ModelEditorPanel constructor * }, * MetaEditor: { * id: "MetaEditor", * panel: MetaEditorPanel constructor * }, * ... * } * @private */ this._visualizers = {}; /** * @example * "ModelEditor" * @private */ this.defaultVisualizerId = null; /** * @example * "MetaEditor" * @private */ this._activeVisualizerId = null; this._activeNodeID = null; /** * * @example * ["ModelEditor", "MetaEditor", ...] * @private */ this._validVisualizerIds = null; //initialize UI this._initialize(); this._loadVisualizers(); this.logger.debug('VisualizerPanel ctor finished'); }; //inherit from PanelBaseWithHeader _.extend(VisualizerPanel.prototype, PanelBaseWithHeader.prototype); VisualizerPanel.prototype._initialize = function () { var self = this, toolbar = WebGMEGlobal.Toolbar; //set Widget title this.setTitle('Visualizer'); this.$el.addClass('visualizer-panel'); //add toolbar controls toolbar.addSeparator(); this._toolbarBtn = toolbar.addDropDownButton({ title: 'Split Panels', icon: 'gme icon-gme_split-panels', menuClass: 'split-panel-dropdown-list', clickFn: function () { var maximized = self._splitPanel.isMaximized(); self._toolbarBtn.clear(); self._toolbarBtn.addButton({ text: maximized ? 'Exit maximize' : 'Maximize active panel', title: maximized ? 'Shows all split panels' : 'Maximizes the active panel', icon: 'split-panel-dropdown-icon glyphicon ' + (maximized ? 'glyphicon-resize-small' : 'glyphicon-resize-full'), disabled: self._splitPanel.getNumberOfPanels() === 1, clickFn: function () { self._splitPanel.maximize(!maximized, self._splitPanel._activePanelId); } }); self._toolbarBtn.addDivider(); self._toolbarBtn.addButton({ text: 'Split vertically', title: 'Splits the active panel vertically', icon: 'fa fa-columns split-panel-dropdown-icon', disabled: maximized, clickFn: function () { self._addNewPanel(true); } }); self._toolbarBtn.addButton({ text: 'Split horizontally', title: 'Splits the active panel horizontally', icon: 'fa fa-columns fa-rotate-270 split-panel-dropdown-icon', disabled: maximized, clickFn: function () { self._addNewPanel(); } }); self._toolbarBtn.addDivider(); self._toolbarBtn.addButton({ text: 'Remove active panel', title: 'Removes the active panel', icon: 'fa fa-minus-circle split-panel-dropdown-icon', disabled: maximized || self._splitPanel.getNumberOfPanels() === 1, clickFn: function () { self._deletePanel(); } }); self._toolbarBtn.addButton({ text: 'Exit split mode', title: 'Remove all but the active panel', icon: 'fa fa-times-circle split-panel-dropdown-icon', disabled: self._splitPanel.getNumberOfPanels() === 1, clickFn: function () { self._exitSplitMode(); } }); } }); this._panelVisContainer = $('<div/>'); this._panelVisContainer.append($('<div class="pp">Visualizer Selector</div>')); this._ul = $('<ul class="nav nav-pills nav-stacked">'); this._panelVisContainer.append(this._ul); this.$el.append(this._panelVisContainer); this.$el.on('click', 'ul > li:not(.active)', function (event) { var vizId = $(this).attr('data-id'); self._setActiveVisualizer(vizId); event.stopPropagation(); event.preventDefault(); }); WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_TO_BE_ACTIVE_OBJECT, function (model, activeObjectId, opts) { if (opts.invoker !== self) { self._onSelectedObjectChanged(activeObjectId, opts); } }); this._client.addEventListener(CONSTANTS.CLIENT.PROJECT_CLOSED, function (/* __project, nodeId */) { self._validVisualizerIds = null; self._exitSplitMode(true); }); this._client.addEventListener(CONSTANTS.CLIENT.PROJECT_OPENED, function (/* __project, nodeId */) { self._validVisualizerIds = null; self._exitSplitMode(true); }); this._client.addEventListener(CONSTANTS.CLIENT.BRANCH_CHANGED, function (/* __project, nodeId */) { self._validVisualizerIds = null; self._exitSplitMode(true); }); WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_VISUALIZER, function (model, activeVisualizer, opts) { var keepPrevPanelInstance = opts.invoker === self || opts.invoker === WebGMEGlobal.PanelManager; self._setActiveVisualizer(activeVisualizer, keepPrevPanelInstance); }); this._splitPanel = new SplitPanel(); this._layoutManager.addPanel('visualizerSplitPanel', this._splitPanel, 'center'); }; VisualizerPanel.prototype._onSelectedObjectChanged = function (currentNodeId, opts) { this._activeNodeID = currentNodeId; this._updateValidVisualizers(currentNodeId); this._updateListedVisualizers(!opts.suppressVisualizerFromNode); }; VisualizerPanel.prototype._setActiveVisualizer = function (visualizerId, keepPrevPanelInstance) { var PanelClass, activePanel; if (this._activeVisualizerId !== visualizerId && this._visualizers.hasOwnProperty(visualizerId)) { this._activeVisualizerId = visualizerId; this._ul.find('> li').removeClass('active'); this._ul.find('> li[data-id="' + visualizerId + '"]').addClass('active'); if (keepPrevPanelInstance) { return; } // Destroy the previous visualizer. activePanel = WebGMEGlobal.PanelManager.getActivePanel(); if (activePanel) { activePanel.destroy(); } if (this._visualizers[visualizerId]) { PanelClass = this._visualizers[visualizerId].panel; if (PanelClass) { activePanel = new PanelClass(this._layoutManager, {client: this._client}); activePanel[CONSTANTS.VISUALIZER_PANEL_IDENTIFIER] = this._visualizers[visualizerId].id; this._splitPanel.updateActivePanel(activePanel); // The new panel is activated and should be listening to state events of interest.. } if (typeof this._activeNodeID === 'string') { // ...so trigger an event about the active-object to get it started. // First set the state to null silently so even the same object will trigger an event. WebGMEGlobal.State.registerActiveObject(null, {silent: true}); WebGMEGlobal.State.registerActiveObject(this._activeNodeID, {invoker: this}); } } WebGMEGlobal.State.registerActiveVisualizer(visualizerId, {invoker: this}); } }; VisualizerPanel.prototype._updateValidVisualizers = function (currentNodeId) { var node, validVisuals; // Update the validVisualizers if (typeof currentNodeId === 'string') { node = this._client.getNode(currentNodeId); if (node) { validVisuals = node.getRegistry(REGISTRY_KEYS.VALID_VISUALIZERS); if (validVisuals) { this._validVisualizerIds = validVisuals.split(' '); return; } } else { this.logger.error('could not load node in _updateValidVisualizers', currentNodeId); } } else { this.logger.debug('nodePath not given'); } // Fall back on null -> all visualizers displayed this._validVisualizerIds = null; }; VisualizerPanel.prototype._updateListedVisualizers = function (setActiveViz) { var self = this, currentNode = self._client.getNode(self._activeNodeID), idToElem = {}, visualizerToSet, i, vizId, libraryRoot = false; function isAvailable(visualizerId) { var i; for (i = 0; i < VisualizersJSON.length; i += 1) { if (visualizerId === VisualizersJSON[i].id) { return true; } } return false; } if (currentNode) { libraryRoot = currentNode.isLibraryRoot(); } // For the active panel hide/show listed visualizers self._ul.children('li').each(function (index, _li) { var li = $(_li), id = li.attr('data-id'); if (self._validVisualizerIds === null) { // By default fall back on showing all loaded visualizers. if (libraryRoot && id === 'SetEditor') { li.hide(); } else { li.show(); } } else { if (self._validVisualizerIds.indexOf(id) > -1 && (!libraryRoot || (libraryRoot && id !== 'SetEditor'))) { li.show(); } else { li.hide(); } idToElem[id] = li.detach(); } }); if (self._validVisualizerIds !== null) { // Reinsert the detached li-elements based on the specified order. for (i = 0; i < self._validVisualizerIds.length; i += 1) { vizId = self._validVisualizerIds[i]; if (idToElem.hasOwnProperty(vizId)) { self._ul.append(idToElem[vizId]); idToElem[vizId] = null; } } // Finally append the hidden elements too. for (vizId in idToElem) { if (idToElem[vizId] !== null) { self._ul.append(idToElem[vizId]); } } } this.updateContainerSize(); if (self._validVisualizerIds) { visualizerToSet = self._validVisualizerIds[0]; if (!isAvailable(visualizerToSet)) { //fallback to the global default visualizerToSet = self.defaultVisualizerId.id; } } else { // Set this to the global default if it is valid for the project visualizerToSet = self.defaultVisualizerId.id; } if (!isAvailable(visualizerToSet)) { if (isAvailable(CONSTANTS.DEFAULT_VISUALIZER)) { //fall back to model editor if nothing else works visualizerToSet = CONSTANTS.DEFAULT_VISUALIZER; } else { visualizerToSet = null; } } // Only set the visualizer only if we were able to select some valid one. if (setActiveViz && visualizerToSet) { //setTimeout(function () { self._setActiveVisualizer(visualizerToSet); //}, 0); } }; VisualizerPanel.prototype._isAvailableVisualizer = function (visualizerId) { var i; for (i = 0; i < VisualizersJSON.length; i += 1) { if (visualizerId === VisualizersJSON[i].id) { return true; } } return false; }; VisualizerPanel.prototype._loadVisualizers = function () { var self = this, defaultFromConst; // Set the default visualizer for (var i = VisualizersJSON.length; i--;) { if (VisualizersJSON[i].default) { self.defaultVisualizerId = VisualizersJSON[i]; } // If no default given - use the one from constants. if (VisualizersJSON[i].id === CONSTANTS.DEFAULT_VISUALIZER) { defaultFromConst = VisualizersJSON[i]; } } self.defaultVisualizerId = self.defaultVisualizerId || defaultFromConst; this.addRange(VisualizersJSON, function () { self._setActiveVisualizer(self.defaultVisualizerId.id); }); }; VisualizerPanel.prototype._removeLoader = function (li, loaderDiv) { if (li.loader) { li.loader.stop(); li.loader.destroy(); delete li.loader; } loaderDiv.remove(); }; VisualizerPanel.prototype._addNewPanel = function (vertical) { //find the selected on var activeLi = this._ul.find('li.active'), visualizerId = activeLi.attr('data-id') || this.defaultVisualizerId; if (this._splitPanel.canSplitActivePanel(vertical)) { this._activeVisualizerId = null; this._splitPanel.addPanel(vertical); this._setActiveVisualizer(visualizerId); } else { this._client.notifyUser({ message: 'Active panel is too small to split ' + (vertical ? 'vertically' : 'horizontally') + '...', severity: 'warn' }); } }; VisualizerPanel.prototype._deletePanel = function () { var activePanel = WebGMEGlobal.PanelManager.getActivePanel(); if (this._splitPanel.getNumberOfPanels() > 1) { if (activePanel) { activePanel.destroy(); } this._splitPanel.deletePanel(); // Split panel will register new active panel. } else { this._client.notifyUser({ message: 'You cannot remove last visualizer panel...', severity: 'warn' }); } }; VisualizerPanel.prototype._exitSplitMode = function (force) { var activePanel; if (this._splitPanel.getNumberOfPanels() > 1) { this._splitPanel.deleteAllPanels(); activePanel = WebGMEGlobal.PanelManager.getActivePanel(); WebGMEGlobal.PanelManager.setActivePanel(null); WebGMEGlobal.PanelManager.setActivePanel(activePanel); //WebGMEGlobal.State.registerActiveObject(this._activeNodeID); } else if (!force) { this._client.notifyUser({ message: 'There are no split panels..', severity: 'info' }); } }; /**********************************************************************/ /*************** P U B L I C A P I ****************/ /**********************************************************************/ VisualizerPanel.prototype.add = function (menuDesc, callback) { var li = $('<li class="center pointer"><a class="btn-env" id=""></a></li>'), a = li.find('> a'), self = this, loaderDiv, doCallBack; doCallBack = function () { if (callback) { callback(); } }; if (menuDesc.DEBUG_ONLY === true && DEBUG !== true) { doCallBack(); return; } if (this._visualizers[menuDesc.id]) { this.logger.warn('A visualizer with the ID "' + menuDesc.id + '" already exists...'); doCallBack(); } else { li.attr('data-id', menuDesc.id); a.text(menuDesc.title); this._ul.append(li); if (menuDesc.panel) { loaderDiv = $('<div/>', {class: 'vis-loader'}); li.loader = new LoaderProgressBar({containerElement: loaderDiv}); li.loader.start(); a.append(loaderDiv); require([menuDesc.panel], function (panelClass) { self.logger.debug('downloaded: ' + menuDesc.panel); self._visualizers[menuDesc.id] = {panel: panelClass, id: menuDesc.id}; self._removeLoader(li, loaderDiv); doCallBack(); }, function (err) { var msg = 'Failed to download "' + err.requireModules[0] + '"'; //for any error store undefined in the list and the default decorator will be used on the canvas self.logger.error(msg); a.append(' <i class="glyphicon glyphicon-warning-sign" title="' + msg + '"></i>'); self._removeLoader(li, loaderDiv); doCallBack(); }); } else { a.append(' <i class="glyphicon glyphicon-warning-sign"></i>'); this.logger.warn('The visualizer with the ID "' + menuDesc.id + '" is missing "panel" or "control"'); doCallBack(); } } }; VisualizerPanel.prototype.addRange = function (menuDescList, callback) { var queueLen = 0, len = menuDescList.length, i, callbackWrap; if (callback) { callbackWrap = function () { queueLen -= 1; if (queueLen === 0) { callback(); } }; } for (i = 0; i < len; i += 1) { queueLen += 1; this.add(menuDescList[i], callbackWrap); } }; return VisualizerPanel; });