UNPKG

webgme

Version:

Web-based Generic Modeling Environment

1,164 lines (988 loc) 67.5 kB
/*globals define, _, WebGMEGlobal*/ /*jshint browser: true*/ /** * @author rkereskenyi / https://github.com/rkereskenyi */ define(['js/logger', 'js/RegistryKeys', 'js/Constants', './CrosscutConstants', 'js/DragDrop/DragHelper', 'js/Utils/GMEConcepts', 'js/Panels/ControllerBase/DiagramDesignerWidgetMultiTabMemberListControllerBase', 'js/Panels/MetaEditor/MetaRelations', 'js/NodePropertyNames', 'js/Utils/ComponentSettings' ], function (Logger, REGISTRY_KEYS, CONSTANTS, CrosscutConstants, DragHelper, GMEConcepts, DiagramDesignerWidgetMultiTabMemberListControllerBase, MetaRelations, nodePropertyNames, ComponentSettings) { 'use strict'; var CrosscutController, DEFAULT_DECORATOR = 'ModelDecorator', WIDGET_NAME = 'DiagramDesigner'; //CONNECTION_DECORATOR = 'CircleDecorator'; CrosscutController = function (options) { options = options || {}; options.loggerName = 'gme:Panels:CrossCut:CrosscutController'; this._config = CrosscutController.getDefaultConfig(); ComponentSettings.resolveWithWebGMEGlobal(this._config, CrosscutController.getComponentId()); DiagramDesignerWidgetMultiTabMemberListControllerBase.call(this, options); this._filteredOutConnTypes = []; this._filteredOutConnectionDescriptors = {}; this._activeCrosscutId = null; this._initActiveTab = false; this._tabsHasBeenRequested = false; this._autoCreateCrosscut = this._config.autoCreateCrosscut === true; this._initFilterPanel(); this.logger.debug('CrosscutController ctor finished'); }; _.extend(CrosscutController.prototype, DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype); CrosscutController.prototype._attachDiagramDesignerWidgetEventHandlers = function () { var self = this, oldTabChangefn; //cal base classes _attachDiagramDesignerWidgetEventHandlers DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype ._attachDiagramDesignerWidgetEventHandlers.call(this); //add own event handlers this._widget.onFilterNewConnectionDroppableEnds = function (params) { return self._onFilterNewConnectionDroppableEnds(params); }; this._widget.onCreateNewConnection = function (params) { self._onCreateNewConnection(params); }; this._widget.onModifyConnectionEnd = function (params) { self._onModifyConnectionEnd(params); }; this._widget.onCheckChanged = function (value, isChecked) { self._onConnectionTypeFilterCheckChanged(value, isChecked); }; oldTabChangefn = this._widget.onSelectedTabChanged; this._widget.onSelectedTabChanged = function (tabID) { oldTabChangefn(tabID); self._selectedTabChanged(tabID); }; }; //enable every node CrosscutController.prototype._validateNodeId = function (nodeId) { return nodeId; }; CrosscutController.prototype._canAddTab = function () { var memberListContainerID = this._memberListContainerID; return (memberListContainerID || memberListContainerID === CONSTANTS.PROJECT_ROOT_ID); }; CrosscutController.prototype._updateSelectedMemberListMembersTerritoryPatterns = function () { var currentlyDisplayedMembers = (this._selectedMemberListMembers || []).slice(0), actualMembers = (this._memberListMembers[this._selectedMemberListID] || []).slice(0), diff, len, territoryChanged = false, territoryId = this._selectedMemberListMembersTerritoryId, territoryPatterns = this._selectedMemberListMembersTerritoryPatterns, client = this._client, putConnectionsToBeHandledLast = function (list) { var boxes = [], connections = [], i, node; for (i = 0; i < list.length; i += 1) { node = client.getNode(list[i]); if (node && node.isConnection()) { connections.push(list[i]); } else { boxes.push(list[i]); } } return connections.concat(boxes); }; //let's see who has been deleted diff = putConnectionsToBeHandledLast(_.difference(currentlyDisplayedMembers, actualMembers)); len = diff.length; while (len--) { delete territoryPatterns[diff[len]]; territoryChanged = true; } //let's see who has been added diff = putConnectionsToBeHandledLast(_.difference(actualMembers, currentlyDisplayedMembers)); len = diff.length; while (len--) { territoryPatterns[diff[len]] = {children: 0}; territoryChanged = true; } //let's update the one that has not been changed but their position might have diff = putConnectionsToBeHandledLast(_.intersection(actualMembers, currentlyDisplayedMembers)); len = diff.length; this._widget.beginUpdate(); while (len--) { this._onUpdate(diff[len]); } this._widget.endUpdate(); //save current list of members this._selectedMemberListMembers = actualMembers; if (territoryChanged) { setTimeout(function () { client.updateTerritory(territoryId, territoryPatterns); }, 10); } }; CrosscutController.prototype._initializeSelectedMemberList = function () { this._nodePointers = {}; this._nodeSets = {}; this._connectionListByType = {}; this._filteredOutConnectionDescriptors = {}; //we should clear this this._connectionWaitingListByDstGMEID = {}; this._connectionWaitingListBySrcGMEID = {}; DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype._initializeSelectedMemberList.call(this); }; CrosscutController.prototype._memberListTerritoryCallback = function (events) { var decoratorsToDownload = [DEFAULT_DECORATOR], len = events.length, obj, objDecorator, connEvents = [], client = this._client, self = this; while (len--) { if ((events[len].etype === CONSTANTS.TERRITORY_EVENT_LOAD) || (events[len].etype === CONSTANTS.TERRITORY_EVENT_UPDATE)) { obj = client.getNode(events[len].eid); events[len].desc = {isConnection: GMEConcepts.isConnection(events[len].eid)}; if (obj) { //if it is a connection find src and dst and do not care about decorator if (events[len].desc.isConnection === true) { //objDecorator = CONNECTION_DECORATOR; //events[len].desc.isConnection = false; //events[len].desc.decoratorParams = {displayName: false}; events[len].desc.srcID = obj.getPointer(CONSTANTS.POINTER_SOURCE).to; events[len].desc.dstID = obj.getPointer(CONSTANTS.POINTER_TARGET).to; events[len].desc.reconnectable = true; events[len].desc.editable = true; } else { objDecorator = obj.getRegistry(REGISTRY_KEYS.DECORATOR); } if (!objDecorator || objDecorator === '') { objDecorator = DEFAULT_DECORATOR; } if (decoratorsToDownload.indexOf(objDecorator) === -1) { decoratorsToDownload.pushUnique(objDecorator); } events[len].desc.decorator = objDecorator; if (events[len].desc.isConnection) { connEvents.push(events[len]); events.splice(len, 1); } } } } client.decoratorManager.download(decoratorsToDownload, WIDGET_NAME, function () { //put the connection events back to the very end events = events.concat(connEvents); self._dispatchEvents(events); }); }; CrosscutController.prototype.getOrderedMemberListInfo = function (/* memberListContainerObject */) { var result = [], memberListContainerID = this._memberListContainerID, crosscutsRegistry = this.getMemberListSetsRegistry(memberListContainerID), len = crosscutsRegistry.length; if (this._autoCreateCrosscut === true && len === 0 && this._tabsHasBeenRequested === false) { this._onTabAddClicked(); memberListContainerID = this._memberListContainerID; crosscutsRegistry = this.getMemberListSetsRegistry(memberListContainerID); len = crosscutsRegistry.length; } this._tabsHasBeenRequested = true; while (len--) { result.push({ memberListID: crosscutsRegistry[len].SetID, title: crosscutsRegistry[len].title, filterInfo: crosscutsRegistry[len].filter || {}, enableDeleteTab: true, enableRenameTab: true, order: crosscutsRegistry[len].order }); } result.sort(function (a, b) { if (a.order < b.order) { return -1; } else { return 1; } }); return result; }; CrosscutController.prototype.getMemberListSetsRegistryKey = function () { return REGISTRY_KEYS.CROSSCUTS; }; CrosscutController.prototype.getMemberListSetsRegistry = GMEConcepts.getCrosscuts; CrosscutController.prototype.getNewSetNamePrefixDesc = function () { return { SetID: CrosscutConstants.CROSSCUT_NAME_PREFIX, Title: 'Crosscut ' }; }; /* * Overwrite 'no tab' warning message to the user */ CrosscutController.prototype.displayNoTabMessage = function () { this._widget.setBackgroundText('No crosscuts defined yet. Press the + button in the top-left corner to ' + 'create one...'); }; /**********************************************************/ /* HANDLE OBJECT DRAG & DROP ACCEPTANCE */ /**********************************************************/ CrosscutController.prototype._onBackgroundDroppableAccept = function (event, dragInfo) { var gmeIDList = DragHelper.getDragItems(dragInfo), accept = DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype._onBackgroundDroppableAccept.call( this, event, dragInfo); if (accept === true) { //if based on the DiagramDesignerWidgetMultiTabMemberListControllerBase check it could be accepted, // ie items are not members of the set so far //we need to see if we can accept them based on the META rules accept = GMEConcepts.isValidChildrenTypeInCrossCut(this._memberListContainerID, gmeIDList); } return accept; }; /**********************************************************/ /* END OF --- HANDLE OBJECT DRAG & DROP ACCEPTANCE */ /**********************************************************/ CrosscutController.prototype._onLoad = function (gmeID, desc) { var territoryChanged; //desc.isConnection = false; territoryChanged = DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype._onLoad.call(this, gmeID, desc); //process new node to display containment / pointers / inheritance / sets as connections //if (!desc.isConnection) { this._processNodePointers(gmeID, false); this._processNodePointers(gmeID, true); //} //check all the waiting pointers (whose SRC/DST is already displayed and waiting for the DST/SRC to show up) //it might be this new node this._processConnectionWaitingList(gmeID); return territoryChanged; }; CrosscutController.prototype._onUpdate = function (gmeID, desc) { //component updated var node = this._client.getNode(gmeID); desc = desc || {}; if (node && node.isConnection()) { desc.isConnection = true; desc.srcID = node.getPointer(CONSTANTS.POINTER_SOURCE).to; desc.dstID = node.getPointer(CONSTANTS.POINTER_TARGET).to; desc.reconnectable = true; desc.editable = true; } else { desc.isConnection = false; } //we are interested in the load of member items and their custom territory involvement if (this._selectedMemberListMembers.indexOf(gmeID) !== -1 && this._GMEID2ComponentID[gmeID]) { // TODO this is not a full solution, the whole crosscut and the underlying multi-tab needs refactoring!!! // First we should check if a connection changes from box or to box due to the update if (desc.isConnection) { if (this._GMEID2ComponentID[gmeID].length > 0 && this._GMEID2ComponentID[gmeID][0].indexOf('C_') === 0 && (!desc.srcID || !desc.dstID)) { // one endpoint value was removed - but not necessarily the item itself this._onUnload(gmeID); this._onLoad(gmeID, desc); return; } if (this._GMEID2ComponentID[gmeID].length > 0 && this._GMEID2ComponentID[gmeID][0].indexOf('C_') !== 0 && desc.srcID && desc.dstID) { // one endpoint value was removed - but not necessarily the item itself this._onUnload(gmeID); this._onLoad(gmeID, desc); return; } } DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype._onUpdate.call(this, gmeID, desc); //if (!node.isConnection()) { //process new node to display containment / pointers / inheritance / sets as connections this._processNodePointers(gmeID, false); this._processNodePointers(gmeID, true); //check all the waiting pointers (whose SRC/DST is already displayed and waiting for the DST/SRC to show up) //it might be this new node this._processConnectionWaitingList(gmeID); //} } else if (this._selectedMemberListMembers.indexOf(gmeID) === -1 && this._GMEID2ComponentID[gmeID]) { //member have been removed but still in territory this._onUpdatePortToItem(gmeID, true, desc); } else if (this._selectedMemberListMembers.indexOf(gmeID) !== -1 && !this._GMEID2ComponentID[gmeID]) { //new member that was in the territory this._onUpdatePortToItem(gmeID, false, desc); } }; CrosscutController.prototype._onUnload = function (gmeID) { var componentID, len, territoryChanged = false, aConns, connectionID, pointerName, otherEnd; //if (this._GMEID2ComponentID.hasOwnProperty(gmeID) && this._GMEID2ComponentID[gmeID][0].indexOf('C_') === 0) { // this._widget.deleteComponent(this._GMEID2ComponentID[gmeID][0]); // delete this._GMEID2ComponentID[gmeID]; //} else { if (this._GMEID2ComponentID.hasOwnProperty(gmeID)) { //gather all the information that is stored in this node's META //POINTERS len = this._nodePointers[gmeID].combinedNames.length; while (len--) { pointerName = this._nodePointers[gmeID].combinedNames[len]; otherEnd = this._nodePointers[gmeID][pointerName].target; pointerName = this._nodePointers[gmeID][pointerName].name; this._removeConnection(gmeID, otherEnd, MetaRelations.META_RELATIONS.POINTER, pointerName); } //POINTER LISTS len = this._nodeSets[gmeID].combinedNames.length; while (len--) { pointerName = this._nodeSets[gmeID].combinedNames[len]; otherEnd = this._nodeSets[gmeID][pointerName].target; pointerName = this._nodeSets[gmeID][pointerName].name; this._removeConnection(gmeID, otherEnd, MetaRelations.META_RELATIONS.SET, pointerName); } len = this._GMEID2ComponentID[gmeID].length; while (len--) { componentID = this._GMEID2ComponentID[gmeID][len]; if (this._widget.itemIds.indexOf(componentID) !== -1) { territoryChanged = territoryChanged || this._updateDecoratorTerritoryQuery( this._widget.items[componentID]._decoratorInstance, true); } this._widget.deleteComponent(componentID); delete this._ComponentID2GMEID[componentID]; } delete this._GMEID2ComponentID[gmeID]; } //check if one of the decorators' is dependent on this this._checkComponentDependency(gmeID, CONSTANTS.TERRITORY_EVENT_UNLOAD); //check if there is any more connection present that's associated with this object //typically the connection end is this guy //if so, remove but save to savedList aConns = this._getAssociatedConnections(gmeID); len = aConns.src.length; while (len--) { connectionID = aConns.src[len]; //save the connection to the waiting list, since the destination is still there this._saveConnectionToWaitingList(this._connectionListByID[connectionID].GMESrcId, this._connectionListByID[connectionID].GMEDstId, this._connectionListByID[connectionID].type, this._connectionListByID[connectionID].connTexts); this._removeConnection(this._connectionListByID[connectionID].GMESrcId, this._connectionListByID[connectionID].GMEDstId, this._connectionListByID[connectionID].type); } len = aConns.dst.length; while (len--) { connectionID = aConns.dst[len]; if (this._connectionListByID[connectionID]) { //save the connection to the waiting list, since the destination is still there this._saveConnectionToWaitingList(this._connectionListByID[connectionID].GMESrcId, this._connectionListByID[connectionID].GMEDstId, this._connectionListByID[connectionID].type, this._connectionListByID[connectionID].connTexts); this._removeConnection(this._connectionListByID[connectionID].GMESrcId, this._connectionListByID[connectionID].GMEDstId, this._connectionListByID[connectionID].type); } } //check the waiting list and remove any connection that was waiting and this end was present for (otherEnd in this._connectionWaitingListBySrcGMEID) { if (this._connectionWaitingListBySrcGMEID.hasOwnProperty(otherEnd)) { delete this._connectionWaitingListBySrcGMEID[otherEnd][gmeID]; if (_.isEmpty(this._connectionWaitingListBySrcGMEID[otherEnd])) { delete this._connectionWaitingListBySrcGMEID[otherEnd]; } } } for (otherEnd in this._connectionWaitingListByDstGMEID) { if (this._connectionWaitingListByDstGMEID.hasOwnProperty(otherEnd)) { delete this._connectionWaitingListByDstGMEID[otherEnd][gmeID]; if (_.isEmpty(this._connectionWaitingListByDstGMEID[otherEnd])) { delete this._connectionWaitingListByDstGMEID[otherEnd]; } } } //keep up accounting delete this._nodePointers[gmeID]; delete this._nodeSets[gmeID]; //} return territoryChanged; }; /*CrosscutController.prototype._onUpdatePortToItem = function (gmeID) { var members = this._selectedMemberListMembers, i, node, src, dst; for (i = 0; i < members.length; i += 1) { node = this._client.getNode(members[i]); if (node && node.isConnection()) { src = node.getPointer('src').to; dst = node.getPointer('dst').to; if (src === gmeID || dst === gmeID) { DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype._onUnload.call(this, members[i]); DiagramDesignerWidgetMultiTabMemberListControllerBase.prototype._onLoad.call(this, members[i], { isConnection: true, srcID: src, dstID: dst, reconnectable: true, editable: true }); } } } };*/ CrosscutController.prototype._getAssociatedConnections = function (objectID) { var result = {src: [], dst: []}, otherID, connType, len, cID, checkConnections; checkConnections = function (cList, res) { //check objectID as source if (cList.hasOwnProperty(objectID)) { for (otherID in cList[objectID]) { if (cList[objectID].hasOwnProperty(otherID)) { for (connType in cList[objectID][otherID]) { if (cList[objectID][otherID].hasOwnProperty(connType)) { len = cList[objectID][otherID][connType].length; while (len--) { cID = cList[objectID][otherID][connType][len]; if (res.indexOf(cID) === -1) { res.push(cID); } } } } } } } }; checkConnections(this._connectionListBySrcGMEID, result.src); checkConnections(this._connectionListByDstGMEID, result.dst); return result; }; /*******************************************************************************/ /* DISPLAY META POINTER RELATIONS AS A CONNECTION FROM CONTAINER TO CONTAINED */ /*******************************************************************************/ CrosscutController.prototype._processNodePointers = function (gmeID, isSet) { var node = this._client.getNode(gmeID), pointerNames = node === null ? [] : isSet === true ? GMEConcepts.getSets(gmeID) : node.getPointerNames(), pointerTo, len, oldPointers, newPointers = {names: [], combinedNames: []}, diff, pointerTarget, pointerName, idx, combinedName, isConnection = node === null ? false : node.isConnection(), setMembers, member; if (isSet === true) { this._nodeSets[gmeID] = this._nodeSets[gmeID] || {names: [], combinedNames: []}; oldPointers = this._nodeSets[gmeID]; } else { this._nodePointers[gmeID] = this._nodePointers[gmeID] || {names: [], combinedNames: []}; oldPointers = this._nodePointers[gmeID]; } len = pointerNames.length; while (len--) { if (isSet) { setMembers = node.getMemberIds(pointerNames[len]); member = setMembers.pop(); if (typeof member === 'string') { newPointers.names.push(pointerNames[len]); } while (typeof member === 'string') { combinedName = gmeID + '_' + pointerNames[len] + '_' + member; newPointers.combinedNames.push(combinedName); newPointers[combinedName] = { name: pointerNames[len], target: member, type: MetaRelations.META_RELATIONS.SET }; member = setMembers.pop(); } } else { if (pointerNames[len] !== CONSTANTS.POINTER_BASE && !(isConnection && (pointerNames[len] === CONSTANTS.POINTER_SOURCE || pointerNames[len] === CONSTANTS.POINTER_TARGET))) { pointerTo = node.getPointer(pointerNames[len]).to; if (pointerTo) { combinedName = gmeID + '_' + pointerNames[len] + '_' + pointerTo; newPointers.names.push(pointerNames[len]); newPointers.combinedNames.push(combinedName); newPointers[combinedName] = { name: pointerNames[len], target: pointerTo, type: MetaRelations.META_RELATIONS.POINTER }; } } } } //base and parent are special relations and they also needs to be added when not checked for sets if (!isSet && node) { pointerTo = node.getParentId(); if (typeof pointerTo === 'string') { combinedName = gmeID + '_' + CONSTANTS.RELATION_CONTAINMENT + '_' + pointerTo; newPointers.names.push(CONSTANTS.RELATION_CONTAINMENT); newPointers.combinedNames.push(combinedName); newPointers[combinedName] = { name: CONSTANTS.RELATION_CONTAINMENT, target: pointerTo, type: MetaRelations.META_RELATIONS.CONTAINMENT }; } pointerTo = node.getBaseId(); if (typeof pointerTo === 'string') { combinedName = gmeID + '_' + CONSTANTS.POINTER_BASE + '_' + pointerTo; newPointers.names.push(CONSTANTS.POINTER_BASE); newPointers.combinedNames.push(combinedName); newPointers[combinedName] = { name: CONSTANTS.POINTER_BASE, target: pointerTo, type: MetaRelations.META_RELATIONS.INHERITANCE }; } } //compute deleted pointers diff = _.difference(oldPointers.combinedNames, newPointers.combinedNames); len = diff.length; while (len--) { combinedName = diff[len]; pointerName = oldPointers[combinedName].name; pointerTarget = oldPointers[combinedName].target; this._removeConnection(gmeID, pointerTarget, oldPointers[combinedName].type, pointerName); idx = oldPointers.combinedNames.indexOf(combinedName); oldPointers.combinedNames.splice(idx, 1); delete oldPointers[combinedName]; } //compute added pointers diff = _.difference(newPointers.combinedNames, oldPointers.combinedNames); len = diff.length; while (len--) { combinedName = diff[len]; pointerName = newPointers[combinedName].name; pointerTarget = newPointers[combinedName].target; oldPointers.names.push(pointerName); oldPointers.combinedNames.push(combinedName); oldPointers[combinedName] = { name: newPointers[combinedName].name, target: newPointers[combinedName].target, type: newPointers[combinedName].type }; this._createConnection(gmeID, pointerTarget, newPointers[combinedName].type, {name: pointerName}); } }; /******************************************************************************************/ /* END OF --- DISPLAY META POINTER RELATIONS AS A CONNECTION FROM CONTAINER TO CONTAINED */ /******************************************************************************************/ CrosscutController.prototype._processConnectionWaitingList = function (gmeID) { var len, gmeSrcID, gmeDstID, connType, connTexts, c = []; //check for possible endpoint as gmeID gmeDstID = gmeID; if (this._connectionWaitingListByDstGMEID && this._connectionWaitingListByDstGMEID.hasOwnProperty(gmeDstID)) { for (gmeSrcID in this._connectionWaitingListByDstGMEID[gmeDstID]) { if (this._connectionWaitingListByDstGMEID[gmeDstID].hasOwnProperty(gmeSrcID)) { len = this._connectionWaitingListByDstGMEID[gmeDstID][gmeSrcID].length; while (len--) { connType = this._connectionWaitingListByDstGMEID[gmeDstID][gmeSrcID][len][0]; connTexts = this._connectionWaitingListByDstGMEID[gmeDstID][gmeSrcID][len][1]; c.push({ gmeSrcID: gmeSrcID, gmeDstID: gmeDstID, connType: connType, connTexts: connTexts }); } } } delete this._connectionWaitingListByDstGMEID[gmeDstID]; } //check for possible source as gmeID gmeSrcID = gmeID; if (this._connectionWaitingListBySrcGMEID && this._connectionWaitingListBySrcGMEID.hasOwnProperty(gmeSrcID)) { for (gmeDstID in this._connectionWaitingListBySrcGMEID[gmeSrcID]) { if (this._connectionWaitingListBySrcGMEID[gmeSrcID].hasOwnProperty(gmeDstID)) { len = this._connectionWaitingListBySrcGMEID[gmeSrcID][gmeDstID].length; while (len--) { connType = this._connectionWaitingListBySrcGMEID[gmeSrcID][gmeDstID][len][0]; connTexts = this._connectionWaitingListBySrcGMEID[gmeSrcID][gmeDstID][len][1]; c.push({ gmeSrcID: gmeSrcID, gmeDstID: gmeDstID, connType: connType, connTexts: connTexts }); } } } delete this._connectionWaitingListBySrcGMEID[gmeSrcID]; } len = c.length; while (len--) { gmeSrcID = c[len].gmeSrcID; gmeDstID = c[len].gmeDstID; connType = c[len].connType; connTexts = c[len].connTexts; this._createConnection(gmeSrcID, gmeDstID, connType, connTexts); } }; /**************************************************************************/ /* END OF --- HANDLE OBJECT LOAD DISPLAY IT WITH ALL THE POINTERS / ... */ /**************************************************************************/ /****************************************************************************/ /* CREATE A SPECIFIC TYPE OF CONNECTION BETWEEN 2 GME OBJECTS */ /****************************************************************************/ CrosscutController.prototype._createConnection = function (gmeSrcId, gmeDstId, connType, connTexts) { var connDesc, connComponent, visualType; //need to check if the src and dst objects are displayed or not //if YES, create connection //if NO, store information in a waiting queue if (this._GMEID2ComponentID[gmeSrcId] && this._GMEID2ComponentID[gmeSrcId].length > 0 && this._GMEID2ComponentID[gmeDstId] && this._GMEID2ComponentID[gmeDstId].length > 0) { //source and destination is displayed if (this._filteredOutConnTypes.indexOf(connType) === -1) { //connection type is not filtered out connDesc = { srcObjId: this._GMEID2ComponentID[gmeSrcId][0], srcSubCompId: undefined, dstObjId: this._GMEID2ComponentID[gmeDstId][0], dstSubCompId: undefined, reconnectable: false, name: '', nameEdit: false }; //set visual properties visualType = connType; if (connTexts) { switch (connTexts.name) { case CONSTANTS.POINTER_BASE: visualType = MetaRelations.META_RELATIONS.INHERITANCE; break; case CONSTANTS.RELATION_CONTAINMENT: visualType = MetaRelations.META_RELATIONS.CONTAINMENT; connDesc.srcObjId = this._GMEID2ComponentID[gmeDstId][0]; connDesc.dstObjId = this._GMEID2ComponentID[gmeSrcId][0]; break; default: break; } } _.extend(connDesc, MetaRelations.getLineVisualDescriptor(visualType)); //fill out texts if (connTexts) { _.extend(connDesc, connTexts); } connComponent = this._widget.createConnection(connDesc); this._saveConnection(gmeSrcId, gmeDstId, connType, connComponent.id, connTexts); } else { //connection type is filtered out this._filteredOutConnectionDescriptors[connType] = this._filteredOutConnectionDescriptors[connType] || []; this._filteredOutConnectionDescriptors[connType].push([gmeSrcId, gmeDstId, connTexts]); } } else { //source or destination is not displayed, store it in a queue this._saveConnectionToWaitingList(gmeSrcId, gmeDstId, connType, connTexts); } }; CrosscutController.prototype._saveConnectionToWaitingList = function (gmeSrcId, gmeDstId, connType, connTexts) { var scrDisplayed = this._GMEID2ComponentID[gmeSrcId] && this._GMEID2ComponentID[gmeSrcId].length > 0, dstDisplayed = this._GMEID2ComponentID[gmeDstId] && this._GMEID2ComponentID[gmeDstId].length > 0; if (scrDisplayed && !dstDisplayed) { //#1 - the destination object is missing from the screen this._connectionWaitingListByDstGMEID[gmeDstId] = this._connectionWaitingListByDstGMEID[gmeDstId] || {}; this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId] = this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId] || []; this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId].push([connType, connTexts]); } else if (!scrDisplayed && dstDisplayed) { //#2 - the source object is missing from the screen this._connectionWaitingListBySrcGMEID[gmeSrcId] = this._connectionWaitingListBySrcGMEID[gmeSrcId] || {}; this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId] = this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId] || []; this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId].push([connType, connTexts]); } else { //#3 - both gmeSrcId and gmeDstId is missing from the screen //NOTE: this should never happen!!! //this.logger.error('_saveConnectionToWaitingList both gmeSrcId and gmeDstId is undefined...'); } }; CrosscutController.prototype._saveConnection = function (gmeSrcId, gmeDstId, connType, connComponentId, connTexts) { //save by SRC this._connectionListBySrcGMEID[gmeSrcId] = this._connectionListBySrcGMEID[gmeSrcId] || {}; this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] || {}; this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType] = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType] || []; this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].push(connComponentId); //save by DST this._connectionListByDstGMEID[gmeDstId] = this._connectionListByDstGMEID[gmeDstId] || {}; this._connectionListByDstGMEID[gmeDstId][gmeSrcId] = this._connectionListByDstGMEID[gmeDstId][gmeSrcId] || {}; this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType] = this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType] || []; this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType].push(connComponentId); //save by type this._connectionListByType[connType] = this._connectionListByType[connType] || []; this._connectionListByType[connType].push(connComponentId); //save by connectionID this._connectionListByID[connComponentId] = { GMESrcId: gmeSrcId, GMEDstId: gmeDstId, type: connType, name: (connTexts && connTexts.name) ? connTexts.name : undefined, connTexts: connTexts }; }; /****************************************************************************/ /* END OF --- CREATE A SPECIFIC TYPE OF CONNECTION BETWEEN 2 GME OBJECTS */ /****************************************************************************/ /****************************************************************************/ /* REMOVES A SPECIFIC TYPE OF CONNECTION FROM 2 GME OBJECTS */ /****************************************************************************/ CrosscutController.prototype._removeConnection = function (gmeSrcId, gmeDstId, connType, pointerName) { var connectionID, idx, len, connectionPresent = false; //only bother if //- both the source and destination is present on the screen //the connection in question is drawn if (this._connectionListBySrcGMEID[gmeSrcId] && this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] && this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType]) { connectionPresent = true; } if (!connectionPresent) { return; } len = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].length; while (len--) { connectionID = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType][len]; //if a pointer with a specific name should be removed //clear out the connectionID if this connection is not the representation of that pointer if (connType === MetaRelations.META_RELATIONS.POINTER && pointerName && pointerName !== '' && this._connectionListByID[connectionID].name !== pointerName) { connectionID = undefined; } //if the connectionID is still valid if (connectionID) { this._widget.deleteComponent(connectionID); //clean up accounting delete this._connectionListByID[connectionID]; idx = this._connectionListByType[connType].indexOf(connectionID); this._connectionListByType[connType].splice(idx, 1); idx = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].indexOf(connectionID); this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].splice(idx, 1); idx = this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType].indexOf(connectionID); this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType].splice(idx, 1); } } }; /****************************************************************************/ /* END OF --- REMOVES A SPECIFIC TYPE OF CONNECTION FROM 2 GME OBJECTS */ /****************************************************************************/ /*************************************************************/ /* HANDLE OBJECT / CONNECTION DELETION IN THE ASPECT ASPECT */ /*************************************************************/ CrosscutController.prototype._onSelectionDelete = function (idList) { var client = this._client, memberListContainerID = this._memberListContainerID, memberListToRemoveFrom = this._selectedMemberListID, len, gmeID, gmeObj, componentId, NON_DELETABLE_POINTERS = [CONSTANTS.POINTER_SOURCE, CONSTANTS.POINTER_TARGET, CONSTANTS.POINTER_BASE], lineDesc, canDeletePointer, exclusiveMemberOfCrosscut = function (gmeObject) { var setsInfo = gmeObject.isMemberOf(), setOwnerId, crosscutMemberships = [], i; for (setOwnerId in setsInfo) { for (i = 0; i < setsInfo[setOwnerId].length; i += 1) { if (setsInfo[setOwnerId][i].indexOf(CrosscutConstants.CROSSCUT_NAME_PREFIX) === 0) { crosscutMemberships.push(setsInfo[setOwnerId][i]); } } } if (crosscutMemberships.length === 1 && crosscutMemberships[0] === memberListToRemoveFrom) { return true; } return false; }, logger = this.logger; client.startTransaction(); len = idList.length; while (len--) { componentId = idList[len]; gmeID = this._ComponentID2GMEID[componentId]; //#1: deleting an item --> deleting a member // #1/a: if deleting a connection whose hierarchical parent is the membershipContainer and only occurs in // this crosscut (#896 issue resolution), // delete the connection from the hierarchy too //#2: deleting a line --> deleting a pointer // #2/a: do not let the user delete an src/dst pointer?? if (gmeID) { //deleting a box --> remove from crosscut's set logger.debug('removeMember memberListContainerID: ' + memberListContainerID + ', gmeID: ' + gmeID + ', memberListToRemoveFrom: ' + memberListToRemoveFrom); //_client.removeMember(memberListContainerID, gmeID, memberListToRemoveFrom); // check if this GME object is a connection and whether it's parent is the crosscut container and it is // and exclusive member of this crosscut gmeObj = client.getNode(gmeID); if (GMEConcepts.isConnection(gmeID) && gmeObj.getParentId() === memberListContainerID && exclusiveMemberOfCrosscut(gmeObj)) { if (GMEConcepts.canDeleteNode(gmeID)) { logger.debug('deleting connection from crosscut hierarchy too gmeID: ' + gmeID); client.deleteNodes([gmeID]); } else { client.removeMember(memberListContainerID, gmeID, memberListToRemoveFrom); } } else { client.removeMember(memberListContainerID, gmeID, memberListToRemoveFrom); } } else { //deleting a line lineDesc = this._connectionListByID[componentId]; if (lineDesc) { canDeletePointer = true; // #2/a: do not let the user delete an src/dst pointer?? if (lineDesc.type === MetaRelations.META_RELATIONS.POINTER && NON_DELETABLE_POINTERS.indexOf(lineDesc.name) !== -1) { canDeletePointer = false; } if (canDeletePointer) { logger.debug('deleting pointer from: ' + lineDesc.GMESrcId + ', type: ' + lineDesc.name); //it's a pointer that's allowed to be deleted client.delPointer(lineDesc.GMESrcId, lineDesc.name); } else { logger.warn('can not delete pointer from: ' + lineDesc.GMESrcId + ', type: ' + lineDesc.name); } } } } client.completeTransaction(); }; /************************************************************************/ /* END OF --- HANDLE OBJECT / CONNECTION DELETION IN THE ASPECT ASPECT */ /************************************************************************/ CrosscutController.prototype._onFilterNewConnectionDroppableEnds = function (params) { var availableConnectionEnds = params.availableConnectionEnds, result = [], i, sourceId, targetId, j, client = this._client, validPointerTargetTypes, validSetTargetTypes, validConnectionEndTypes, validConnectionTypes, validEndTypes, parentId = this._memberListContainerID, pointerMetaDescriptor, p; if (params.srcSubCompId === undefined) { sourceId = this._ComponentID2GMEID[params.srcId]; } else { sourceId = this._Subcomponent2GMEID[params.srcId][params.srcSubCompId]; } //need to figure out who are the potential end items for a 'connection' //#1: all the items are potential 'connection' destination that could be a pointer target from this source //#2: all the items are potential 'connection' destination that could be a set (pointerlist) target // from this source //#3: all the items are potential 'connection' destination where a WebGME-connection can be created // between this source and that target and the connection can be created in this parent based on the META rules //check #1: validPointerTargetTypes = GMEConcepts.getValidPointerTargetTypesFromSource(sourceId, false); //check #2: validSetTargetTypes = GMEConcepts.getValidPointerTargetTypesFromSource(sourceId, true); //check #3: validConnectionTypes = GMEConcepts.getValidConnectionTypesFromSourceInAspect(sourceId, parentId, CONSTANTS.ASPECT_ALL); validConnectionEndTypes = []; //filter them to see which of those can actually be created as a child of the parent //check what endpoint could be a potential endpoint of any of the connection types i = validConnectionTypes.length; while (i--) { if (GMEConcepts.canCreateChild(parentId, validConnectionTypes[i])) { pointerMetaDescriptor = this._client.getValidTargetItems(validConnectionTypes[i], CONSTANTS.POINTER_TARGET); if (pointerMetaDescriptor && pointerMetaDescriptor.length > 0) { j = pointerMetaDescriptor.length; while (j--) { if (validConnectionEndTypes.indexOf(pointerMetaDescriptor[j].id) === -1) { validConnectionEndTypes.push(pointerMetaDescriptor[j].id); } } } } } //filter out the items on the screen validEndTypes = _.union(validPointerTargetTypes, validSetTargetTypes, validConnectionEndTypes); this.logger.debug('validEndTypes: ' + validEndTypes); i = availableConnectionEnds.length; while (i--) { p = availableConnectionEnds[i]; if (p.dstSubCompID === undefined) { targetId = this._ComponentID2GMEID[p.dstItemID]; } else { targetId = this._Subcomponent2GMEID[p.dstItemID][p.dstSubCompID]; } j = validEndTypes.length; while (j--) { if (client.isTypeOf(targetId, validEndTypes[j])) { result.push(availableConnectionEnds[i]); break; } } } return result; }; CrosscutController.prototype._onCreateNewConnection = function (params) { var sourceId, targetId, self = this, parentId = this._memberListContainerID, client = this._client, menuItems = {}, sourceObj, targetObj, logger = this.logger, aspect = this._selectedMemberListID, dstPosition = this._widget.items[params.dst].getBoundingBox(), srcPosition = this._widget.items[params.src].getBoundingBox(), validPointerTypes, validSetTypes, validConnectionTypes, existingConns; function createPointer(srcId, dstId, ptrName) { logger.debug('createPointer srcId: ' + srcId + ', dstId: ' + dstId + ', ptrName: ' + ptrName); client.setPointer(srcId, ptrName, dstId); } function addToSet(containerId, objId, setName) { logger.debug('addToSet containerId: ' + containerId + ', objId: ' + objId + ', ptrName: ' + setName);