UNPKG

webgme

Version:

Web-based Generic Modeling Environment

1,277 lines (1,055 loc) 49.9 kB
/*globals define, _, WebGMEGlobal, $ */ /*jshint browser: true */ /** * @author rkereskenyi / https://github.com/rkereskenyi */ define(['js/logger', 'js/Constants', 'js/NodePropertyNames', 'js/RegistryKeys', 'js/Widgets/DiagramDesigner/DiagramDesignerWidget.Constants', './ModelEditorControl.DiagramDesignerWidgetEventHandlers', 'js/Utils/GMEConcepts', 'js/Utils/GMEVisualConcepts', 'js/Utils/PreferencesHelper', 'js/Controls/AlignMenu', 'js/Utils/ComponentSettings' ], function (Logger, CONSTANTS, nodePropertyNames, REGISTRY_KEYS, DiagramDesignerWidgetConstants, ModelEditorControlDiagramDesignerWidgetEventHandlers, GMEConcepts, GMEVisualConcepts, PreferencesHelper, AlignMenu, ComponentSettings) { 'use strict'; var ModelEditorControl, BACKGROUND_TEXT_COLOR = '#DEDEDE', BACKGROUND_TEXT_SIZE = 30, DEFAULT_DECORATOR = 'ModelDecorator', WIDGET_NAME = 'DiagramDesigner', SRC_POINTER_NAME = CONSTANTS.POINTER_SOURCE, DST_POINTER_NAME = CONSTANTS.POINTER_TARGET; ModelEditorControl = function (options, config) { this.logger = options.logger || Logger.create(options.loggerName || 'gme:Panels:ModelEditor:' + 'ModelEditorControl', WebGMEGlobal.gmeConfig.client.log); this._client = options.client; this._config = ModelEditorControl.getDefaultConfig(); ComponentSettings.resolveWithWebGMEGlobal(this._config, ModelEditorControl.getComponentId()); this._firstLoad = false; this._topNode = CONSTANTS.PROJECT_ROOT_ID; //initialize core collections and variables this.designerCanvas = options.widget; this._alignMenu = new AlignMenu(this.designerCanvas.CONSTANTS, {}); if (this._client === undefined) { this.logger.error('ModelEditorControl\'s client is not specified...'); throw ('ModelEditorControl can not be created'); } if (this.designerCanvas === undefined) { this.logger.error('ModelEditorControl\'s DesignerCanvas is not specified...'); throw ('ModelEditorControl can not be created'); } this._selfPatterns = {}; // Alias just in case someone is extending this visualizers this._GMEID2ComponentID = this._GmeID2ComponentID = {}; this._ComponentID2GMEID = this._ComponentID2GmeID = {}; this.eventQueue = []; this._componentIDPartIDMap = {}; //TODO: experiemtnal only, remove!!! this.___SLOW_CONN = false; //local variable holding info about the currently opened node this.currentNodeInfo = {id: null, children: [], parentId: null}; /****************** END OF - ADD BUTTONS AND THEIR EVENT HANDLERS TO DESIGNER CANVAS ******************/ //attach all the event handlers for event's coming from DesignerCanvas this.attachDiagramDesignerWidgetEventHandlers(); this._updateTopNode(); this.logger.debug('ModelEditorControl ctor finished'); }; ModelEditorControl.prototype.selectedObjectChanged = function (nodeId) { var desc, nodeName, node, self = this; this.logger.debug('activeObject "' + nodeId + '"'); //delete everything from model editor this.designerCanvas.clear(); //clean up local hash map this._GMEModels = []; this._GMEConnections = []; // Alias just in case someone is extending this visualizers this._GMEID2ComponentID = this._GmeID2ComponentID = {}; this._ComponentID2GMEID = this._ComponentID2GmeID = {}; this._GMEID2Subcomponent = {}; this._Subcomponent2GMEID = {}; //remove current territory patterns if (this._territoryId) { this._client.removeUI(this._territoryId); } this.currentNodeInfo.id = nodeId; this.currentNodeInfo.parentId = undefined; this._delayedConnections = []; this._selectedAspect = WebGMEGlobal.State.getActiveAspect(); //since PROJECT_ROOT_ID is an empty string, it is considered false.. if (nodeId || nodeId === CONSTANTS.PROJECT_ROOT_ID) { desc = this._getObjectDescriptor(nodeId); nodeName = (desc && desc.name || ' '); if (desc) { this.currentNodeInfo.parentId = desc.parentId; } node = this._client.getNode(nodeId); if (this._selectedAspect !== CONSTANTS.ASPECT_ALL) { //make sure that the selectedAspect exist in the node, otherwise fallback to All var aspectNames = node ? node.getValidAspectNames() : []; if (aspectNames.indexOf(this._selectedAspect) === -1) { this.logger.warn('The currently selected aspect "' + this._selectedAspect + '" does not exist in the object "' + nodeName + ' (' + nodeId + ')", falling back to "All"'); this._selectedAspect = CONSTANTS.ASPECT_ALL; WebGMEGlobal.State.registerActiveAspect(CONSTANTS.ASPECT_ALL); } } //put new node's info into territory rules this._selfPatterns = {}; if (this._selectedAspect === CONSTANTS.ASPECT_ALL) { this._selfPatterns[nodeId] = {children: 2}; } else { this._selfPatterns[nodeId] = this._client.getAspectTerritoryPattern(nodeId, this._selectedAspect); this._selfPatterns[nodeId].children = 2; } this._firstLoad = true; this.designerCanvas.setTitle(nodeName); this.designerCanvas.setBackgroundText(nodeName, { 'font-size': BACKGROUND_TEXT_SIZE, color: BACKGROUND_TEXT_COLOR }); if (node && !this._client.isProjectReadOnly() && !this._client.isCommitReadOnly()) { this.designerCanvas.setReadOnly(node.isLibraryRoot() || node.isLibraryElement()); this.setReadOnly(node.isLibraryRoot() || node.isLibraryElement()); } this.designerCanvas.showProgressbar(); this._territoryId = this._client.addUI(this, function (events) { self._eventCallback(events); }); //update the territory this._client.updateTerritory(this._territoryId, this._selfPatterns); } else { this.designerCanvas.setBackgroundText('No object to display', { color: BACKGROUND_TEXT_COLOR, 'font-size': BACKGROUND_TEXT_SIZE }); } }; ModelEditorControl.prototype._getObjectDescriptor = function (nodeId) { var self = this, nodeObj = this._client.getNode(nodeId), objDescriptor, customPoints; function extendWithBoxProperties() { var defaultPos = 0, pos, memberListContainerObj; //aspect specific coordinate if (self._selectedAspect === CONSTANTS.ASPECT_ALL) { pos = nodeObj.getRegistry(REGISTRY_KEYS.POSITION); } else { memberListContainerObj = self._client.getNode(self.currentNodeInfo.id); try { pos = memberListContainerObj.getMemberRegistry(self._selectedAspect, nodeId, REGISTRY_KEYS.POSITION); } catch (err) { if (err.name !== 'CoreIllegalOperationError') { throw err; } } pos = pos || nodeObj.getRegistry(REGISTRY_KEYS.POSITION); } if (pos) { objDescriptor.position = {x: pos.x, y: pos.y}; } else { objDescriptor.position = {x: defaultPos, y: defaultPos}; } if (objDescriptor.position.hasOwnProperty('x')) { objDescriptor.position.x = self._getDefaultValueForNumber(objDescriptor.position.x, defaultPos); } else { objDescriptor.position.x = defaultPos; } if (objDescriptor.position.hasOwnProperty('y')) { objDescriptor.position.y = self._getDefaultValueForNumber(objDescriptor.position.y, defaultPos); } else { objDescriptor.position.y = defaultPos; } objDescriptor.decorator = nodeObj.getRegistry(REGISTRY_KEYS.DECORATOR) || ''; objDescriptor.rotation = parseInt(nodeObj.getRegistry(REGISTRY_KEYS.ROTATION), 10) || 0; } if (nodeObj) { objDescriptor = {}; objDescriptor.id = nodeObj.getId(); objDescriptor.name = nodeObj.getFullyQualifiedName(); objDescriptor.parentId = nodeObj.getParentId(); if (nodeObj.isLibraryRoot()) { //this means that a library root will not be visualized on our modelEditor return objDescriptor; } if (nodeId !== this.currentNodeInfo.id) { //fill the descriptor based on its type if (GMEConcepts.isConnection(nodeId)) { objDescriptor.connectionChanged = this._GMEModels.indexOf(nodeId) > -1 && this._GMEConnections.indexOf(nodeId) === -1; objDescriptor.kind = 'CONNECTION'; objDescriptor.source = nodeObj.getPointer(SRC_POINTER_NAME).to; objDescriptor.target = nodeObj.getPointer(DST_POINTER_NAME).to; //get all the other visual properties of the connection _.extend(objDescriptor, GMEVisualConcepts.getConnectionVisualProperties(nodeId)); if (objDescriptor[REGISTRY_KEYS.BOX_DECORATION]) { extendWithBoxProperties(); } // If srcText or dstText is given -> remove the name. if (objDescriptor.srcText || objDescriptor.dstText) { delete objDescriptor.name; } //get custom points from the node object customPoints = nodeObj.getRegistry(REGISTRY_KEYS.LINE_CUSTOM_POINTS); if (customPoints && _.isArray(customPoints)) { //JSON.parse(JSON.stringify(customPoints)); objDescriptor[CONSTANTS.LINE_STYLE.CUSTOM_POINTS] = $.extend(true, [], customPoints); } } else { objDescriptor.kind = 'MODEL'; objDescriptor.connectionChanged = this._GMEModels.indexOf(nodeId) === -1 && this._GMEConnections.indexOf(nodeId) > -1; extendWithBoxProperties(); } } } return objDescriptor; }; ModelEditorControl.prototype._getDefaultValueForNumber = function (cValue, defaultValue) { if (_.isNumber(cValue)) { if (_.isNaN(cValue)) { return defaultValue; } } else { return defaultValue; } //cValue is a number, simply return it return cValue; }; // PUBLIC METHODS ModelEditorControl.prototype._eventCallback = function (events) { var i = events ? events.length : 0, refresh; this.logger.debug('_eventCallback "' + i + '" items'); if (i > 0) { this.eventQueue.push(events); refresh = this.processNextInQueue(); } this.logger.debug('_eventCallback "' + events.length + '" items - DONE'); if (refresh === true) { this.selectedObjectChanged(this.currentNodeInfo.id); } }; ModelEditorControl.prototype.processNextInQueue = function () { var nextBatchInQueue, len = this.eventQueue.length, decoratorsToDownload = [DEFAULT_DECORATOR], itemDecorator, refresh = false, self = this; if (len > 0) { nextBatchInQueue = this.eventQueue.pop(); len = nextBatchInQueue.length; while (len--) { if ((nextBatchInQueue[len].etype === CONSTANTS.TERRITORY_EVENT_LOAD) || (nextBatchInQueue[len].etype === CONSTANTS.TERRITORY_EVENT_UPDATE)) { nextBatchInQueue[len].desc = this._getObjectDescriptor(nextBatchInQueue[len].eid); refresh = refresh || nextBatchInQueue[len].desc.connectionChanged === true; itemDecorator = nextBatchInQueue[len].desc.decorator; if (itemDecorator && itemDecorator !== '') { if (decoratorsToDownload.indexOf(itemDecorator) === -1) { decoratorsToDownload.pushUnique(itemDecorator); } } } } this._client.decoratorManager.download(decoratorsToDownload, WIDGET_NAME, function () { self._dispatchEvents(nextBatchInQueue); }); } return refresh; }; ModelEditorControl.prototype._dispatchEvents = function (events) { var i = events.length, e, territoryChanged = false, self = this, orderedItemEvents, orderedConnectionEvents, unloadEvents, srcGMEID, dstGMEID, srcConnIdx, dstConnIdx, j, ce, insertIdxAfter, insertIdxBefore, MAX_VAL = 999999999, depSrcConnIdx, depDstConnIdx; this.logger.debug('_dispatchEvents ' + events[0].etype); events.shift(); this.logger.debug('_dispatchEvents "' + i + '" items'); /********** ORDER EVENTS BASED ON DEPENDENCY ************/ /** 1: items first, no dependency **/ /** 2: connections second, dependency if a connection is connected to an other connection **/ orderedItemEvents = []; orderedConnectionEvents = []; if (this._delayedConnections && this._delayedConnections.length > 0) { /*this.logger.warn('_delayedConnections: ' + this._delayedConnections.length );*/ for (i = 0; i < this._delayedConnections.length; i += 1) { orderedConnectionEvents.push({ etype: CONSTANTS.TERRITORY_EVENT_LOAD, eid: this._delayedConnections[i], desc: this._getObjectDescriptor(this._delayedConnections[i]) }); } } this._delayedConnections = []; unloadEvents = []; i = events.length; while (i--) { e = events[i]; if (e.etype === CONSTANTS.TERRITORY_EVENT_UNLOAD) { unloadEvents.push(e); } else if (e.desc.kind === 'MODEL') { orderedItemEvents.push(e); } else if (e.desc.kind === 'CONNECTION') { if (e.desc.parentId === this.currentNodeInfo.id) { //check to see if SRC and DST is another connection //if so, put this guy AFTER them srcGMEID = e.desc.source; dstGMEID = e.desc.target; srcConnIdx = -1; dstConnIdx = -1; j = orderedConnectionEvents.length; while (j--) { ce = orderedConnectionEvents[j]; if (ce.id === srcGMEID) { srcConnIdx = j; } else if (ce.id === dstGMEID) { dstConnIdx = j; } if (srcConnIdx !== -1 && dstConnIdx !== -1) { break; } } insertIdxAfter = Math.max(srcConnIdx, dstConnIdx); //check to see if this guy is a DEPENDENT of any already processed CONNECTION //insert BEFORE THEM depSrcConnIdx = MAX_VAL; depDstConnIdx = MAX_VAL; j = orderedConnectionEvents.length; while (j--) { ce = orderedConnectionEvents[j]; if (e.desc.id === ce.desc.source) { depSrcConnIdx = j; } else if (e.desc.id === ce.desc.target) { depDstConnIdx = j; } if (depSrcConnIdx !== MAX_VAL && depDstConnIdx !== MAX_VAL) { break; } } insertIdxBefore = Math.min(depSrcConnIdx, depDstConnIdx); if (insertIdxAfter === -1 && insertIdxBefore === MAX_VAL) { orderedConnectionEvents.push(e); } else { if (insertIdxAfter !== -1 && insertIdxBefore === MAX_VAL) { orderedConnectionEvents.splice(insertIdxAfter + 1, 0, e); } else if (insertIdxAfter === -1 && insertIdxBefore !== MAX_VAL) { orderedConnectionEvents.splice(insertIdxBefore, 0, e); } else if (insertIdxAfter !== -1 && insertIdxBefore !== MAX_VAL) { orderedConnectionEvents.splice(insertIdxBefore, 0, e); } } } else { orderedItemEvents.push(e); } } else if (this.currentNodeInfo.id === e.eid) { orderedItemEvents.push(e); } } events = unloadEvents.concat(orderedItemEvents); this._notifyPackage = {}; this.designerCanvas.beginUpdate(); //items for (i = 0; i < events.length; i += 1) { e = events[i]; switch (e.etype) { case CONSTANTS.TERRITORY_EVENT_LOAD: territoryChanged = this._onLoad(e.eid, e.desc) || territoryChanged; break; case CONSTANTS.TERRITORY_EVENT_UPDATE: territoryChanged = this._onUpdate(e.eid, e.desc) || territoryChanged; break; case CONSTANTS.TERRITORY_EVENT_UNLOAD: territoryChanged = this._onUnload(e.eid) || territoryChanged; break; } } this._handleDecoratorNotification(); //connections events = orderedConnectionEvents; //items for (i = 0; i < events.length; i += 1) { e = events[i]; switch (e.etype) { case CONSTANTS.TERRITORY_EVENT_LOAD: this._onLoad(e.eid, e.desc); break; case CONSTANTS.TERRITORY_EVENT_UPDATE: this._onUpdate(e.eid, e.desc); break; case CONSTANTS.TERRITORY_EVENT_UNLOAD: this._onUnload(e.eid); break; } } this.designerCanvas.endUpdate(); this.designerCanvas.hideProgressbar(); //update the territory if (territoryChanged) { //TODO: review this async here if (this.___SLOW_CONN === true) { setTimeout(function () { self.logger.debug('Updating territory with ruleset from decorators: ' + JSON.stringify(self._selfPatterns)); self._client.updateTerritory(self._territoryId, self._selfPatterns); }, 2000); } else { this.logger.debug('Updating territory with ruleset from decorators: ' + JSON.stringify(this._selfPatterns)); this._client.updateTerritory(this._territoryId, this._selfPatterns); } } //check if firstload if (this._firstLoad === true) { this._firstLoad = false; //check if there is active selection set in client var activeSelection = WebGMEGlobal.State.getActiveSelection(); if (activeSelection && activeSelection.length > 0) { i = activeSelection.length; var gmeID; var ddSelection = []; while (i--) { //try to find each object present in the active selection mapped to DiagramDesigner element gmeID = activeSelection[i]; if (this._GMEID2ComponentID[gmeID]) { ddSelection = ddSelection.concat(this._GMEID2ComponentID[gmeID]); } } this.designerCanvas.select(ddSelection); } } this.logger.debug('_dispatchEvents "' + events.length + '" items - DONE'); //continue processing event queue this.processNextInQueue(); }; ModelEditorControl.prototype._getItemDecorator = function (decorator) { var result; result = this._client.decoratorManager.getDecoratorForWidget(decorator, WIDGET_NAME); if (!result) { result = this._client.decoratorManager.getDecoratorForWidget(DEFAULT_DECORATOR, WIDGET_NAME); } return result; }; ModelEditorControl.prototype._addGMEConnection = function (gmeID, objDesc) { var uiComponent = this.designerCanvas.createConnection(objDesc); this.logger.debug('Connection: ' + uiComponent.id + ' for GME object: ' + objDesc.id); this._GMEID2ComponentID[gmeID].push(uiComponent.id); this._ComponentID2GMEID[uiComponent.id] = gmeID; }; ModelEditorControl.prototype._addGMEModel = function (gmeID, objDesc) { var self = this, territoryChanged = false, decClass, uiComponent; function getDecoratorTerritoryQueries(decorator) { var query, entry; if (decorator) { query = decorator.getTerritoryQuery(); if (query) { for (entry in query) { if (query.hasOwnProperty(entry)) { self._selfPatterns[entry] = query[entry]; territoryChanged = true; } } } } } this._GMEModels.push(gmeID); decClass = this._getItemDecorator(objDesc.decorator); objDesc.decoratorClass = decClass; objDesc.control = this; objDesc.metaInfo = {}; objDesc.metaInfo[CONSTANTS.GME_ID] = gmeID; objDesc.preferencesHelper = PreferencesHelper.getPreferences(); objDesc.aspect = this._selectedAspect; uiComponent = this.designerCanvas.createDesignerItem(objDesc); this._GMEID2ComponentID[gmeID].push(uiComponent.id); this._ComponentID2GMEID[uiComponent.id] = gmeID; getDecoratorTerritoryQueries(uiComponent._decoratorInstance); return territoryChanged; }; // PUBLIC METHODS ModelEditorControl.prototype._onLoad = function (gmeID, objD) { var objDesc, sources = [], destinations = [], territoryChanged = false, srcDst, sn, dn; //component loaded //we are interested in the load of sub_components of the opened component if (this.currentNodeInfo.id !== gmeID) { if (objD) { if (objD.parentId === this.currentNodeInfo.id) { objDesc = _.extend({}, objD); this._GMEID2ComponentID[gmeID] = []; if (objDesc.kind === 'MODEL' || objDesc[REGISTRY_KEYS.BOX_DECORATION]) { territoryChanged = this._addGMEModel(gmeID, objDesc); } if (objDesc.kind === 'CONNECTION') { this._GMEConnections.push(gmeID); srcDst = this._getAllSourceDestinationPairsForConnection(objDesc.source, objDesc.target); sources = srcDst.sources; destinations = srcDst.destinations; if (sources.length > 0 && destinations.length > 0) { for (sn = 0; sn < sources.length; sn += 1) { for (dn = 0; dn < destinations.length; dn += 1) { objDesc.srcObjId = sources[sn].objId; objDesc.srcSubCompId = sources[sn].subCompId; objDesc.dstObjId = destinations[dn].objId; objDesc.dstSubCompId = destinations[dn].subCompId; objDesc.reconnectable = true; objDesc.editable = true; delete objDesc.source; delete objDesc.target; _.extend(objDesc, this.getConnectionDescriptor(gmeID)); this._addGMEConnection(gmeID, objDesc); } } } else { //the connection is here, but no valid endpoint on canvas //save the connection this._delayedConnections.push(gmeID); } } } else { //supposed to be the grandchild of the currently open node //--> load of port /*if(this._GMEModels.indexOf(objD.parentId) !== -1){ this._onUpdate(objD.parentId,this._getObjectDescriptor(objD.parentId)); }*/ this._checkComponentDependency(gmeID, CONSTANTS.TERRITORY_EVENT_LOAD); } } } else { //currently opened node this._updateSheetName(objD.name); this._updateAspects(); } return territoryChanged; }; ModelEditorControl.prototype._onUpdate = function (gmeID, objDesc) { var territoryChanged = false, updatedBox, componentID, len, decClass, srcDst, sources, destinations, sn, dn; //self or child updated //check if the updated object is the opened node if (gmeID === this.currentNodeInfo.id) { //the updated object is the parent whose children are displayed here //the interest about the parent is: // - name change this._updateSheetName(objDesc.name); this._updateAspects(); } else { if (objDesc) { if (objDesc.parentId === this.currentNodeInfo.id) { if (objDesc.kind === 'MODEL' || objDesc[REGISTRY_KEYS.BOX_DECORATION]) { if (this._GMEID2ComponentID[gmeID]) { len = this._GMEID2ComponentID[gmeID].length; updatedBox = false; while (len--) { componentID = this._GMEID2ComponentID[gmeID][len]; if (this.designerCanvas.connectionIds.indexOf(componentID) === -1) { updatedBox = true; decClass = this._getItemDecorator(objDesc.decorator); objDesc.decoratorClass = decClass; objDesc.preferencesHelper = PreferencesHelper.getPreferences(); objDesc.aspect = this._selectedAspect; this.designerCanvas.updateDesignerItem(componentID, objDesc); } } if (objDesc[REGISTRY_KEYS.BOX_DECORATION] && updatedBox === false) { // There wasn't any box associated with the connection. territoryChanged = this._addGMEModel(gmeID, objDesc); } } } else if (objDesc.kind === 'CONNECTION' && objDesc[REGISTRY_KEYS.BOX_DECORATION] && this._GMEID2ComponentID[gmeID].length > 1) { } //there is a connection associated with this GMEID if (this._GMEConnections.indexOf(gmeID) !== -1) { len = this._GMEID2ComponentID[gmeID].length; srcDst = this._getAllSourceDestinationPairsForConnection(objDesc.source, objDesc.target); sources = srcDst.sources; destinations = srcDst.destinations; len -= 1; for (sn = 0; sn < sources.length; sn += 1) { for (dn = 0; dn < destinations.length; dn += 1) { objDesc.srcObjId = sources[sn].objId; objDesc.srcSubCompId = sources[sn].subCompId; objDesc.dstObjId = destinations[dn].objId; objDesc.dstSubCompId = destinations[dn].subCompId; objDesc.reconnectable = true; objDesc.editable = true; delete objDesc.source; delete objDesc.target; if (len >= 0) { componentID = this._GMEID2ComponentID[gmeID][len]; _.extend(objDesc, this.getConnectionDescriptor(gmeID)); if (this.designerCanvas.connectionIds.indexOf(componentID) > -1) { this.designerCanvas.updateConnection(componentID, objDesc); } else { this._addGMEConnection(gmeID, objDesc); } len -= 1; } else { this.logger.error('Updating connections...Existing connections are less than the ' + 'needed src-dst combo...'); //let's create a connection _.extend(objDesc, this.getConnectionDescriptor(gmeID)); this._addGMEConnection(gmeID, objDesc); } } } if (len >= 0 && !objDesc[REGISTRY_KEYS.BOX_DECORATION]) { //some leftover connection boxes on the widget len += 1; while (len--) { componentID = this._GMEID2ComponentID[gmeID][len]; if (this.designerCanvas.connectionIds.indexOf(componentID) > -1) { this.designerCanvas.deleteComponent(componentID); this._GMEID2ComponentID[gmeID].splice(len, 1); delete this._ComponentID2GMEID[componentID]; } } } } } else { //update about a subcomponent - will be handled in the decorator this._checkComponentDependency(gmeID, CONSTANTS.TERRITORY_EVENT_UPDATE); } } } return territoryChanged; }; ModelEditorControl.prototype._updateSheetName = function (name) { this.designerCanvas.setTitle(name); this.designerCanvas.setBackgroundText(name, { 'font-size': BACKGROUND_TEXT_SIZE, color: BACKGROUND_TEXT_COLOR }); }; ModelEditorControl.prototype._onUnload = function (gmeID) { var componentID, len, getDecoratorTerritoryQueries, self = this, territoryChanged = false; getDecoratorTerritoryQueries = function (decorator) { var query, entry; if (decorator) { query = decorator.getTerritoryQuery(); if (query) { for (entry in query) { if (query.hasOwnProperty(entry)) { delete self._selfPatterns[entry]; territoryChanged = true; } } } } }; if (gmeID === this.currentNodeInfo.id) { //the opened model has been removed from territoy --> most likely deleted... this.logger.debug('The previously opened model does not exist... --- GMEID: "' + this.currentNodeInfo.id + '"'); this.designerCanvas.setBackgroundText('The previously opened model does not exist...', { 'font-size': BACKGROUND_TEXT_SIZE, color: BACKGROUND_TEXT_COLOR }); } else { if (this._GMEID2ComponentID.hasOwnProperty(gmeID)) { len = this._GMEID2ComponentID[gmeID].length; while (len--) { componentID = this._GMEID2ComponentID[gmeID][len]; if (this.designerCanvas.itemIds.indexOf(componentID) !== -1) { getDecoratorTerritoryQueries(this.designerCanvas.items[componentID]._decoratorInstance); } this.designerCanvas.deleteComponent(componentID); delete this._ComponentID2GMEID[componentID]; } delete this._GMEID2ComponentID[gmeID]; } else { //probably a subcomponent has been deleted - will be handled in the decorator this._checkComponentDependency(gmeID, CONSTANTS.TERRITORY_EVENT_UNLOAD); } } return territoryChanged; }; //TODO: check this here... ModelEditorControl.prototype.destroy = function () { this._detachClientEventListeners(); this._removeToolbarItems(); this._client.removeUI(this._territoryId); }; ModelEditorControl.prototype._removeConnectionSegmentPoints = function () { var idList = this.designerCanvas.selectionManager.getSelectedElements(), len = idList.length, nodeObj; this._client.startTransaction(); while (len--) { if (this.designerCanvas.connectionIds.indexOf(idList[len]) !== -1) { nodeObj = this._client.getNode(this._ComponentID2GMEID[idList[len]]); if (nodeObj) { this._client.delRegistry(nodeObj.getId(), REGISTRY_KEYS.LINE_CUSTOM_POINTS); } } } this._client.completeTransaction(); }; ModelEditorControl.prototype._getAllSourceDestinationPairsForConnection = function (GMESrcId, GMEDstId) { var sources = [], destinations = [], i; if (this._GMEID2ComponentID.hasOwnProperty(GMESrcId)) { //src is a DesignerItem i = this._GMEID2ComponentID[GMESrcId].length; while (i--) { sources.push({ objId: this._GMEID2ComponentID[GMESrcId][i], subCompId: undefined }); } } else { //src is not a DesignerItem //must be a sub_components somewhere, find the corresponding designerItem if (this._GMEID2Subcomponent && this._GMEID2Subcomponent.hasOwnProperty(GMESrcId)) { for (i in this._GMEID2Subcomponent[GMESrcId]) { if (this._GMEID2Subcomponent[GMESrcId].hasOwnProperty(i)) { sources.push({ objId: i, subCompId: this._GMEID2Subcomponent[GMESrcId][i] }); } } } } if (this._GMEID2ComponentID.hasOwnProperty(GMEDstId)) { i = this._GMEID2ComponentID[GMEDstId].length; while (i--) { destinations.push({ objId: this._GMEID2ComponentID[GMEDstId][i], subCompId: undefined }); } } else { //dst is not a DesignerItem //must be a sub_components somewhere, find the corresponding designerItem if (this._GMEID2Subcomponent && this._GMEID2Subcomponent.hasOwnProperty(GMEDstId)) { for (i in this._GMEID2Subcomponent[GMEDstId]) { if (this._GMEID2Subcomponent[GMEDstId].hasOwnProperty(i)) { destinations.push({ objId: i, subCompId: this._GMEID2Subcomponent[GMEDstId][i] }); } } } } return { sources: sources, destinations: destinations }; }; ModelEditorControl.prototype.registerComponentIDForPartID = function (componentID, partId) { this._componentIDPartIDMap[componentID] = this._componentIDPartIDMap[componentID] || []; if (this._componentIDPartIDMap[componentID].indexOf(partId) === -1) { this._componentIDPartIDMap[componentID].push(partId); } }; ModelEditorControl.prototype.unregisterComponentIDFromPartID = function (componentID, partId) { var idx; if (this._componentIDPartIDMap && this._componentIDPartIDMap[componentID]) { idx = this._componentIDPartIDMap[componentID].indexOf(partId); if (idx !== -1) { this._componentIDPartIDMap[componentID].splice(idx, 1); if (this._componentIDPartIDMap[componentID].length === 0) { delete this._componentIDPartIDMap[componentID]; } } } }; ModelEditorControl.prototype._checkComponentDependency = function (gmeID, eventType) { var len; if (this._componentIDPartIDMap && this._componentIDPartIDMap[gmeID]) { len = this._componentIDPartIDMap[gmeID].length; while (len--) { this._notifyPackage[this._componentIDPartIDMap[gmeID][len]] = this._notifyPackage[this._componentIDPartIDMap[gmeID][len]] || []; this._notifyPackage[this._componentIDPartIDMap[gmeID][len]].push({id: gmeID, event: eventType}); } } }; ModelEditorControl.prototype._handleDecoratorNotification = function () { var gmeID, i, itemID; for (gmeID in this._notifyPackage) { if (this._notifyPackage.hasOwnProperty(gmeID)) { this.logger.debug('NotifyPartDecorator: ' + gmeID + ', componentIDs: ' + JSON.stringify(this._notifyPackage[gmeID])); if (this._GMEID2ComponentID.hasOwnProperty(gmeID)) { //src is a DesignerItem i = this._GMEID2ComponentID[gmeID].length; while (i--) { itemID = this._GMEID2ComponentID[gmeID][i]; this.designerCanvas.notifyItemComponentEvents(itemID, this._notifyPackage[gmeID]); } } } } }; ModelEditorControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) { if (this.currentNodeInfo && this.currentNodeInfo.id === activeObjectId) { // [patrik] added this check to avoid redrawing when becoming active in split panel mode. this.logger.debug('Disregarding activeObject changed when it is already the same.'); } else { this.selectedObjectChanged(activeObjectId); } }; ModelEditorControl.prototype._stateActiveSelectionChanged = function (model, activeSelection, opts) { var selectedIDs = [], len = activeSelection ? activeSelection.length : 0; if (opts.invoker !== this) { while (len--) { if (this._GMEID2ComponentID.hasOwnProperty(activeSelection[len])) { selectedIDs = selectedIDs.concat(this._GMEID2ComponentID[activeSelection[len]]); } } this.designerCanvas.select(selectedIDs); } }; ModelEditorControl.prototype._activeProjectChanged = function (/*model, activeProjectId*/) { this._updateTopNode(); }; ModelEditorControl.prototype._updateTopNode = function () { var projectId = this._client.getActiveProjectId(), projectKind; if (projectId) { projectKind = this._client.getActiveProjectKind(); if (this._config.byProjectId.topNode.hasOwnProperty(projectId)) { this._topNode = this._config.byProjectId.topNode[projectId]; } else if (projectKind && this._config.byProjectKind.topNode.hasOwnProperty(projectKind)) { this._topNode = this._config.byProjectKind.topNode[projectKind]; } else { this._topNode = this._config.topNode; } } }; ModelEditorControl.prototype._attachClientEventListeners = function () { this._detachClientEventListeners(); WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged, this); WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_SELECTION, this._stateActiveSelectionChanged, this); WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_PROJECT_NAME, this._activeProjectChanged, this); }; ModelEditorControl.prototype._detachClientEventListeners = function () { WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged); WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_SELECTION, this._stateActiveSelectionChanged); WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_PROJECT_NAME, this._activeProjectChanged); }; ModelEditorControl.prototype.onActivate = function () { this._attachClientEventListeners(); this._displayToolbarItems(); if (this._selectedAspect) { WebGMEGlobal.State.registerActiveAspect(this._selectedAspect); } if (this.currentNodeInfo && typeof this.currentNodeInfo.id === 'string') { WebGMEGlobal.State.registerActiveObject(this.currentNodeInfo.id, {suppressVisualizerFromNode: true}); } }; ModelEditorControl.prototype.onDeactivate = function () { this._detachClientEventListeners(); this._hideToolbarItems(); }; ModelEditorControl.prototype._displayToolbarItems = function () { if (this._toolbarInitialized !== true) { this._initializeToolbar(); } else { for (var i = 0; i < this._toolbarItems.length; i++) { this._toolbarItems[i].show(); } } }; ModelEditorControl.prototype._hideToolbarItems = function () { if (this._toolbarInitialized === true) { for (var i = 0; i < this._toolbarItems.length; i++) { this._toolbarItems[i].hide(); } } }; ModelEditorControl.prototype._removeToolbarItems = function () { if (this._toolbarInitialized === true) { for (var i = 0; i < this._toolbarItems.length; i++) { this._toolbarItems[i].destroy(); } } }; ModelEditorControl.prototype._initializeToolbar = function () { var toolBar = WebGMEGlobal.Toolbar, self = this; this._toolbarItems = []; /****************** ADD BUTTONS AND THEIR EVENT HANDLERS TO DESIGNER CANVAS ******************/ /************** REMOVE CONNECTION SEGMENTPOINTS BUTTON ****************/ //TODO: This btn should probably be moved to the ContextMenu this.$btnConnectionRemoveSegmentPoints = toolBar.addButton( { title: 'Remove segment points', icon: 'glyphicon glyphicon-remove-circle', clickFn: function (/*data*/) { self._removeConnectionSegmentPoints(); } }); this._toolbarItems.push(this.$btnConnectionRemoveSegmentPoints); this.$btnConnectionRemoveSegmentPoints.enabled(false); this._toolbarInitialized = true; }; ModelEditorControl.prototype.getNodeID = function () { return this.currentNodeInfo.id; }; ModelEditorControl.prototype._updateAspects = function () { var nodeId = this.currentNodeInfo.id, nodeObj = this._client.getNode(nodeId), aspects, tabID, i, selectedTabID, activePanel = WebGMEGlobal.PanelManager.getActivePanel(); this._aspects = {}; this.designerCanvas.clearTabs(); // If the active panel isn't set (and the ModelEditor exists), assume // the ModelEditor is the active panel if (!activePanel || activePanel.control === this) { this._selectedAspect = WebGMEGlobal.State.getActiveAspect(); } if (nodeId || nodeId === CONSTANTS.PROJECT_ROOT_ID) { aspects = nodeObj ? nodeObj.getValidAspectNames() : []; aspects.sort(function (a, b) { var an = a.toLowerCase(), bn = b.toLowerCase(); return (an < bn) ? -1 : 1; }); aspects.splice(0, 0, CONSTANTS.ASPECT_ALL); this.designerCanvas.addMultipleTabsBegin(); for (i = 0; i < aspects.length; i += 1) { const tabInfo = { title: aspects[i], backgroundText: this._selectedLibrary ? `${this._selectedLibrary}.${aspects[i]}` : aspects[i] }; tabID = this.designerCanvas.addTab(tabInfo); this._aspects[tabID] = aspects[i]; if (this._selectedAspect && this._selectedAspect === aspects[i]) { selectedTabID = tabID; } } this.designerCanvas.addMultipleTabsEnd(); } if (!selectedTabID) { for (selectedTabID in this._aspects) { if (this._aspects.hasOwnProperty(selectedTabID)) { break; } } } this.designerCanvas.selectTab(selectedTabID.toString()); //check if the node's aspect rules has changed or not, and if so, initialize with that if (this._selectedAspect !== CONSTANTS.ASPECT_ALL) { var newAspectRules = this._client.getAspectTerritoryPattern(nodeId, this._selectedAspect); var aspectRulesChanged = false; if (this._selfPatterns[nodeId].items && newAspectRules.items) { aspectRulesChanged = (_.difference(this._selfPatterns[nodeId].items, newAspectRules.items)).length > 0; if (aspectRulesChanged === false) { aspectRulesChanged = (_.difference(newAspectRules.items, this._selfPatterns[nodeId].items)).length > 0; } } else { if (this._selfPatterns[nodeId].items || newAspectRules.items) { //at least one has an item aspectRulesChanged = true; } } if (aspectRulesChanged) { this.selectedObjectChanged(nodeId); } } }; ModelEditorControl.prototype._initializeSelectedAspect = function (tabID) { WebGMEGlobal.State.registerActiveAspect(this._selectedAspect); WebGMEGlobal.State.registerActiveTab(tabID); this.selectedObjectChanged(this.currentNodeInfo.id); }; ModelEditorControl.prototype.getConnectionDescriptor = function (/* gmeID */) { return {}; }; ModelEditorControl.getDefaultConfig = function () { return { topNode: '', byProjectKind: { topNode: {} }, byProjectId: { topNode: {} } }; }; ModelEditorControl.getComponentId = function () { return 'GenericUIModelEditorControl'; }; //attach ModelEditorControl - DesignerCanvas event handler functions _.extend(ModelEditorControl.prototype, ModelEditorControlDiagramDesignerWidgetEventHandlers.prototype); return ModelEditorControl; });