UNPKG

webgme

Version:

Web-based Generic Modeling Environment

934 lines (832 loc) 41.3 kB
/*globals define, WebGMEGlobal, _*/ /*jshint browser: true*/ /** * @author rkereskenyi / https://github.com/rkereskenyi * @author nabana / https://github.com/nabana */ define(['js/logger', 'common/util/canon', 'js/NodePropertyNames', 'js/RegistryKeys', 'js/Constants', 'assets/line/lineSvgs', 'js/Utils/DisplayFormat', './PropertyEditorPanelControllerHelpers', 'js/Dialogs/DecoratorSVGExplorer/DecoratorSVGExplorerDialog', 'js/Dialogs/ValidVisualizers/ValidVisualizersDialog', 'js/Controls/PropertyGrid/PropertyGridWidgets' ], function (Logger, CANON, nodePropertyNames, REGISTRY_KEYS, CONSTANTS, LINE_SVG_DIRECTORY, displayFormat, PropertyEditorPanelControllerHelpers, DecoratorSVGExplorerDialog, ValidVisualizersDialog, PROPERTY_GRID_WIDGETS) { 'use strict'; var PropertyEditorController, NO_COMMON_VALUE_COLOR = '#f89406', META_REGISTRY_KEYS = [ REGISTRY_KEYS.IS_PORT, REGISTRY_KEYS.IS_ABSTRACT, REGISTRY_KEYS.VALID_PLUGINS, REGISTRY_KEYS.USED_ADDONS, REGISTRY_KEYS.VALID_VISUALIZERS, REGISTRY_KEYS.VALID_DECORATORS ], TEMPLATING_SUB_GROUP = 'Templating', LINE_SUB_GROUP = 'Line', COLOR_SUB_GROUP = 'Color', ICON_SUB_GROUP = 'Icon', PREFERENCES_BASIC_SUB_GROUP = 'Basic', PREFERENCES_REGISTRY_KEYS = [ REGISTRY_KEYS.DECORATOR, REGISTRY_KEYS.DISPLAY_FORMAT, REGISTRY_KEYS.SVG_ICON, REGISTRY_KEYS.TREE_ITEM_COLLAPSED_ICON, REGISTRY_KEYS.TREE_ITEM_EXPANDED_ICON, REGISTRY_KEYS.SVG_ICON, REGISTRY_KEYS.PORT_SVG_ICON, REGISTRY_KEYS.PORT_ORIENTATION, REGISTRY_KEYS.COLOR, REGISTRY_KEYS.TEXT_COLOR, REGISTRY_KEYS.BORDER_COLOR, REGISTRY_KEYS.BOX_DECORATION, REGISTRY_KEYS.LINE_STYLE, REGISTRY_KEYS.LINE_START_ARROW, REGISTRY_KEYS.LINE_END_ARROW, REGISTRY_KEYS.LINE_WIDTH, REGISTRY_KEYS.LINE_LABEL_PLACEMENT, REGISTRY_KEYS.LINE_LABEL_X_OFFSET, REGISTRY_KEYS.LINE_LABEL_Y_OFFSET, REGISTRY_KEYS.REPLACEABLE ], LINE_REG_KEYS = [ REGISTRY_KEYS.BOX_DECORATION, REGISTRY_KEYS.LINE_STYLE, REGISTRY_KEYS.LINE_START_ARROW, REGISTRY_KEYS.LINE_END_ARROW, REGISTRY_KEYS.LINE_WIDTH, REGISTRY_KEYS.LINE_LABEL_PLACEMENT, REGISTRY_KEYS.LINE_LABEL_X_OFFSET, REGISTRY_KEYS.LINE_LABEL_Y_OFFSET, ], NON_INVALID_PTRS = [CONSTANTS.POINTER_BASE]; PropertyEditorController = function (client, propertyGrid, type) { this._client = client; this._propertyGrid = propertyGrid; this.NON_INVALID_PTRS = NON_INVALID_PTRS; if ([CONSTANTS.PROPERTY_GROUP_ATTRIBUTES, CONSTANTS.PROPERTY_GROUP_POINTERS, CONSTANTS.PROPERTY_GROUP_META, CONSTANTS.PROPERTY_GROUP_PREFERENCES].indexOf(type) === -1) { throw new Error('No valid type given for property controller!'); } this._type = type; // CONSTANTS.PROPERTY_GROUP_ATTRIBUTES ... this._logger = Logger.create('gme:Panels:PropertyEditor:PropertyEditorController', WebGMEGlobal.gmeConfig.client.log); //it should be sorted alphabetically this._propertyGrid.setOrdered(true); //set custom types here this._propertyGrid.registerWidgetForType('boolean', 'iCheckBox'); this._initEventHandlers(); this._logger.debug('Created'); }; // Prototypical inheritance from PropertyEditorPanelControllerHelpers. PropertyEditorController.prototype = Object.create(PropertyEditorPanelControllerHelpers.prototype); PropertyEditorController.prototype.constructor = PropertyEditorController; // Event handling and update triggering PropertyEditorController.prototype._initEventHandlers = function () { var self = this; WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_SELECTION, function (model, activeSelection) { self._logger.debug('activeSelection changed', activeSelection); var activeNodeId = WebGMEGlobal.State.getActiveObject(); if (activeSelection && activeSelection.length > 0) { self._selectedObjectsChanged(activeSelection); } else { self._selectObjectsUsingActiveObject(activeNodeId); } }); WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, function (model, activeObjectId) { self._logger.debug('active object changed: ', activeObjectId); self._selectObjectsUsingActiveObject(activeObjectId); }); this._propertyGrid.onFinishChange(function (args) { self._onPropertyChanged(args); }); this._propertyGrid.onReset(function (propertyName) { self._onReset(propertyName); }); }; PropertyEditorController.prototype._selectObjectsUsingActiveObject = function (activeObjectId) { if (activeObjectId || activeObjectId === CONSTANTS.PROJECT_ROOT_ID) { this._selectedObjectsChanged([activeObjectId]); } else { this._selectedObjectsChanged([]); } }; PropertyEditorController.prototype._selectedObjectsChanged = function (idList) { var patterns = {}, i, self = this; this._idList = idList; if (this._territoryId) { this._client.removeUI(this._territoryId); } if (idList.length > 0) { i = idList.length; while (i--) { patterns[idList[i]] = {children: 0}; } this._territoryId = this._client.addUI(this, function (events) { self._logger.debug('about to refresh property list', events); self._refreshPropertyList(); }); this._client.updateTerritory(this._territoryId, patterns); } else { this._refreshPropertyList(); } }; PropertyEditorController.prototype._refreshPropertyList = function () { var propList = this._getCommonPropertiesForSelection(this._idList); this._logger.debug('propList', this._idList, propList); this._initializeReadOnlyForSelection(this._idList); this._propertyGrid.setPropertyList(propList); }; PropertyEditorController.prototype._getCommonPropertiesForSelection = function (selectedObjIDs) { var self = this, propList = {}, isFirstNode = true, selectedNodes = [], cNode, metaTypeId, i, flattenedAttrs, flattenedPreferences, flattenedMeta, flattenedPointers, commonAttrs = {}, commonPreferences = {}, commonMeta = {}, commonPointers = {}, commonAttrMeta = {}, rootNode = this._client.getNode(CONSTANTS.PROJECT_ROOT_ID), validDecorators = null, decoratorNames = WebGMEGlobal.allDecorators; if (rootNode && rootNode.getRegistry(REGISTRY_KEYS.VALID_DECORATORS)) { validDecorators = rootNode.getRegistry(REGISTRY_KEYS.VALID_DECORATORS).split(' '); this._logger.debug('validDecorators registered on root-node', validDecorators); decoratorNames = decoratorNames.filter(function (avaliableDecorator) { return validDecorators.indexOf(avaliableDecorator) > -1; }); } else { this._logger.debug('Could not get validDecorators from root-node'); if (!rootNode) { this._logger.warn('rootNode was not avaliable'); } } decoratorNames.sort(function (a, b) { if (a.toLowerCase() < b.toLowerCase()) { return -1; } else { return 1; } }); if (selectedObjIDs.length === 0) { return propList; } //get all attributes //get all registry elements i = selectedObjIDs.length; while (--i >= 0) { cNode = this._client.getNode(selectedObjIDs[i]); if (cNode) { selectedNodes.push(cNode); switch (self._type) { case CONSTANTS.PROPERTY_GROUP_ATTRIBUTES: flattenedAttrs = this._getNodeAttributeValues(cNode); this._buildCommonAttrMeta(commonAttrMeta, cNode, isFirstNode); this._filterCommon(commonAttrMeta, commonAttrs, flattenedAttrs, isFirstNode); break; case CONSTANTS.PROPERTY_GROUP_PREFERENCES: // flattenedPreferences = this._getNodeRegistryValues(cNode, PREFERENCES_REGISTRY_KEYS); this._filterCommon(commonAttrMeta, commonPreferences, flattenedPreferences, isFirstNode); break; case CONSTANTS.PROPERTY_GROUP_META: flattenedMeta = this._getNodeRegistryValues(cNode, META_REGISTRY_KEYS); this._filterCommon(commonAttrMeta, commonMeta, flattenedMeta, isFirstNode); break; case CONSTANTS.PROPERTY_GROUP_POINTERS: flattenedPointers = self._getPointerInfo(cNode); this._filterCommon(commonAttrMeta, commonPointers, flattenedPointers, isFirstNode); break; default: // Should not happen as it is check in constructor. self._logger.error('Unknown type', self._type); } isFirstNode = false; } } if (selectedNodes.length === 1) { cNode = selectedNodes[0]; propList[' ID/Path'] = { name: 'ID', value: cNode.getId(), valueType: 'string', isCommon: true, readOnly: true, clipboard: true, extraCss: { fontFamily: 'monospace' } }; if (cNode) { propList[' GUID'] = { name: 'GUID', value: cNode.getGuid(), valueType: 'string', isCommon: true, readOnly: true, clipboard: true, extraCss: { fontFamily: 'monospace' } }; if (cNode.isLibraryElement()) { propList[' GUIDl'] = { name: 'GUID (library)', value: cNode.getLibraryGuid(), valueType: 'string', isCommon: true, readOnly: true, clipboard: true, extraCss: { fontFamily: 'monospace' } }; } metaTypeId = cNode.getMetaTypeId(); if (metaTypeId) { propList[' Meta Type'] = { name: 'Meta type', value: metaTypeId, isCommon: true, widget: PROPERTY_GRID_WIDGETS.META_TYPE_WIDGET, client: self._client }; } } } switch (self._type) { case CONSTANTS.PROPERTY_GROUP_ATTRIBUTES: propList[CONSTANTS.PROPERTY_GROUP_ATTRIBUTES] = { name: CONSTANTS.PROPERTY_GROUP_ATTRIBUTES, text: CONSTANTS.PROPERTY_GROUP_ATTRIBUTES, value: undefined, isFolder: true }; this._addItemsToResultList(selectedNodes, commonAttrMeta, decoratorNames, commonAttrs, CONSTANTS.PROPERTY_GROUP_ATTRIBUTES, propList, true, false, false); break; case CONSTANTS.PROPERTY_GROUP_PREFERENCES: propList[PREFERENCES_BASIC_SUB_GROUP] = { name: CONSTANTS.PROPERTY_GROUP_PREFERENCES, text: PREFERENCES_BASIC_SUB_GROUP, value: undefined, isFolder: true }; if (commonPointers.hasOwnProperty(CONSTANTS.POINTER_CONSTRAINED_BY)) { commonPreferences[CONSTANTS.POINTER_CONSTRAINED_BY] = commonPointers[CONSTANTS.POINTER_CONSTRAINED_BY]; delete commonPointers[CONSTANTS.POINTER_CONSTRAINED_BY]; } this._addItemsToResultList(selectedNodes, commonAttrMeta, decoratorNames, commonPreferences, CONSTANTS.PROPERTY_GROUP_PREFERENCES, propList, false, true, false); break; case CONSTANTS.PROPERTY_GROUP_META: propList[CONSTANTS.PROPERTY_GROUP_META] = { name: CONSTANTS.PROPERTY_GROUP_META, text: CONSTANTS.PROPERTY_GROUP_META, value: undefined, isFolder: true }; this._addItemsToResultList(selectedNodes, commonAttrMeta, decoratorNames, commonMeta, CONSTANTS.PROPERTY_GROUP_META, propList, false, true, false); break; case CONSTANTS.PROPERTY_GROUP_POINTERS: propList[CONSTANTS.PROPERTY_GROUP_POINTERS] = { name: CONSTANTS.PROPERTY_GROUP_POINTERS, text: CONSTANTS.PROPERTY_GROUP_POINTERS, value: undefined, isFolder: true }; this._addItemsToResultList(selectedNodes, commonAttrMeta, decoratorNames, commonPointers, CONSTANTS.PROPERTY_GROUP_POINTERS, propList, false, false, true); break; default: // Should not happen as it is check in constructor. self._logger.error('Unknown type', self._type); } return propList; }; PropertyEditorController.prototype._addItemsToResultList = function (selectedNodes, commonAttrMeta, decoratorNames, src, prefix, dst, isAttribute, isRegistry, isPointer) { var onlyRootSelected = selectedNodes.length === 1 && selectedNodes[0].getId() === CONSTANTS.PROJECT_ROOT_ID, keys = Object.keys(src), onlyConnectionsSelected = true, canBeReplaceable, key, range, i, isPassword = false, extKey, repKey, cbyKey, keyParts; for (i = 0; i < selectedNodes.length; i += 1) { if (selectedNodes[i].isConnection() === false) { onlyConnectionsSelected = false; break; } } if (prefix !== '') { prefix += '.'; } for (i = 0; i < keys.length; i += 1) { key = keys[i]; if (key === CONSTANTS.POINTER_CONSTRAINED_BY) { // This is handled in replaceable. continue; } if (isAttribute) { if (commonAttrMeta.hasOwnProperty(key) === false) { continue; } isPassword = commonAttrMeta[key].isPassword ? true : false; } if (onlyRootSelected) { // If only the root is selected... if (prefix === CONSTANTS.PROPERTY_GROUP_PREFERENCES + '.' && key !== REGISTRY_KEYS.TREE_ITEM_COLLAPSED_ICON && key !== REGISTRY_KEYS.TREE_ITEM_EXPANDED_ICON) { // all but the tree icons are hidden in the preferences continue; } else if (prefix === CONSTANTS.PROPERTY_GROUP_META + '.' && key === REGISTRY_KEYS.IS_ABSTRACT || key === REGISTRY_KEYS.IS_PORT) { // isAbstract and isPort are hidden i meta. continue; } } else { // If not only the root is selected... if (prefix === CONSTANTS.PROPERTY_GROUP_META + '.' && (key === REGISTRY_KEYS.USED_ADDONS || key === REGISTRY_KEYS.VALID_DECORATORS)) { // we hide the used addon' and 'valid decorators' fields. continue; } } // If not only connections are selected, the connection related preferences are filtered out. if (onlyConnectionsSelected !== true && prefix === CONSTANTS.PROPERTY_GROUP_PREFERENCES + '.' && LINE_REG_KEYS.indexOf(key) > -1) { continue; } extKey = prefix + key; keyParts = key.split('.'); dst[extKey] = { name: keyParts[keyParts.length - 1], value: src[key].value, valueType: src[key].valueType, options: src[key].options }; if (key === 'position.x' || key === 'position.y') { dst[extKey].minValue = 0; dst[extKey].stepValue = 10; } if (src[key].readOnly === false || src[key].readOnly === true) { dst[extKey].readOnly = src[key].readOnly; } if (src[key].isCommon === false) { dst[extKey].value = ''; dst[extKey].options = {textColor: NO_COMMON_VALUE_COLOR}; } if (isAttribute === true) { if (isPassword) { dst[extKey].options = dst[extKey].options || {}; dst[extKey].options.isPassword = true; } if (this._isReadonlyAttribute(selectedNodes, keyParts[0])) { dst[extKey].readOnly = true; } else { //is it inherited??? if so, it can be reseted to the inherited value if (this._isResettableAttribute(selectedNodes, keyParts[0])) { dst[extKey].options = dst[extKey].options || {}; dst[extKey].options.resetable = true; } //if it is an attribute it might be invalid according the current meta rules if (this._isInvalidAttribute(selectedNodes, keyParts[0])) { dst[extKey].options = dst[extKey].options || {}; dst[extKey].options.invalid = true; } else if (this._isInvalidAttributeValue(selectedNodes, keyParts[0])) { dst[extKey].options = dst[extKey].options || {}; dst[extKey].options.invalidValue = true; } } if (commonAttrMeta[key].description) { dst[extKey].text = commonAttrMeta[key].description; } //if the attribute value is an enum, display the enum values if (commonAttrMeta[key].enum && commonAttrMeta[key].enum.length > 0) { dst[extKey].valueItems = commonAttrMeta[key].enum.slice(0); dst[extKey].valueItems.sort(); } // Get the min max for floats and integers if (dst[extKey].valueType === 'float' || dst[extKey].valueType === 'integer') { range = this._getAttributeRange(selectedNodes, keyParts[0]); dst[extKey].minValue = range.min; dst[extKey].maxValue = range.max; } // Check if the attribute is a multi-line if (commonAttrMeta[key].multiline) { dst[extKey].multiline = true; dst[extKey].multilineType = commonAttrMeta[key].multilineType || 'generic'; } } else if (isRegistry === true) { //is it inherited??? if so, it can be reseted to the inherited value if (this._isResettableRegistry(selectedNodes, keyParts[0])) { dst[extKey].options = dst[extKey].options || {}; dst[extKey].options.resetable = true; } if (prefix === CONSTANTS.PROPERTY_GROUP_PREFERENCES + '.') { //decorator value should be rendered as an option list if (key === REGISTRY_KEYS.DECORATOR) { //dstList[extKey].valueType = "option"; //FIXME: only the decorators for DiagramDesigner are listed so far dst[extKey].valueItems = decoratorNames; } else if (key === REGISTRY_KEYS.SVG_ICON || key === REGISTRY_KEYS.PORT_SVG_ICON || key === REGISTRY_KEYS.TREE_ITEM_COLLAPSED_ICON || key === REGISTRY_KEYS.TREE_ITEM_EXPANDED_ICON) { dst[ICON_SUB_GROUP] = { name: ICON_SUB_GROUP, text: ICON_SUB_GROUP, value: undefined, isFolder: true }; repKey = ICON_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].widget = PROPERTY_GRID_WIDGETS.DIALOG_WIDGET; dst[repKey].dialog = DecoratorSVGExplorerDialog; dst[repKey].value = typeof dst[repKey].value !== 'string' ? '' : dst[repKey].value; if (WebGMEGlobal.SvgManager.isSvg(dst[repKey].value)) { dst[repKey].displayedValue = '_inmodel svg_'; dst[repKey].useDisplayedValue = WebGMEGlobal.SvgManager.isSvg; } dst[repKey].clipboard = true; } else if (key === REGISTRY_KEYS.PORT_ORIENTATION) { dst[ICON_SUB_GROUP] = { name: ICON_SUB_GROUP, text: ICON_SUB_GROUP, value: undefined, isFolder: true }; repKey = ICON_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].value = typeof dst[repKey].value !== 'string' ? '' : dst[repKey].value; dst[repKey].valueItems = ['E', 'W', 'S', 'N']; } else if (key === REGISTRY_KEYS.COLOR || key === REGISTRY_KEYS.BORDER_COLOR || key === REGISTRY_KEYS.TEXT_COLOR) { dst[COLOR_SUB_GROUP] = { name: COLOR_SUB_GROUP, text: COLOR_SUB_GROUP, value: undefined, isFolder: true }; repKey = COLOR_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].widget = PROPERTY_GRID_WIDGETS.COLOR_PICKER; } else if (key === REGISTRY_KEYS.LINE_STYLE) { // all option is always available so we create the subgroup only here dst[LINE_SUB_GROUP] = { name: LINE_SUB_GROUP, text: LINE_SUB_GROUP, value: undefined, isFolder: true }; repKey = LINE_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].widget = PROPERTY_GRID_WIDGETS.SVG_SELECT; dst[repKey].items = LINE_SVG_DIRECTORY[REGISTRY_KEYS.LINE_STYLE]; dst[repKey].value = dst[repKey].value || CONSTANTS.LINE_STYLE.PATTERNS.SOLID; } else if (key === REGISTRY_KEYS.LINE_START_ARROW || key === REGISTRY_KEYS.LINE_END_ARROW) { repKey = LINE_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].widget = PROPERTY_GRID_WIDGETS.SVG_SELECT; dst[repKey].items = LINE_SVG_DIRECTORY[REGISTRY_KEYS.LINE_END_ARROW]; dst[repKey].value = dst[repKey].value || CONSTANTS.LINE_STYLE.LINE_ARROWS.NONE; } else if (key === REGISTRY_KEYS.LINE_WIDTH) { repKey = LINE_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].widget = PROPERTY_GRID_WIDGETS.SVG_SELECT; dst[repKey].items = LINE_SVG_DIRECTORY[REGISTRY_KEYS.LINE_WIDTH]; dst[repKey].value = dst[repKey].value || 1; } else if (key === REGISTRY_KEYS.LINE_LABEL_PLACEMENT) { repKey = LINE_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; dst[repKey].widget = PROPERTY_GRID_WIDGETS.SVG_SELECT; dst[repKey].items = LINE_SVG_DIRECTORY[REGISTRY_KEYS.LINE_LABEL_PLACEMENT]; dst[repKey].value = dst[repKey].value || CONSTANTS.LINE_STYLE.LABEL_PLACEMENTS.MIDDLE; } else if (key === REGISTRY_KEYS.LINE_LABEL_X_OFFSET || key === REGISTRY_KEYS.LINE_LABEL_Y_OFFSET) { repKey = LINE_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; dst[repKey].valueType = 'integer'; delete dst[extKey]; dst[repKey].value = dst[repKey].value || 0; } else if (key === REGISTRY_KEYS.REPLACEABLE) { dst[TEMPLATING_SUB_GROUP] = { name: TEMPLATING_SUB_GROUP, text: TEMPLATING_SUB_GROUP, value: undefined, isFolder: true }; repKey = TEMPLATING_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; delete dst[extKey]; canBeReplaceable = this._canBeReplaceable(selectedNodes); dst[repKey].value = (!dst[repKey].value || canBeReplaceable === false) ? false : true; dst[repKey].valueType = 'boolean'; if (canBeReplaceable === false) { dst[repKey].readOnly = true; dst[repKey].alwaysReadOnly = true; dst[repKey].title = 'Meta nodes or inherited children cannot be templates.'; } if (keys.indexOf(CONSTANTS.POINTER_CONSTRAINED_BY) > -1) { cbyKey = TEMPLATING_SUB_GROUP + '.' + CONSTANTS.POINTER_CONSTRAINED_BY; dst[cbyKey] = { name: CONSTANTS.POINTER_CONSTRAINED_BY, value: src[CONSTANTS.POINTER_CONSTRAINED_BY].value, valueType: src[CONSTANTS.POINTER_CONSTRAINED_BY].valueType, options: src[CONSTANTS.POINTER_CONSTRAINED_BY].options || {}, widget: PROPERTY_GRID_WIDGETS.POINTER_WIDGET, client: this._client }; dst[cbyKey].options.resetable = this._isResettablePointer(selectedNodes, CONSTANTS.POINTER_CONSTRAINED_BY); dst[cbyKey].options.invalid = this._isInvalidPointer(selectedNodes, CONSTANTS.POINTER_CONSTRAINED_BY); if (dst[repKey].value === false && !dst[cbyKey].options.resetable && !dst[cbyKey].options.invalid) { // In this case it is only clutter to display this pointer widget. delete dst[cbyKey]; } } } else if (key === REGISTRY_KEYS.BOX_DECORATION) { repKey = LINE_SUB_GROUP + '.' + key; dst[repKey] = dst[extKey]; dst[repKey].value = !!dst[extKey].value; dst[repKey].valueType = 'boolean'; } } else if (prefix === CONSTANTS.PROPERTY_GROUP_META + '.') { if (key === REGISTRY_KEYS.VALID_VISUALIZERS) { dst[extKey].widget = PROPERTY_GRID_WIDGETS.DIALOG_WIDGET; dst[extKey].dialog = ValidVisualizersDialog; dst[extKey].value = dst[extKey].value === undefined ? '' : dst[extKey].value; } else if (key === REGISTRY_KEYS.VALID_PLUGINS) { dst[extKey].value = dst[extKey].value === undefined ? '' : dst[extKey].value; dst[extKey].valueItems = WebGMEGlobal.allPlugins; dst[extKey].widget = PROPERTY_GRID_WIDGETS.MULTI_SELECT_WIDGET; } else if (onlyRootSelected) { if (key === REGISTRY_KEYS.VALID_DECORATORS) { dst[extKey].value = dst[extKey].value === undefined ? '' : dst[extKey].value; dst[extKey].valueItems = WebGMEGlobal.allDecorators; dst[extKey].widget = PROPERTY_GRID_WIDGETS.MULTI_SELECT_WIDGET; } else if (key === REGISTRY_KEYS.USED_ADDONS) { dst[extKey].value = dst[extKey].value === undefined ? '' : dst[extKey].value; dst[extKey].valueItems = WebGMEGlobal.allAddOns; dst[extKey].widget = PROPERTY_GRID_WIDGETS.MULTI_SELECT_WIDGET; } } else if (key === REGISTRY_KEYS.IS_ABSTRACT || key === REGISTRY_KEYS.IS_PORT) { // Make sure they're treated like a boolean. dst[extKey].value = !!dst[extKey].value; dst[extKey].valueType = 'boolean'; } } } else if (isPointer === true) { dst[extKey].options = dst[extKey].options || {}; // What is non-invalid cannot be reset dst[extKey].options.resetable = NON_INVALID_PTRS.indexOf(keyParts[0]) === -1 && this._isResettablePointer(selectedNodes, keyParts[0]); dst[extKey].options.invalid = this._isInvalidPointer(selectedNodes, keyParts[0]); //pointers have a custom widget that allows following the pointer dst[extKey].widget = PROPERTY_GRID_WIDGETS.POINTER_WIDGET; //add custom widget specific values dst[extKey].client = this._client; } if (!dst[extKey]) { delete dst[extKey]; } } }; PropertyEditorController.prototype._filterCommon = function (commonAttrMeta, result, other, initPhase) { var it, i, keys; if (initPhase === true) { keys = Object.keys(other); for (i = 0; i < keys.length; i += 1) { it = keys[i]; if (commonAttrMeta.hasOwnProperty(it)) { result[it] = { value: other[it], valueType: commonAttrMeta[it].type, isCommon: true }; } else { result[it] = { value: other[it], valueType: typeof other[it], isCommon: true }; } } } else { keys = Object.keys(result); for (i = 0; i < keys.length; i += 1) { it = keys[i]; if (other.hasOwnProperty(it)) { if (result[it].isCommon) { result[it].isCommon = result[it].value === other[it]; } } else { delete result[it]; } } } }; PropertyEditorController.prototype._buildCommonAttrMeta = function (commonAttrMeta, node, initPhase) { var nodeAttributeNames = _.union(node.getAttributeNames() || [], node.getValidAttributeNames() || []), len = nodeAttributeNames.length, attrMetaDescriptor, attrName, attrNames, i; attrNames = Object.keys(commonAttrMeta); // First delete the ones from the common that do not exist at this node. for (i = 0; i < attrNames.length; i += 1) { if (nodeAttributeNames.indexOf(attrNames[i]) === -1) { delete commonAttrMeta[attrName]; } } // For the remaining list check if still common, that is the attribute-meta's are deeply equal. while (len--) { attrName = nodeAttributeNames[len]; attrMetaDescriptor = node.getAttributeMeta(attrName) || {type: 'string'}; if (commonAttrMeta.hasOwnProperty(attrName)) { if (CANON.stringify(commonAttrMeta[attrName]) !== CANON.stringify(attrMetaDescriptor)) { delete commonAttrMeta[attrName]; } } else if (initPhase) { if (attrMetaDescriptor.hidden !== true || node.isMetaNode()) { commonAttrMeta[attrName] = {}; _.extend(commonAttrMeta[attrName], attrMetaDescriptor); } } } }; PropertyEditorController.prototype._onPropertyChanged = function (args) { var selectedObjIDs = this._idList, i = selectedObjIDs.length, keyArr, setterFn, getterFn, propObject, propPointer, gmeID, path; if (args.newValue === undefined) { //cannot set value to undefined, so we handle as removal this._onReset(args.id); return; } this._client.startTransaction(); while (--i >= 0) { gmeID = selectedObjIDs[i]; keyArr = args.id.split('.'); setterFn = undefined; getterFn = undefined; if (keyArr[0] === CONSTANTS.PROPERTY_GROUP_ATTRIBUTES) { setterFn = 'setAttribute'; getterFn = 'getAttribute'; } else if (keyArr[0] === CONSTANTS.PROPERTY_GROUP_PREFERENCES || keyArr[0] === CONSTANTS.PROPERTY_GROUP_META) { setterFn = 'setRegistry'; getterFn = 'getRegistry'; } else if (keyArr[0] === CONSTANTS.PROPERTY_GROUP_POINTERS) { this._client.setPointer(gmeID, keyArr[1], args.newValue); } else if (keyArr[0] === LINE_SUB_GROUP || keyArr[0] === ICON_SUB_GROUP || keyArr[0] === COLOR_SUB_GROUP) { setterFn = 'setRegistry'; getterFn = 'getRegistry'; } else if (keyArr[0] === TEMPLATING_SUB_GROUP) { if (keyArr[1] === REGISTRY_KEYS.REPLACEABLE) { setterFn = 'setRegistry'; getterFn = 'getRegistry'; } else if (keyArr[1] === CONSTANTS.POINTER_CONSTRAINED_BY) { this._client.setPointer(gmeID, keyArr[1], args.newValue); } } if (setterFn && getterFn) { keyArr.splice(0, 1); //get property object from node path = keyArr[0]; propObject = this._client.getNode(gmeID)[getterFn](path); //get root object propPointer = propObject; keyArr.splice(0, 1); if (keyArr.length < 1) { //simple value so just set it propObject = args.newValue; } else { //dig down to leaf property while (keyArr.length > 1) { propPointer = propPointer[keyArr[0]]; keyArr.splice(0, 1); } //set value propPointer[keyArr[0]] = args.newValue; } //save back object this._client[setterFn](gmeID, path, propObject); } } this._client.completeTransaction(); }; PropertyEditorController.prototype._onReset = function (propertyName) { var selectedObjIDs = this._idList, i = selectedObjIDs.length, keyArr, delFn, gmeID, path; this._client.startTransaction(); while (--i >= 0) { gmeID = selectedObjIDs[i]; keyArr = propertyName.split('.'); delFn = undefined; if (keyArr[0] === CONSTANTS.PROPERTY_GROUP_ATTRIBUTES) { delFn = 'delAttribute'; } else if (keyArr[0] === CONSTANTS.PROPERTY_GROUP_PREFERENCES || keyArr[0] === CONSTANTS.PROPERTY_GROUP_META) { delFn = 'delRegistry'; } else if (keyArr[0] === CONSTANTS.PROPERTY_GROUP_POINTERS) { delFn = 'delPointer'; } else if (keyArr[0] === TEMPLATING_SUB_GROUP) { if (keyArr[1] === REGISTRY_KEYS.REPLACEABLE) { delFn = 'delRegistry'; } else if (keyArr[1] === CONSTANTS.POINTER_CONSTRAINED_BY) { delFn = 'delPointer'; } } else if (keyArr[0] === LINE_SUB_GROUP || keyArr[0] === COLOR_SUB_GROUP || keyArr[0] === ICON_SUB_GROUP) { delFn = 'delRegistry'; } if (delFn) { keyArr.splice(0, 1); path = keyArr[0]; this._client[delFn](gmeID, path); } } this._client.completeTransaction(); }; PropertyEditorController.prototype._initializeReadOnlyForSelection = function (objectIds) { var i, node, isReadOnly = false; if (this._client.isProjectReadOnly() || this._client.isCommitReadOnly()) { isReadOnly = true; } else { for (i = 0; i < objectIds.length; i += 1) { node = this._client.getNode(objectIds[i]); if (node && (node.isLibraryRoot() || node.isLibraryElement())) { isReadOnly = true; break; } } } this._propertyGrid.setReadOnly(isReadOnly); this.setReadOnly(isReadOnly); }; return PropertyEditorController; });