comparison src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.01-Config.js @ 0:493bcb69166c

added public content
author Daniel Wolff
date Tue, 09 Feb 2016 20:54:02 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:493bcb69166c
1 "use strict";
2
3 App.module("ContextModule", function(ContextModule, App, Backbone, Marionette, $, _, Logger) {
4
5 // Define private variables
6 var logger = null;
7
8 ContextModule.addInitializer(function(options){
9
10 logger = Logger.get("ContextModule.Config");
11 logger.setLevel(Logger.WARN);
12
13 /**
14 * Config embraces common behaviour patterns of an object that can have pending changes –
15 * it consists of two attributes: parameters and plannedParameterUpdates. Both are simple
16 * Backbone objects (Models) with a list of scalar attributes.
17 *
18 * The ConfigObject itself is identified by its non-changeable clientId (getClientId())
19 * and can be serialized (dumped) and unserialized (restored).
20 *
21 * It is advised to listen to changes in the Config using "change", "change:parameters"
22 * and "change:plannedParameterUpdates" listener.
23 * It will be triggered each time when "parameters or "plannedParameterUpdates" attributes have been changed.
24 */
25 ContextModule.Config = Backbone.Model.extend({
26
27 // Client id prefix (cf123 instead of standard c123)
28 cidPrefix: "cf",
29
30 /**
31 * @memberOf App.ContextModule.Config
32 */
33 constructor: function(attributes, options) {
34 this._modificationPropagationEnabled = true;
35 this._parametersWereModified = false;
36 this._plannedParameterUpdatesWereModified = false;
37 this._cachedHashForTrimmedParameters = null;
38 this._cachedHashForParameters = null;
39 this._cachedHashForPlannedParameterUpdates = null;
40 this._cachedHashForPermanent = null;
41 this._cachedHashForTemp = null;
42 this._cachedHash = null;
43
44 var defaultParameters = (_.isSimpleObject(attributes) && _.isSimpleObject(attributes.parameters)) ? attributes.parameters : undefined;
45 var defaultPlannedParameterUpdates = (_.isSimpleObject(attributes) && _.isSimpleObject(attributes.plannedParameterUpdates)) ? attributes.plannedParameterUpdates : undefined;
46
47 var realAttributes = {};
48 realAttributes.parameters = new Backbone.Model(defaultParameters);
49 realAttributes.plannedParameterUpdates = new Backbone.Model(defaultPlannedParameterUpdates);
50
51 this.listenTo(realAttributes.parameters, "change", this._registerModificationOfParameters);
52 this.listenTo(realAttributes.plannedParameterUpdates, "change", this._registerModificationOfPlannedParameterUpdates);
53
54 Backbone.Model.apply(this, [realAttributes, options]);
55
56 if (attributes && attributes.clientId) {
57 this.cid = attributes.clientId;
58 _.markUniqueIdAsAlreadyUsed(attributes.clientId);
59 }
60 },
61
62 getClientId: function() {
63 return this.cid;
64 },
65
66 getDimension: function() {
67 return this.collection ? this.collection.dimension : undefined;
68 },
69 getConfigGridType: function() {
70 return this.collection ? this.collection.configGridType : undefined;
71 },
72
73 /**
74 * @memberOf App.ContextModule.Config
75 */
76 getParameterValue: function(parameterName) {
77 return this.attributes.parameters.attributes[parameterName];
78 },
79
80 /**
81 * @memberOf App.ContextModule.Config
82 */
83 getPlannedParameterValue: function(parameterName) {
84 var plannedParameterUpdatesAttributes = this.attributes.plannedParameterUpdates.attributes;
85 if (plannedParameterUpdatesAttributes.hasOwnProperty(parameterName)) {
86 return plannedParameterUpdatesAttributes[parameterName];
87 } else {
88 return this.attributes.parameters.attributes[parameterName];
89 }
90 },
91
92 /**
93 * @memberOf App.ContextModule.Config
94 *
95 * XXX clone parameter values?
96 */
97 getPlannedParameterValues: function(parameterName) {
98 var result = _.clone(this.attributes.parameters.attributes);
99 var plannedParameterUpdatesAttributes = this.attributes.plannedParameterUpdates.attributes;
100 for (var key in plannedParameterUpdatesAttributes) {
101 if (plannedParameterUpdatesAttributes.hasOwnProperty(key)) {
102 if (plannedParameterUpdatesAttributes[key] === undefined) {
103 if (result.hasOwnProperty(key)) {
104 delete result[key];
105 }
106 } else {
107 result[key] = plannedParameterUpdatesAttributes[key];
108 }
109 }
110 }
111 return result;
112 },
113
114 /**
115 * @memberOf App.ContextModule.Config
116 */
117 isPlannedToUpdate: function(parameterName) {
118 return this.attributes.plannedParameterUpdates.attributes.hasOwnProperty(parameterName);
119 // var parameterValue = this.getParameterValue(parameterName);
120 // var plannedParameterValue = this.getPlannedParameterValue(parameterName);
121 // return (parameterValue !== plannedParameterValue);
122 },
123
124 /**
125 * @memberOf App.ContextModule.Config
126 */
127 hasPlannedParameterUpdates: function() {
128 return _.size(this.attributes.plannedParameterUpdates.attributes) > 0;
129 },
130
131 /**
132 * @memberOf App.ContextModule.Config
133 */
134 updateParameter: function(parameterName, parameterValue) {
135 if (!_.isString(parameterName)) {
136 throw _.str.sprintf("Config::updateParameter called a non-string parameterName: %s", parameterName);
137 }
138 var prevModificationPropagationEnabled = this._modificationPropagationEnabled;
139 this._modificationPropagationEnabled = false;
140 this.attributes.plannedParameterUpdates.unset(parameterName);
141 if (typeof parameterValue !== "undefined") {
142 this.attributes.parameters.set(parameterName, parameterValue);
143 } else {
144 this.attributes.parameters.unset(parameterName);
145 }
146 if (prevModificationPropagationEnabled) {
147 this._triggerModificationEventsIfNeeded();
148 this._modificationPropagationEnabled = true;
149 }
150 },
151
152 /**
153 * @memberOf App.ContextModule.Config
154 */
155 planParameterUpdate: function(parameterName, parameterValue) {
156 if (!_.isString(parameterName)) {
157 throw _.str.sprintf("Config::planParameterUpdate called a non-string parameterName: %s", parameterName);
158 }
159 var prevModificationPropagationEnabled = this._modificationPropagationEnabled;
160
161 this._modificationPropagationEnabled = false;
162 var plannedParameterUpdatesAttributes = this.attributes.plannedParameterUpdates.attributes;
163 var parametersAttributes = this.attributes.parameters.attributes;
164 if (parameterValue === parametersAttributes[parameterName]) {
165 // special case: backbone won't fire a change event without this hack (due to how _.isEqual works)
166 if (this.attributes.plannedParameterUpdates.attributes.hasOwnProperty(parameterName) && this.attributes.plannedParameterUpdates.attributes[parameterName] === undefined) {
167 this.attributes.plannedParameterUpdates.set(parameterName, 42, {silent: true});
168 }
169 this.attributes.plannedParameterUpdates.unset(parameterName);
170 } else {
171 // special case: backbone won't fire a change event without this hack (due to how _.isEqual works)
172 if (parameterValue === undefined && this.attributes.parameters.attributes.hasOwnProperty(parameterName) && !this.attributes.plannedParameterUpdates.attributes.hasOwnProperty(parameterName)) {
173 this.attributes.plannedParameterUpdates.set(parameterName, 42, {silent: true});
174 }
175 this.attributes.plannedParameterUpdates.set(parameterName, parameterValue);
176 }
177
178 if (prevModificationPropagationEnabled) {
179 this._triggerModificationEventsIfNeeded();
180 this._modificationPropagationEnabled = true;
181 }
182 },
183
184 /**
185 * @memberOf App.ContextModule.Config
186 */
187 cancelPlannedParameterUpdate: function(parameterName) {
188 if (!_.isString(parameterName)) {
189 throw _.str.sprintf("Config::cancelPlannedParameterUpdate called a non-string parameterName: %s", parameterName);
190 }
191 var prevModificationPropagationEnabled = this._modificationPropagationEnabled;
192 this._modificationPropagationEnabled = false;
193
194 this.attributes.plannedParameterUpdates.unset(parameterName);
195 if (prevModificationPropagationEnabled) {
196 this._triggerModificationEventsIfNeeded();
197 this._modificationPropagationEnabled = true;
198 }
199 },
200
201 /**
202 * @memberOf App.ContextModule.Config
203 */
204 updateParameters: function(parameters) {
205 if (!_.isSimpleObject(parameters)) {
206 throw _.str.sprintf("Config::updateParameters called a wrong argument: %s", parameters);
207 }
208 this._modificationPropagationEnabled = false;
209
210 for (var parameterName in parameters) {
211 if (parameters.hasOwnProperty(parameterName)) {
212 this.updateParameter(parameterName, parameters[parameterName]);
213 }
214 }
215
216 this._triggerModificationEventsIfNeeded();
217 this._modificationPropagationEnabled = true;
218 },
219
220 /**
221 * @memberOf App.ContextModule.Config
222 */
223 planParameterUpdates: function(parameters) {
224 if (!_.isSimpleObject(parameters)) {
225 throw _.str.sprintf("Config::planParameterUpdates called with a wrong argument: %s", parameters);
226 }
227 this._modificationPropagationEnabled = false;
228
229 for (var parameterName in parameters) {
230 if (parameters.hasOwnProperty(parameterName)) {
231 this.planParameterUpdate(parameterName, parameters[parameterName]);
232 }
233 }
234
235 this._triggerModificationEventsIfNeeded();
236 this._modificationPropagationEnabled = true;
237 },
238
239 /**
240 * @memberOf App.ContextModule.Config
241 */
242 cancelPlannedParameterUpdates: function(parameterNames) {
243 if (_.isArray(parameterNames)) {
244 this._modificationPropagationEnabled = false;
245
246 for (var i = 0; i < parameterNames.length; i++) {
247 this.cancelPlannedParameterUpdate(parameterNames[i]);
248 }
249
250 this._triggerModificationEventsIfNeeded();
251 this._modificationPropagationEnabled = true;
252 } else if (!_.isUndefined(parameterNames)) {
253 throw _.str.sprintf("Config::planParameterUpdates called a non-string parameters: %s", parameters);
254 } else {
255 if (_.keys(this.attributes.plannedParameterUpdates.attributes).length) {
256 this.attributes.plannedParameterUpdates.attributes.fix = 42;
257 }
258 this.attributes.plannedParameterUpdates.clear();
259 }
260 },
261
262 /**
263 * @memberOf App.ContextModule.Config
264 */
265 applyPlannedParameterUpdates: function() {
266
267 // Combine parameters with planned updates
268 var newParameters = _.extend(this.attributes.parameters.toJSON(), this.attributes.plannedParameterUpdates.toJSON());
269
270 // Remove "undefined" from the new parameters
271 for (var key in newParameters) {
272 if (!newParameters.hasOwnProperty(key)) continue;
273
274 if (typeof newParameters[key] === "undefined") {
275 delete newParameters[key];
276 }
277 }
278
279 // If there are any existing keys with "undefined" within plannedParameterUpdates,
280 // replace "undefined" with some value to make sure change:plannedParameterUpdates is triggered
281 // Another part of this hack is in "planParameterUpdate" method
282 var attributesInPlannedParameterUpdates = this.attributes.plannedParameterUpdates.attributes;
283 for (var key in attributesInPlannedParameterUpdates) {
284 if (attributesInPlannedParameterUpdates[key] === undefined) {
285 attributesInPlannedParameterUpdates[key] = 42;
286 break;
287 }
288 }
289
290 // Assign parameters and clear planned updates
291 this.unserialize({
292 clientId: this.cid,
293 parameters: newParameters,
294 plannedParameterUpdates: {}
295 });
296 },
297
298 /**
299 * @memberOf App.ContextModule.Config
300 */
301 serialize: function() {
302 var result = {
303 clientId: this.cid,
304 parameters: this.attributes.parameters.toJSON(),
305 plannedParameterUpdates: this.attributes.plannedParameterUpdates.toJSON()
306 };
307
308 return result;
309 },
310
311 /**
312 * @memberOf App.ContextModule.Config
313 */
314 unserialize: function(serializedAttributes) {
315 var fixedSerializedAttributes = serializedAttributes;
316 if (!_.isSimpleObject(serializedAttributes)) {
317 logger.warn("Config::unserialize called for not an object: ", serializedAttributes);
318 fixedSerializedAttributes = {};
319 }
320
321 if (this.cid != fixedSerializedAttributes.clientId && !_.isUndefined(fixedSerializedAttributes.clientId)) {
322 throw _.str.sprintf("Parameter bag client id (%s) is not equal to the client id of the serialized object (%s).", this.cid, fixedSerializedAttributes.clientId);
323 }
324
325 this._parametersWereModified = false;
326 this._plannedParameterUpdatesWereModified = false;
327 this._modificationPropagationEnabled = false;
328
329 var fixedSerializedPlannedParameterUpdates = fixedSerializedAttributes.plannedParameterUpdates;
330 if (!_.isSimpleObject(fixedSerializedPlannedParameterUpdates)) {
331 if (_.isSimpleObject(serializedAttributes)) {
332 logger.warn("Config::unserialize called for object with faulty plannedParameterUpdates: ", fixedSerializedPlannedParameterUpdates);
333 }
334 fixedSerializedPlannedParameterUpdates = {};
335 }
336 if (!_.isEqual(this.attributes.plannedParameterUpdates.attributes, fixedSerializedPlannedParameterUpdates)) {
337 this.attributes.plannedParameterUpdates
338 .set("fix", 42, {silent: true})
339 .clear()
340 .set(fixedSerializedPlannedParameterUpdates);
341 }
342
343 var fixedSerializedParameters = fixedSerializedAttributes.parameters;
344 if (!_.isSimpleObject(fixedSerializedParameters)) {
345 if (_.isSimpleObject(serializedAttributes)) {
346 logger.warn("Config::unserialize called for object with faulty parameters: ", fixedSerializedParameters);
347 }
348 fixedSerializedParameters = {};
349 }
350 if (!_.isEqual(this.attributes.parameters.toJSON(), fixedSerializedParameters)) {
351 this.attributes.parameters
352 .clear()
353 .set(fixedSerializedParameters);
354 }
355
356 this._triggerModificationEventsIfNeeded();
357 this._modificationPropagationEnabled = true;
358 },
359
360 clone: function() {
361 var serializedAttributes = this.serialize();
362 delete serializedAttributes.clientId;
363 return new ContextModule.Config(serializedAttributes);
364 },
365
366 getHashForParameters: function() {
367 if (this._cachedHashForParameters === null) {
368 this._cachedHashForParameters = JSON.stringify(this.attributes.parameters.attributes);
369 }
370 return this._cachedHashForParameters;
371 },
372
373 getHashForTrimmedParameters: function() {
374 if (this._cachedHashForTrimmedParameters === null) {
375 var attributesToHash = _.clone(this.attributes.parameters.attributes);
376 for (var key in attributesToHash){
377 if (attributesToHash.hasOwnProperty(key) && _.isString(attributesToHash[key])) {
378 attributesToHash[key] = _.str.trim(attributesToHash[key]);
379 }
380 }
381 this._cachedHashForTrimmedParameters = JSON.stringify(attributesToHash);
382 }
383 return this._cachedHashForTrimmedParameters;
384 },
385
386 getHashForPlannedParameterUpdates: function() {
387 if (this._cachedHashForPlannedParameterUpdates === null) {
388 var attributes = this.attributes.plannedParameterUpdates.attributes;
389 this._cachedHashForPlannedParameterUpdates = JSON.stringify(attributes);
390 // special treatment of undefined is needed here
391 // see http://stackoverflow.com/questions/26540706/json-stringify-removes-hash-keys-with-undefined-values
392 for (var key in attributes) {
393 if (attributes.hasOwnProperty(key) && attributes[key] === undefined) {
394 this._cachedHashForPlannedParameterUpdates += key + "|";
395 }
396 }
397 }
398 return this._cachedHashForPlannedParameterUpdates;
399 },
400
401 getHashForPermanent: function() {
402 if (this._cachedHashForPermanent === null) {
403 this._cachedHashForPermanent = this.getHashForParameters() + this.getHashForPlannedParameterUpdates();
404 }
405 return this._cachedHashForPermanent;
406 },
407
408 getHashForTemp: function() {
409 if (this._cachedHashForTemp === null) {
410 this._cachedHashForTemp = "";
411 }
412 return this._cachedHashForTemp;
413 },
414
415 getHash: function() {
416 if (!this._cachedHash) {
417 this._cachedHash = this.getHashForPermanent() + this.getHashForTemp();
418 }
419 return this._cachedHash;
420 },
421
422 _registerModificationOfParameters: function() {
423 this._cachedHashForParameters = null;
424 this._cachedHashForTrimmedParameters = null;
425 this._cachedHashForPermanent = null;
426 this._cachedHash = null;
427
428 this._parametersWereModified = true;
429 if (this._modificationPropagationEnabled) {
430 this._triggerModificationEventsIfNeeded();
431 };
432 },
433
434 _registerModificationOfPlannedParameterUpdates: function() {
435 this._cachedHashForPlannedParameterUpdates = null;
436 this._cachedHashForPermanent = null;
437 this._cachedHash = null;
438
439 this._plannedParameterUpdatesWereModified = true;
440 if (this._modificationPropagationEnabled) {
441 this._triggerModificationEventsIfNeeded();
442 };
443 },
444
445 _triggerModificationEventsIfNeeded: function() {
446 this.changed = [true];
447 if (this._parametersWereModified) {
448 this.trigger("change:parameters");
449 }
450 if (this._plannedParameterUpdatesWereModified) {
451 this.trigger("change:plannedParameterUpdates");
452 }
453 if (this._tempParametersWereModified) {
454 this.trigger("change:tempParameters");
455 }
456
457 if (this._parametersWereModified || this._plannedParameterUpdatesWereModified) {
458 this.trigger("change:parametersOrPlannedParameterUpdates");
459 this.trigger("change");
460 }
461 this._parametersWereModified = false;
462 this._plannedParameterUpdatesWereModified = false;
463 this._tempParametersWereModified = false;
464 this.changed = null;
465 },
466 });
467 });
468 }, Logger);