Mercurial > hg > dml-open-vis
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); |