Mercurial > hg > dml-open-vis
view src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.01-Config.js @ 1:f38015048f48 tip
Added GPL
author | Daniel Wolff |
---|---|
date | Sat, 13 Feb 2016 20:43:38 +0100 |
parents | 493bcb69166c |
children |
line wrap: on
line source
"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.Config"); logger.setLevel(Logger.WARN); /** * Config embraces common behaviour patterns of an object that can have pending changes – * it consists of two attributes: parameters and plannedParameterUpdates. Both are simple * Backbone objects (Models) with a list of scalar attributes. * * The ConfigObject itself is identified by its non-changeable clientId (getClientId()) * and can be serialized (dumped) and unserialized (restored). * * It is advised to listen to changes in the Config using "change", "change:parameters" * and "change:plannedParameterUpdates" listener. * It will be triggered each time when "parameters or "plannedParameterUpdates" attributes have been changed. */ ContextModule.Config = Backbone.Model.extend({ // Client id prefix (cf123 instead of standard c123) cidPrefix: "cf", /** * @memberOf App.ContextModule.Config */ constructor: function(attributes, options) { this._modificationPropagationEnabled = true; this._parametersWereModified = false; this._plannedParameterUpdatesWereModified = false; this._cachedHashForTrimmedParameters = null; this._cachedHashForParameters = null; this._cachedHashForPlannedParameterUpdates = null; this._cachedHashForPermanent = null; this._cachedHashForTemp = null; this._cachedHash = null; var defaultParameters = (_.isSimpleObject(attributes) && _.isSimpleObject(attributes.parameters)) ? attributes.parameters : undefined; var defaultPlannedParameterUpdates = (_.isSimpleObject(attributes) && _.isSimpleObject(attributes.plannedParameterUpdates)) ? attributes.plannedParameterUpdates : undefined; var realAttributes = {}; realAttributes.parameters = new Backbone.Model(defaultParameters); realAttributes.plannedParameterUpdates = new Backbone.Model(defaultPlannedParameterUpdates); this.listenTo(realAttributes.parameters, "change", this._registerModificationOfParameters); this.listenTo(realAttributes.plannedParameterUpdates, "change", this._registerModificationOfPlannedParameterUpdates); Backbone.Model.apply(this, [realAttributes, options]); if (attributes && attributes.clientId) { this.cid = attributes.clientId; _.markUniqueIdAsAlreadyUsed(attributes.clientId); } }, getClientId: function() { return this.cid; }, getDimension: function() { return this.collection ? this.collection.dimension : undefined; }, getConfigGridType: function() { return this.collection ? this.collection.configGridType : undefined; }, /** * @memberOf App.ContextModule.Config */ getParameterValue: function(parameterName) { return this.attributes.parameters.attributes[parameterName]; }, /** * @memberOf App.ContextModule.Config */ getPlannedParameterValue: function(parameterName) { var plannedParameterUpdatesAttributes = this.attributes.plannedParameterUpdates.attributes; if (plannedParameterUpdatesAttributes.hasOwnProperty(parameterName)) { return plannedParameterUpdatesAttributes[parameterName]; } else { return this.attributes.parameters.attributes[parameterName]; } }, /** * @memberOf App.ContextModule.Config * * XXX clone parameter values? */ getPlannedParameterValues: function(parameterName) { var result = _.clone(this.attributes.parameters.attributes); var plannedParameterUpdatesAttributes = this.attributes.plannedParameterUpdates.attributes; for (var key in plannedParameterUpdatesAttributes) { if (plannedParameterUpdatesAttributes.hasOwnProperty(key)) { if (plannedParameterUpdatesAttributes[key] === undefined) { if (result.hasOwnProperty(key)) { delete result[key]; } } else { result[key] = plannedParameterUpdatesAttributes[key]; } } } return result; }, /** * @memberOf App.ContextModule.Config */ isPlannedToUpdate: function(parameterName) { return this.attributes.plannedParameterUpdates.attributes.hasOwnProperty(parameterName); // var parameterValue = this.getParameterValue(parameterName); // var plannedParameterValue = this.getPlannedParameterValue(parameterName); // return (parameterValue !== plannedParameterValue); }, /** * @memberOf App.ContextModule.Config */ hasPlannedParameterUpdates: function() { return _.size(this.attributes.plannedParameterUpdates.attributes) > 0; }, /** * @memberOf App.ContextModule.Config */ updateParameter: function(parameterName, parameterValue) { if (!_.isString(parameterName)) { throw _.str.sprintf("Config::updateParameter called a non-string parameterName: %s", parameterName); } var prevModificationPropagationEnabled = this._modificationPropagationEnabled; this._modificationPropagationEnabled = false; this.attributes.plannedParameterUpdates.unset(parameterName); if (typeof parameterValue !== "undefined") { this.attributes.parameters.set(parameterName, parameterValue); } else { this.attributes.parameters.unset(parameterName); } if (prevModificationPropagationEnabled) { this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; } }, /** * @memberOf App.ContextModule.Config */ planParameterUpdate: function(parameterName, parameterValue) { if (!_.isString(parameterName)) { throw _.str.sprintf("Config::planParameterUpdate called a non-string parameterName: %s", parameterName); } var prevModificationPropagationEnabled = this._modificationPropagationEnabled; this._modificationPropagationEnabled = false; var plannedParameterUpdatesAttributes = this.attributes.plannedParameterUpdates.attributes; var parametersAttributes = this.attributes.parameters.attributes; if (parameterValue === parametersAttributes[parameterName]) { // special case: backbone won't fire a change event without this hack (due to how _.isEqual works) if (this.attributes.plannedParameterUpdates.attributes.hasOwnProperty(parameterName) && this.attributes.plannedParameterUpdates.attributes[parameterName] === undefined) { this.attributes.plannedParameterUpdates.set(parameterName, 42, {silent: true}); } this.attributes.plannedParameterUpdates.unset(parameterName); } else { // special case: backbone won't fire a change event without this hack (due to how _.isEqual works) if (parameterValue === undefined && this.attributes.parameters.attributes.hasOwnProperty(parameterName) && !this.attributes.plannedParameterUpdates.attributes.hasOwnProperty(parameterName)) { this.attributes.plannedParameterUpdates.set(parameterName, 42, {silent: true}); } this.attributes.plannedParameterUpdates.set(parameterName, parameterValue); } if (prevModificationPropagationEnabled) { this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; } }, /** * @memberOf App.ContextModule.Config */ cancelPlannedParameterUpdate: function(parameterName) { if (!_.isString(parameterName)) { throw _.str.sprintf("Config::cancelPlannedParameterUpdate called a non-string parameterName: %s", parameterName); } var prevModificationPropagationEnabled = this._modificationPropagationEnabled; this._modificationPropagationEnabled = false; this.attributes.plannedParameterUpdates.unset(parameterName); if (prevModificationPropagationEnabled) { this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; } }, /** * @memberOf App.ContextModule.Config */ updateParameters: function(parameters) { if (!_.isSimpleObject(parameters)) { throw _.str.sprintf("Config::updateParameters called a wrong argument: %s", parameters); } this._modificationPropagationEnabled = false; for (var parameterName in parameters) { if (parameters.hasOwnProperty(parameterName)) { this.updateParameter(parameterName, parameters[parameterName]); } } this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; }, /** * @memberOf App.ContextModule.Config */ planParameterUpdates: function(parameters) { if (!_.isSimpleObject(parameters)) { throw _.str.sprintf("Config::planParameterUpdates called with a wrong argument: %s", parameters); } this._modificationPropagationEnabled = false; for (var parameterName in parameters) { if (parameters.hasOwnProperty(parameterName)) { this.planParameterUpdate(parameterName, parameters[parameterName]); } } this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; }, /** * @memberOf App.ContextModule.Config */ cancelPlannedParameterUpdates: function(parameterNames) { if (_.isArray(parameterNames)) { this._modificationPropagationEnabled = false; for (var i = 0; i < parameterNames.length; i++) { this.cancelPlannedParameterUpdate(parameterNames[i]); } this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; } else if (!_.isUndefined(parameterNames)) { throw _.str.sprintf("Config::planParameterUpdates called a non-string parameters: %s", parameters); } else { if (_.keys(this.attributes.plannedParameterUpdates.attributes).length) { this.attributes.plannedParameterUpdates.attributes.fix = 42; } this.attributes.plannedParameterUpdates.clear(); } }, /** * @memberOf App.ContextModule.Config */ applyPlannedParameterUpdates: function() { // Combine parameters with planned updates var newParameters = _.extend(this.attributes.parameters.toJSON(), this.attributes.plannedParameterUpdates.toJSON()); // Remove "undefined" from the new parameters for (var key in newParameters) { if (!newParameters.hasOwnProperty(key)) continue; if (typeof newParameters[key] === "undefined") { delete newParameters[key]; } } // If there are any existing keys with "undefined" within plannedParameterUpdates, // replace "undefined" with some value to make sure change:plannedParameterUpdates is triggered // Another part of this hack is in "planParameterUpdate" method var attributesInPlannedParameterUpdates = this.attributes.plannedParameterUpdates.attributes; for (var key in attributesInPlannedParameterUpdates) { if (attributesInPlannedParameterUpdates[key] === undefined) { attributesInPlannedParameterUpdates[key] = 42; break; } } // Assign parameters and clear planned updates this.unserialize({ clientId: this.cid, parameters: newParameters, plannedParameterUpdates: {} }); }, /** * @memberOf App.ContextModule.Config */ serialize: function() { var result = { clientId: this.cid, parameters: this.attributes.parameters.toJSON(), plannedParameterUpdates: this.attributes.plannedParameterUpdates.toJSON() }; return result; }, /** * @memberOf App.ContextModule.Config */ unserialize: function(serializedAttributes) { var fixedSerializedAttributes = serializedAttributes; if (!_.isSimpleObject(serializedAttributes)) { logger.warn("Config::unserialize called for not an object: ", serializedAttributes); fixedSerializedAttributes = {}; } if (this.cid != fixedSerializedAttributes.clientId && !_.isUndefined(fixedSerializedAttributes.clientId)) { throw _.str.sprintf("Parameter bag client id (%s) is not equal to the client id of the serialized object (%s).", this.cid, fixedSerializedAttributes.clientId); } this._parametersWereModified = false; this._plannedParameterUpdatesWereModified = false; this._modificationPropagationEnabled = false; var fixedSerializedPlannedParameterUpdates = fixedSerializedAttributes.plannedParameterUpdates; if (!_.isSimpleObject(fixedSerializedPlannedParameterUpdates)) { if (_.isSimpleObject(serializedAttributes)) { logger.warn("Config::unserialize called for object with faulty plannedParameterUpdates: ", fixedSerializedPlannedParameterUpdates); } fixedSerializedPlannedParameterUpdates = {}; } if (!_.isEqual(this.attributes.plannedParameterUpdates.attributes, fixedSerializedPlannedParameterUpdates)) { this.attributes.plannedParameterUpdates .set("fix", 42, {silent: true}) .clear() .set(fixedSerializedPlannedParameterUpdates); } var fixedSerializedParameters = fixedSerializedAttributes.parameters; if (!_.isSimpleObject(fixedSerializedParameters)) { if (_.isSimpleObject(serializedAttributes)) { logger.warn("Config::unserialize called for object with faulty parameters: ", fixedSerializedParameters); } fixedSerializedParameters = {}; } if (!_.isEqual(this.attributes.parameters.toJSON(), fixedSerializedParameters)) { this.attributes.parameters .clear() .set(fixedSerializedParameters); } this._triggerModificationEventsIfNeeded(); this._modificationPropagationEnabled = true; }, clone: function() { var serializedAttributes = this.serialize(); delete serializedAttributes.clientId; return new ContextModule.Config(serializedAttributes); }, getHashForParameters: function() { if (this._cachedHashForParameters === null) { this._cachedHashForParameters = JSON.stringify(this.attributes.parameters.attributes); } return this._cachedHashForParameters; }, getHashForTrimmedParameters: function() { if (this._cachedHashForTrimmedParameters === null) { var attributesToHash = _.clone(this.attributes.parameters.attributes); for (var key in attributesToHash){ if (attributesToHash.hasOwnProperty(key) && _.isString(attributesToHash[key])) { attributesToHash[key] = _.str.trim(attributesToHash[key]); } } this._cachedHashForTrimmedParameters = JSON.stringify(attributesToHash); } return this._cachedHashForTrimmedParameters; }, getHashForPlannedParameterUpdates: function() { if (this._cachedHashForPlannedParameterUpdates === null) { var attributes = this.attributes.plannedParameterUpdates.attributes; this._cachedHashForPlannedParameterUpdates = JSON.stringify(attributes); // special treatment of undefined is needed here // see http://stackoverflow.com/questions/26540706/json-stringify-removes-hash-keys-with-undefined-values for (var key in attributes) { if (attributes.hasOwnProperty(key) && attributes[key] === undefined) { this._cachedHashForPlannedParameterUpdates += key + "|"; } } } return this._cachedHashForPlannedParameterUpdates; }, getHashForPermanent: function() { if (this._cachedHashForPermanent === null) { this._cachedHashForPermanent = this.getHashForParameters() + this.getHashForPlannedParameterUpdates(); } return this._cachedHashForPermanent; }, getHashForTemp: function() { if (this._cachedHashForTemp === null) { this._cachedHashForTemp = ""; } return this._cachedHashForTemp; }, getHash: function() { if (!this._cachedHash) { this._cachedHash = this.getHashForPermanent() + this.getHashForTemp(); } return this._cachedHash; }, _registerModificationOfParameters: function() { this._cachedHashForParameters = null; this._cachedHashForTrimmedParameters = null; this._cachedHashForPermanent = null; this._cachedHash = null; this._parametersWereModified = true; if (this._modificationPropagationEnabled) { this._triggerModificationEventsIfNeeded(); }; }, _registerModificationOfPlannedParameterUpdates: function() { this._cachedHashForPlannedParameterUpdates = null; this._cachedHashForPermanent = null; this._cachedHash = null; this._plannedParameterUpdatesWereModified = true; if (this._modificationPropagationEnabled) { this._triggerModificationEventsIfNeeded(); }; }, _triggerModificationEventsIfNeeded: function() { this.changed = [true]; if (this._parametersWereModified) { this.trigger("change:parameters"); } if (this._plannedParameterUpdatesWereModified) { this.trigger("change:plannedParameterUpdates"); } if (this._tempParametersWereModified) { this.trigger("change:tempParameters"); } if (this._parametersWereModified || this._plannedParameterUpdatesWereModified) { this.trigger("change:parametersOrPlannedParameterUpdates"); this.trigger("change"); } this._parametersWereModified = false; this._plannedParameterUpdatesWereModified = false; this._tempParametersWereModified = false; this.changed = null; }, }); }); }, Logger);