Mercurial > hg > dml-open-vis
diff src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.10-ConfigGrid.js @ 0:493bcb69166c
added public content
author | Daniel Wolff |
---|---|
date | Tue, 09 Feb 2016 20:54:02 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.10-ConfigGrid.js Tue Feb 09 20:54:02 2016 +0100 @@ -0,0 +1,525 @@ +"use strict"; + +App.module("ContextModule", function(ContextModule, App, Backbone, Marionette, $, _, Logger) { + + // Define private variables + var logger = null; + + ContextModule.addInitializer(function(options){ + + logger = Logger.get("ContextModule.ConfigGrid"); + logger.setLevel(Logger.WARN); + + /** + * ConfigGrid stores the configuration of a grid that consists of entityConfigs and viewConfigs + * (both are Backbone.Collection of Config) + * + * In real situation entityConfigs are music collection configs and music recording configs + * + * two sub-collections can be interacted directly (without proxy methods) + * + * The grid can be given a read-only type on creation in order to easily to distinguish between collection and recording grid later + * getType() method is available for this purpose. The type of the grid is not being serialized or unserialized + * + * The following events are triggered: + * + * change_layout + * when both or any of the two collections of Config gets new objects, looses objects or shuffles + * (but not when parameters in individual parameter bags change) + * + * change_entity:c123 + * change_view:c123 + * when a particular parameter bag changes (c123 is replaced with a corresponding client id of an entity or a view) + * + * change_entity_neighbours:c123 + * change_view_neighbours:c123 + * when a Config, which is right before or right after the given parameter bag, changes + * This includes cases when neighbours are added or removed + * + * change_selection + * when selectedEntityConfigClientId or (and) selectedViewConfigClientId change + * + * change + * this event is triggered together with any of the above ones, but + * maximum once during a complex operation such as unserialize + */ + ContextModule.ConfigGrid = Backbone.Model.extend({ + defaults: { + entityConfigs: null, + viewConfigs: null, + selectedEntityConfigClientId: null, + selectedViewConfigClientId: null + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + initialize: function(type) { + + this.type = type; + + this.attributes.entityConfigs = new ContextModule.ConfigCollection(null, { + comparator: false, + configGridType: type, + dimension: "entity", + }); + this.attributes.viewConfigs = new ContextModule.ConfigCollection(null, { + comparator: false, + configGridType: type, + dimension: "view", + }); + + // Shortcuts for entity configs and view configs (for quicker access) + this.entityConfigs = this.attributes.entityConfigs; + this.viewConfigs = this.attributes.viewConfigs; + + this._modificationPropagationEnabled = true; + this._configCollectionsWereModified = false; + this._configsWereModified = false; + this._modifiedEntityConfigClientIds = []; + this._modifiedViewConfigClientIds = []; + this._lastSavedOrderedEntityClientIds = _.pluck(this.attributes.entityConfigs.models, "cid"); + this._lastSavedOrderedViewClientIds = _.pluck(this.attributes.viewConfigs.models, "cid"); + this._lastSavedSelectedEntityConfigClientId = this.attributes.selectedEntityConfigClientId; + this._lastSavedSelectedViewConfigClientId = this.attributes.selectedViewConfigClientId; + + this.entityConfigs.bind("add remove reset sort", this._registerModificationOfConfigCollectionForEntities, this); + this.viewConfigs.bind("add remove reset sort", this._registerModificationOfConfigCollectionForViews, this); + + this.entityConfigs.bind("change", this._registerModificationOfConfig, this); + this.viewConfigs.bind("change", this._registerModificationOfConfig, this); + + this.bind("change:selectedEntityConfigClientId", this._registerModificationOfAtomicProperty); + this.bind("change:selectedViewConfigClientId", this._registerModificationOfAtomicProperty); + this.bind("change:entityWidth", this._registerModificationOfStandardAtomicProperty); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + getType: function() { + return this.type; + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + getPrevEntityNeighbour: function(entityConfig) { + return this._getNeighbour(this.attributes.entityConfigs, entityConfig, -1); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + getNextEntityNeighbour: function(entityConfig) { + return this._getNeighbour(this.attributes.entityConfigs, entityConfig, 1); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + getPrevViewNeighbour: function(viewConfig) { + return this._getNeighbour(this.attributes.viewConfigs, viewConfig, -1); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + getNextViewNeighbour: function(viewConfig) { + return this._getNeighbour(this.attributes.viewConfigs, viewConfig, 1); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + relocateEntityConfig: function(entityConfig, indexOrNextConfigOrNextConfigClientId) { + return this._relocate(this.attributes.entityConfigs, entityConfig, indexOrNextConfigOrNextConfigClientId); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + relocateViewConfig: function(viewConfig, indexOrNextConfigOrNextConfigClientId) { + return this._relocate(this.attributes.viewConfigs, viewConfig, indexOrNextConfigOrNextConfigClientId); + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + serialize: function() { + logger.debug("method called: ConfigGrid::serialize"); + var _this = this; + + var result = { + entityConfigs: this.attributes.entityConfigs.map(function(config){ return config.serialize(); }), + viewConfigs: this.attributes.viewConfigs.map(function(config){ return config.serialize(); }) + }; + + if (this.attributes.selectedEntityConfigClientId) { + result.selectedEntityConfigClientId = this.attributes.selectedEntityConfigClientId; + } + if (this.attributes.selectedViewConfigClientId) { + result.selectedViewConfigClientId = this.attributes.selectedViewConfigClientId; + } + if (this.attributes.entityWidth) { + result.entityWidth = this.attributes.entityWidth; + } + + return result; + }, + + getSelectedEntityConfig: function() { + return this.attributes.entityConfigs.get(this.attributes.selectedEntityConfigClientId); + }, + + getSelectedViewConfig: function() { + return this.attributes.viewConfigs.get(this.attributes.selectedViewConfigClientId); + }, + +// getSelectedConfigAtGivenDimension: function(dimension) { +// if (dimension == "entity") { +// return this.getSelectedEntityConfig(); +// } else if (dimension == "view") { +// return this.getSelectedViewConfig(); +// } +// }, +// + addEntityAndSelectIt: function(entityConfig, indexOrNextConfigOrNextConfigClientId) { + this._addAndSelect(this.attributes.entityConfigs, entityConfig, _.isUndefined(indexOrNextConfigOrNextConfigClientId) ? null : indexOrNextConfigOrNextConfigClientId); + }, + + addViewAndSelectIt: function(viewConfig, indexOrNextConfigOrNextConfigClientId) { + this._addAndSelect(this.attributes.viewConfigs, viewConfig, _.isUndefined(indexOrNextConfigOrNextConfigClientId) ? null : indexOrNextConfigOrNextConfigClientId); + }, + + removeEntityAndSelectNeighbour: function(entityConfig) { + this._removeAndSelectNeighbour(this.attributes.entityConfigs, entityConfig); + }, + + removeViewAndSelectNeighbour: function(viewConfig) { + this._removeAndSelectNeighbour(this.attributes.viewConfigs, viewConfig); + }, + + _addAndSelect: function(configCollection, config, indexOrNextConfigOrNextConfigClientId) { + this._modificationPropagationEnabled = false; + + configCollection.add(config); + if (configCollection == this.attributes.entityConfigs) { + this.attributes.selectedEntityConfigClientId = config.getClientId(); + } else { + this.attributes.selectedViewConfigClientId = config.getClientId(); + } + this._relocate(configCollection, config, indexOrNextConfigOrNextConfigClientId); + + this._triggerModificationEventsIfNeeded(); + this._modificationPropagationEnabled = true; + + }, + + _removeAndSelectNeighbour: function(configCollection, config) { + this._modificationPropagationEnabled = false; + + var neighbourToSelect = this._getNeighbour(configCollection, config, 1); + if (!neighbourToSelect) { + neighbourToSelect = this._getNeighbour(configCollection, config, -1); + } + configCollection.remove(config); + if (configCollection == this.attributes.entityConfigs) { + this.attributes.selectedEntityConfigClientId = neighbourToSelect ? neighbourToSelect.getClientId() : null; + } else { + this.attributes.selectedViewConfigClientId = neighbourToSelect ? neighbourToSelect.getClientId() : null; + } + + this._triggerModificationEventsIfNeeded(); + this._modificationPropagationEnabled = true; + + }, + + /** + * @memberOf App.ContextModule.ConfigGrid + */ + unserialize: function(serializedAttributes) { + logger.debug("method called: ConfigGrid::unserialize"); + + this._modificationPropagationEnabled = false; + + var fixedSerializedAttributes = serializedAttributes; + if (!_.isSimpleObject(serializedAttributes)) { + logger.warn("ConfigGrid::unserialize called for not an object: ", serializedAttributes); + fixedSerializedAttributes = {}; + } + + // entityConfigs + var newConfigs = []; + var fixedSerializedConfigs = fixedSerializedAttributes.entityConfigs; + if (!_.isArray(fixedSerializedConfigs)) { + if (_.isSimpleObject(serializedAttributes)) { + logger.warn("ConfigGrid::unserialize called for an object with faulty entityConfigs: ", fixedSerializedConfigs); + } + fixedSerializedConfigs = []; + }; + for (var i = 0; i < fixedSerializedConfigs.length; i++) { + var serializedConfig = fixedSerializedConfigs[i]; + var config = this.attributes.entityConfigs.get(serializedConfig.clientId); + if (!config) { + config = new App.ContextModule.Config(serializedConfig); + } else { + config.unserialize(serializedConfig); + } + newConfigs.push(config); + } + this.attributes.entityConfigs.reset(newConfigs); + + // viewConfigs + var newConfigs = []; + var fixedSerializedConfigs = fixedSerializedAttributes.viewConfigs; + if (!_.isArray(fixedSerializedConfigs)) { + if (_.isSimpleObject(serializedAttributes)) { + logger.warn("ConfigGrid::unserialize called for an object with faulty viewConfigs: ", fixedSerializedConfigs); + } + fixedSerializedConfigs = []; + }; + for (var i = 0; i < fixedSerializedConfigs.length; i++) { + var serializedConfig = fixedSerializedConfigs[i]; + var config = this.attributes.viewConfigs.get(serializedConfig.clientId); + if (!config) { + config = new App.ContextModule.Config(serializedConfig); + } else { + config.unserialize(serializedConfig); + } + newConfigs.push(config); + } + this.attributes.viewConfigs.reset(newConfigs); + + // selectedEntityConfigClientId, selectedViewConfigClientId + this.attributes.selectedEntityConfigClientId = fixedSerializedAttributes.selectedEntityConfigClientId; + this.attributes.selectedViewConfigClientId = fixedSerializedAttributes.selectedViewConfigClientId; + + this.attributes.entityWidth = fixedSerializedAttributes.entityWidth; + + this._triggerModificationEventsIfNeeded(); + this._modificationPropagationEnabled = true; + }, + + _getNeighbour: function(configCollection, config, offset) { + var index = configCollection.indexOf(config); + if (index === -1) { + throw _.str.sprintf("Can't find config %s", JSON.stringify(config.serialize())); + } + return configCollection.at(index + offset); + }, + + _relocate: function(configCollection, config, indexOrNextConfigOrNextConfigClientId) { + var clientIds = _.pluck(configCollection.models, "cid"); + var nextConfigClientId = null; + + if (_.isNumber(indexOrNextConfigOrNextConfigClientId) && indexOrNextConfigOrNextConfigClientId != clientIds.length) { + nextConfigClientId = clientIds[indexOrNextConfigOrNextConfigClientId]; + } + if (_.isObject(indexOrNextConfigOrNextConfigClientId)) { + nextConfigClientId = indexOrNextConfigOrNextConfigClientId.getClientId(); + } + if (_.isString(indexOrNextConfigOrNextConfigClientId)) { + if (clientIds.indexOf(indexOrNextConfigOrNextConfigClientId) !== -1) { + nextConfigClientId = indexOrNextConfigOrNextConfigClientId; + } + } + if (!nextConfigClientId && !_.isNull(indexOrNextConfigOrNextConfigClientId) && indexOrNextConfigOrNextConfigClientId != configCollection.size()) { + throw _.str.sprintf("Wrong value for indexOrNextConfigOrNextConfigClientId %s", indexOrNextConfigOrNextConfigClientId); + } + + var configClientId = config.getClientId(); + if (!configClientId || clientIds.indexOf(configClientId) == -1) { + var flattenedConfig = config; + if (_.isObject(flattenedConfig)) { + flattenedConfig = JSON.stringify(flattenedConfig); + } + throw _.str.sprintf("Config %s with cid %s is either not a Config or does not belong to a corresponding configCollection with cids [%s]", flattenedConfig, _.isObject(config ) ? config.getClientId() : undefined, clientIds.join(", ")); + } + if (configCollection.get(configClientId) !== config) { + throw _.str.sprintf("Config %s with cid %s is is a clone of what is stored in the grid. Relocation is not possible.", JSON.stringify(config), config.cid); + } + var newClientIds = _.without(clientIds, configClientId); + if (_.isNull(nextConfigClientId)) { + newClientIds.push(configClientId); + } else { + var nextConfigIndex = newClientIds.indexOf(nextConfigClientId); + //if (nextConfigIndex) + var tempClientIds = newClientIds.slice(0, nextConfigIndex); + tempClientIds.push(configClientId); + newClientIds = tempClientIds.concat(newClientIds.slice(nextConfigIndex)); + } + + var oldComparator = configCollection.comparator; + configCollection.comparator = function(model) { + return newClientIds.indexOf(model.getClientId()); + }; + configCollection.sort(); + configCollection.comparator = oldComparator; + }, + + _registerModificationOfConfigCollectionForEntities: function(modelOrModels, options) { + if (!_.isEqual(this._lastSavedOrderedEntityClientIds, _.pluck(this.attributes.entityConfigs.models, "cid"))) { + this._configCollectionsWereModified = true; + if (this._modificationPropagationEnabled) { + this._triggerModificationEventsIfNeeded(); + }; + } + }, + + _registerModificationOfConfigCollectionForViews: function(modelOrModels, options) { + if (!_.isEqual(this._lastSavedOrderedViewClientIds, _.pluck(this.attributes.viewConfigs.models, "cid"))) { + this._configCollectionsWereModified = true; + if (this._modificationPropagationEnabled) { + this._triggerModificationEventsIfNeeded(); + }; + } + }, + + _registerModificationOfConfig: function() { + for (var i = 0; i < this.attributes.entityConfigs.length; i++) { + if(this.attributes.entityConfigs.at(i).hasChanged()) { + this._configsWereModified = true; + this._modifiedEntityConfigClientIds.push(this.attributes.entityConfigs.at(i).getClientId()); + } + } + for (var i = 0; i < this.attributes.viewConfigs.length; i++) { + if(this.attributes.viewConfigs.at(i).hasChanged()) { + this._configsWereModified = true; + this._modifiedViewConfigClientIds.push(this.attributes.viewConfigs.at(i).getClientId()); + } + } + if (this._modificationPropagationEnabled) { + this._triggerModificationEventsIfNeeded(); + }; + }, + + _registerModificationOfAtomicProperty: function() { + if (this._modificationPropagationEnabled) { + this._triggerModificationEventsIfNeeded(true); + }; + }, + + _registerModificationOfStandardAtomicProperty: function() { + if (this._modificationPropagationEnabled) { + this._triggerModificationEventsIfNeeded(); + }; + }, + + _triggerModificationEventsIfNeeded: function(specialCaseForRegisterModificationOfSelection) { + + var triggeredAtLeastSomething = false; + if (this._configCollectionsWereModified) { + triggeredAtLeastSomething = true; + this.trigger("change_layout"); + } + + var newOrderedEntityClientIds = null; + var newOrderedViewClientIds = null; + if (this._configCollectionsWereModified || this._modifiedEntityConfigClientIds.length || this._modifiedViewConfigClientIds.length) { + newOrderedEntityClientIds = _.pluck(this.attributes.entityConfigs.models, "cid"); + newOrderedViewClientIds = _.pluck(this.attributes.viewConfigs.models, "cid"); + + // change_entity:c123 + if (this._modifiedEntityConfigClientIds.length) { + for (var i = 0; i < newOrderedEntityClientIds.length; i++) { + if (_.indexOf(this._modifiedEntityConfigClientIds, newOrderedEntityClientIds[i]) !== -1) { + triggeredAtLeastSomething = true; + this.trigger("change_entity:" + newOrderedEntityClientIds[i]); + } + } + } + // change_view:c123 + if (this._modifiedViewConfigClientIds.length) { + for (var i = 0; i < newOrderedViewClientIds.length; i++) { + if (_.indexOf(this._modifiedViewConfigClientIds, newOrderedViewClientIds[i]) !== -1) { + triggeredAtLeastSomething = true; + this.trigger("change_view:" + newOrderedViewClientIds[i]); + } + } + } + + // change_entity_neighbours:c123 + for (var i = 0; i < newOrderedEntityClientIds.length; i++) { + var entityClientId = newOrderedEntityClientIds[i]; + var oldEntityIndex = this._lastSavedOrderedEntityClientIds.indexOf(entityClientId); + if (oldEntityIndex == -1) { + continue; + } + var newPrevEntityClientId = newOrderedEntityClientIds[i - 1]; + var newNextEntityClientId = newOrderedEntityClientIds[i + 1]; + var oldPrevEntityClientId = this._lastSavedOrderedEntityClientIds[oldEntityIndex - 1]; + var oldNextEntityClientId = this._lastSavedOrderedEntityClientIds[oldEntityIndex + 1]; + + if (newPrevEntityClientId != oldPrevEntityClientId + || newNextEntityClientId != oldNextEntityClientId + || this._modifiedEntityConfigClientIds.indexOf(newPrevEntityClientId) !== -1 + || this._modifiedEntityConfigClientIds.indexOf(newNextEntityClientId) !== -1 + ) { + triggeredAtLeastSomething = true; + this.trigger("change_entity_neighbours:" + entityClientId); + }; + } + + // change_view_neighbours:c123 + for (var i = 0; i < newOrderedViewClientIds.length; i++) { + var viewClientId = newOrderedViewClientIds[i]; + var oldViewIndex = this._lastSavedOrderedViewClientIds.indexOf(viewClientId); + if (oldViewIndex == -1) { + continue; + } + var newPrevViewClientId = newOrderedViewClientIds[i - 1]; + var newNextViewClientId = newOrderedViewClientIds[i + 1]; + var oldPrevViewClientId = this._lastSavedOrderedViewClientIds[oldViewIndex - 1]; + var oldNextViewClientId = this._lastSavedOrderedViewClientIds[oldViewIndex + 1]; + + if (newPrevViewClientId != oldPrevViewClientId + || newNextViewClientId != oldNextViewClientId + || this._modifiedViewConfigClientIds.indexOf(newPrevViewClientId) !== -1 + || this._modifiedViewConfigClientIds.indexOf(newNextViewClientId) !== -1 + ) { + triggeredAtLeastSomething = true; + this.trigger("change_view_neighbours:" + viewClientId); + }; + }; + } + + this._lastSavedOrderedEntityClientIds = _.pluck(this.attributes.entityConfigs.models, "cid"); + this._lastSavedOrderedViewClientIds = _.pluck(this.attributes.viewConfigs.models, "cid"); + + // Fix selection + if (!this.attributes.entityConfigs.get(this.attributes.selectedEntityConfigClientId)) { + this.attributes.selectedEntityConfigClientId = null; + } + if (!this.attributes.viewConfigs.get(this.attributes.selectedViewConfigClientId)) { + this.attributes.selectedViewConfigClientId = null; + } + if (this._lastSavedSelectedEntityConfigClientId != this.attributes.selectedEntityConfigClientId + || this._lastSavedSelectedViewConfigClientId != this.attributes.selectedViewConfigClientId + ) { + if (!specialCaseForRegisterModificationOfSelection) { + triggeredAtLeastSomething = true; + } + this.trigger("change_selection"); + } + + if (this._lastSavedEntityWidth != this.attributes.entityWidth) { + triggeredAtLeastSomething = true; + } + + this._configCollectionsWereModified = false; + this._configsWereModified = false; + this._selectionWasModified = false; + this._modifiedEntityConfigClientIds = []; + this._modifiedViewConfigClientIds = []; + this._lastSavedSelectedEntityConfigClientId = this.attributes.selectedEntityConfigClientId; + this._lastSavedSelectedViewConfigClientId = this.attributes.selectedViewConfigClientId; + this._lastSavedEntityWidth = this.attributes.entityWidth; + + if (triggeredAtLeastSomething) { + this.trigger("change"); + } + }, + }); + }); +}, Logger);