knockoutjs-rails 2.2.0.1 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -18,9 +18,14 @@ Add the following directive to your Javascript manifest file (application.js):
18
18
 
19
19
  //= require knockout
20
20
 
21
+ You can optionally add the object mapping plugin for Knockout (also
22
+ application.js):
23
+
24
+ //= require knockout.mapping
25
+
21
26
  ## Versioning
22
27
 
23
- knockoutjs-rails 2.2.0 == Knockout.js 2.2.0
28
+ knockoutjs-rails 2.2.1 == Knockout.js 2.2.1
24
29
 
25
30
  Every attempt is made to mirror the currently shipping Knockout.js version number wherever possible.
26
31
  The major and minor version numbers will always represent the Knockout.js version, but the patch level
@@ -1,5 +1,5 @@
1
1
  module Knockoutjs
2
2
  module Rails
3
- VERSION = "2.2.0.1"
3
+ VERSION = "2.2.1"
4
4
  end
5
5
  end
@@ -1,4 +1,4 @@
1
- // Knockout JavaScript library v2.2.0
1
+ // Knockout JavaScript library v2.2.1
2
2
  // (c) Steven Sanderson - http://knockoutjs.com/
3
3
  // License: MIT (http://www.opensource.org/licenses/mit-license.php)
4
4
 
@@ -37,7 +37,7 @@ ko.exportSymbol = function(koPath, object) {
37
37
  ko.exportProperty = function(owner, publicName, object) {
38
38
  owner[publicName] = object;
39
39
  };
40
- ko.version = "2.2.0";
40
+ ko.version = "2.2.1";
41
41
 
42
42
  ko.exportSymbol('version', ko.version);
43
43
  ko.utils = new (function () {
@@ -703,22 +703,28 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
703
703
  }
704
704
 
705
705
  function jQueryHtmlParse(html) {
706
- var elems = jQuery['clean']([html]);
707
-
708
- // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
709
- // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
710
- // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
711
- if (elems && elems[0]) {
712
- // Find the top-most parent element that's a direct child of a document fragment
713
- var elem = elems[0];
714
- while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
715
- elem = elem.parentNode;
716
- // ... then detach it
717
- if (elem.parentNode)
718
- elem.parentNode.removeChild(elem);
719
- }
720
-
721
- return elems;
706
+ // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
707
+ if (jQuery['parseHTML']) {
708
+ return jQuery['parseHTML'](html);
709
+ } else {
710
+ // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
711
+ var elems = jQuery['clean']([html]);
712
+
713
+ // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
714
+ // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
715
+ // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
716
+ if (elems && elems[0]) {
717
+ // Find the top-most parent element that's a direct child of a document fragment
718
+ var elem = elems[0];
719
+ while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
720
+ elem = elem.parentNode;
721
+ // ... then detach it
722
+ if (elem.parentNode)
723
+ elem.parentNode.removeChild(elem);
724
+ }
725
+
726
+ return elems;
727
+ }
722
728
  }
723
729
 
724
730
  ko.utils.parseHtmlFragment = function(html) {
@@ -0,0 +1,809 @@
1
+ /// Knockout Mapping plugin v2.4.1
2
+ /// (c) 2013 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/
3
+ /// License: MIT (http://www.opensource.org/licenses/mit-license.php)
4
+ (function (factory) {
5
+ // Module systems magic dance.
6
+
7
+ if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
8
+ // CommonJS or Node: hard-coded dependency on "knockout"
9
+ factory(require("knockout"), exports);
10
+ } else if (typeof define === "function" && define["amd"]) {
11
+ // AMD anonymous module with hard-coded dependency on "knockout"
12
+ define(["knockout", "exports"], factory);
13
+ } else {
14
+ // <script> tag: use the global `ko` object, attaching a `mapping` property
15
+ factory(ko, ko.mapping = {});
16
+ }
17
+ }(function (ko, exports) {
18
+ var DEBUG=true;
19
+ var mappingProperty = "__ko_mapping__";
20
+ var realKoDependentObservable = ko.dependentObservable;
21
+ var mappingNesting = 0;
22
+ var dependentObservables;
23
+ var visitedObjects;
24
+ var recognizedRootProperties = ["create", "update", "key", "arrayChanged"];
25
+ var emptyReturn = {};
26
+
27
+ var _defaultOptions = {
28
+ include: ["_destroy"],
29
+ ignore: [],
30
+ copy: [],
31
+ observe: []
32
+ };
33
+ var defaultOptions = _defaultOptions;
34
+
35
+ // Author: KennyTM @ StackOverflow
36
+ function unionArrays (x, y) {
37
+ var obj = {};
38
+ for (var i = x.length - 1; i >= 0; -- i) obj[x[i]] = x[i];
39
+ for (var i = y.length - 1; i >= 0; -- i) obj[y[i]] = y[i];
40
+ var res = [];
41
+
42
+ for (var k in obj) {
43
+ res.push(obj[k]);
44
+ };
45
+
46
+ return res;
47
+ }
48
+
49
+ function extendObject(destination, source) {
50
+ var destType;
51
+
52
+ for (var key in source) {
53
+ if (source.hasOwnProperty(key) && source[key]) {
54
+ destType = exports.getType(destination[key]);
55
+ if (key && destination[key] && destType !== "array" && destType !== "string") {
56
+ extendObject(destination[key], source[key]);
57
+ } else {
58
+ var bothArrays = exports.getType(destination[key]) === "array" && exports.getType(source[key]) === "array";
59
+ if (bothArrays) {
60
+ destination[key] = unionArrays(destination[key], source[key]);
61
+ } else {
62
+ destination[key] = source[key];
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ function merge(obj1, obj2) {
70
+ var merged = {};
71
+ extendObject(merged, obj1);
72
+ extendObject(merged, obj2);
73
+
74
+ return merged;
75
+ }
76
+
77
+ exports.isMapped = function (viewModel) {
78
+ var unwrapped = ko.utils.unwrapObservable(viewModel);
79
+ return unwrapped && unwrapped[mappingProperty];
80
+ }
81
+
82
+ exports.fromJS = function (jsObject /*, inputOptions, target*/ ) {
83
+ if (arguments.length == 0) throw new Error("When calling ko.fromJS, pass the object you want to convert.");
84
+
85
+ try {
86
+ if (!mappingNesting++) {
87
+ dependentObservables = [];
88
+ visitedObjects = new objectLookup();
89
+ }
90
+
91
+ var options;
92
+ var target;
93
+
94
+ if (arguments.length == 2) {
95
+ if (arguments[1][mappingProperty]) {
96
+ target = arguments[1];
97
+ } else {
98
+ options = arguments[1];
99
+ }
100
+ }
101
+ if (arguments.length == 3) {
102
+ options = arguments[1];
103
+ target = arguments[2];
104
+ }
105
+
106
+ if (target) {
107
+ options = merge(options, target[mappingProperty]);
108
+ }
109
+ options = fillOptions(options);
110
+
111
+ var result = updateViewModel(target, jsObject, options);
112
+ if (target) {
113
+ result = target;
114
+ }
115
+
116
+ // Evaluate any dependent observables that were proxied.
117
+ // Do this after the model's observables have been created
118
+ if (!--mappingNesting) {
119
+ while (dependentObservables.length) {
120
+ var DO = dependentObservables.pop();
121
+ if (DO) {
122
+ DO();
123
+
124
+ // Move this magic property to the underlying dependent observable
125
+ DO.__DO["throttleEvaluation"] = DO["throttleEvaluation"];
126
+ }
127
+ }
128
+ }
129
+
130
+ // Save any new mapping options in the view model, so that updateFromJS can use them later.
131
+ result[mappingProperty] = merge(result[mappingProperty], options);
132
+
133
+ return result;
134
+ } catch(e) {
135
+ mappingNesting = 0;
136
+ throw e;
137
+ }
138
+ };
139
+
140
+ exports.fromJSON = function (jsonString /*, options, target*/ ) {
141
+ var parsed = ko.utils.parseJson(jsonString);
142
+ arguments[0] = parsed;
143
+ return exports.fromJS.apply(this, arguments);
144
+ };
145
+
146
+ exports.updateFromJS = function (viewModel) {
147
+ throw new Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!");
148
+ };
149
+
150
+ exports.updateFromJSON = function (viewModel) {
151
+ throw new Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!");
152
+ };
153
+
154
+ exports.toJS = function (rootObject, options) {
155
+ if (!defaultOptions) exports.resetDefaultOptions();
156
+
157
+ if (arguments.length == 0) throw new Error("When calling ko.mapping.toJS, pass the object you want to convert.");
158
+ if (exports.getType(defaultOptions.ignore) !== "array") throw new Error("ko.mapping.defaultOptions().ignore should be an array.");
159
+ if (exports.getType(defaultOptions.include) !== "array") throw new Error("ko.mapping.defaultOptions().include should be an array.");
160
+ if (exports.getType(defaultOptions.copy) !== "array") throw new Error("ko.mapping.defaultOptions().copy should be an array.");
161
+
162
+ // Merge in the options used in fromJS
163
+ options = fillOptions(options, rootObject[mappingProperty]);
164
+
165
+ // We just unwrap everything at every level in the object graph
166
+ return exports.visitModel(rootObject, function (x) {
167
+ return ko.utils.unwrapObservable(x)
168
+ }, options);
169
+ };
170
+
171
+ exports.toJSON = function (rootObject, options) {
172
+ var plainJavaScriptObject = exports.toJS(rootObject, options);
173
+ return ko.utils.stringifyJson(plainJavaScriptObject);
174
+ };
175
+
176
+ exports.defaultOptions = function () {
177
+ if (arguments.length > 0) {
178
+ defaultOptions = arguments[0];
179
+ } else {
180
+ return defaultOptions;
181
+ }
182
+ };
183
+
184
+ exports.resetDefaultOptions = function () {
185
+ defaultOptions = {
186
+ include: _defaultOptions.include.slice(0),
187
+ ignore: _defaultOptions.ignore.slice(0),
188
+ copy: _defaultOptions.copy.slice(0)
189
+ };
190
+ };
191
+
192
+ exports.getType = function(x) {
193
+ if ((x) && (typeof (x) === "object")) {
194
+ if (x.constructor === Date) return "date";
195
+ if (x.constructor === Array) return "array";
196
+ }
197
+ return typeof x;
198
+ }
199
+
200
+ function fillOptions(rawOptions, otherOptions) {
201
+ var options = merge({}, rawOptions);
202
+
203
+ // Move recognized root-level properties into a root namespace
204
+ for (var i = recognizedRootProperties.length - 1; i >= 0; i--) {
205
+ var property = recognizedRootProperties[i];
206
+
207
+ // Carry on, unless this property is present
208
+ if (!options[property]) continue;
209
+
210
+ // Move the property into the root namespace
211
+ if (!(options[""] instanceof Object)) options[""] = {};
212
+ options[""][property] = options[property];
213
+ delete options[property];
214
+ }
215
+
216
+ if (otherOptions) {
217
+ options.ignore = mergeArrays(otherOptions.ignore, options.ignore);
218
+ options.include = mergeArrays(otherOptions.include, options.include);
219
+ options.copy = mergeArrays(otherOptions.copy, options.copy);
220
+ options.observe = mergeArrays(otherOptions.observe, options.observe);
221
+ }
222
+ options.ignore = mergeArrays(options.ignore, defaultOptions.ignore);
223
+ options.include = mergeArrays(options.include, defaultOptions.include);
224
+ options.copy = mergeArrays(options.copy, defaultOptions.copy);
225
+ options.observe = mergeArrays(options.observe, defaultOptions.observe);
226
+
227
+ options.mappedProperties = options.mappedProperties || {};
228
+ options.copiedProperties = options.copiedProperties || {};
229
+ return options;
230
+ }
231
+
232
+ function mergeArrays(a, b) {
233
+ if (exports.getType(a) !== "array") {
234
+ if (exports.getType(a) === "undefined") a = [];
235
+ else a = [a];
236
+ }
237
+ if (exports.getType(b) !== "array") {
238
+ if (exports.getType(b) === "undefined") b = [];
239
+ else b = [b];
240
+ }
241
+
242
+ return ko.utils.arrayGetDistinctValues(a.concat(b));
243
+ }
244
+
245
+ // When using a 'create' callback, we proxy the dependent observable so that it doesn't immediately evaluate on creation.
246
+ // The reason is that the dependent observables in the user-specified callback may contain references to properties that have not been mapped yet.
247
+ function withProxyDependentObservable(dependentObservables, callback) {
248
+ var localDO = ko.dependentObservable;
249
+ ko.dependentObservable = function (read, owner, options) {
250
+ options = options || {};
251
+
252
+ if (read && typeof read == "object") { // mirrors condition in knockout implementation of DO's
253
+ options = read;
254
+ }
255
+
256
+ var realDeferEvaluation = options.deferEvaluation;
257
+
258
+ var isRemoved = false;
259
+
260
+ // We wrap the original dependent observable so that we can remove it from the 'dependentObservables' list we need to evaluate after mapping has
261
+ // completed if the user already evaluated the DO themselves in the meantime.
262
+ var wrap = function (DO) {
263
+ // Temporarily revert ko.dependentObservable, since it is used in ko.isWriteableObservable
264
+ var tmp = ko.dependentObservable;
265
+ ko.dependentObservable = realKoDependentObservable;
266
+ var isWriteable = ko.isWriteableObservable(DO);
267
+ ko.dependentObservable = tmp;
268
+
269
+ var wrapped = realKoDependentObservable({
270
+ read: function () {
271
+ if (!isRemoved) {
272
+ ko.utils.arrayRemoveItem(dependentObservables, DO);
273
+ isRemoved = true;
274
+ }
275
+ return DO.apply(DO, arguments);
276
+ },
277
+ write: isWriteable && function (val) {
278
+ return DO(val);
279
+ },
280
+ deferEvaluation: true
281
+ });
282
+ if (DEBUG) wrapped._wrapper = true;
283
+ wrapped.__DO = DO;
284
+ return wrapped;
285
+ };
286
+
287
+ options.deferEvaluation = true; // will either set for just options, or both read/options.
288
+ var realDependentObservable = new realKoDependentObservable(read, owner, options);
289
+
290
+ if (!realDeferEvaluation) {
291
+ realDependentObservable = wrap(realDependentObservable);
292
+ dependentObservables.push(realDependentObservable);
293
+ }
294
+
295
+ return realDependentObservable;
296
+ }
297
+ ko.dependentObservable.fn = realKoDependentObservable.fn;
298
+ ko.computed = ko.dependentObservable;
299
+ var result = callback();
300
+ ko.dependentObservable = localDO;
301
+ ko.computed = ko.dependentObservable;
302
+ return result;
303
+ }
304
+
305
+ function updateViewModel(mappedRootObject, rootObject, options, parentName, parent, parentPropertyName, mappedParent) {
306
+ var isArray = exports.getType(ko.utils.unwrapObservable(rootObject)) === "array";
307
+
308
+ parentPropertyName = parentPropertyName || "";
309
+
310
+ // If this object was already mapped previously, take the options from there and merge them with our existing ones.
311
+ if (exports.isMapped(mappedRootObject)) {
312
+ var previousMapping = ko.utils.unwrapObservable(mappedRootObject)[mappingProperty];
313
+ options = merge(previousMapping, options);
314
+ }
315
+
316
+ var callbackParams = {
317
+ data: rootObject,
318
+ parent: mappedParent || parent
319
+ };
320
+
321
+ var hasCreateCallback = function () {
322
+ return options[parentName] && options[parentName].create instanceof Function;
323
+ };
324
+
325
+ var createCallback = function (data) {
326
+ return withProxyDependentObservable(dependentObservables, function () {
327
+
328
+ if (ko.utils.unwrapObservable(parent) instanceof Array) {
329
+ return options[parentName].create({
330
+ data: data || callbackParams.data,
331
+ parent: callbackParams.parent,
332
+ skip: emptyReturn
333
+ });
334
+ } else {
335
+ return options[parentName].create({
336
+ data: data || callbackParams.data,
337
+ parent: callbackParams.parent
338
+ });
339
+ }
340
+ });
341
+ };
342
+
343
+ var hasUpdateCallback = function () {
344
+ return options[parentName] && options[parentName].update instanceof Function;
345
+ };
346
+
347
+ var updateCallback = function (obj, data) {
348
+ var params = {
349
+ data: data || callbackParams.data,
350
+ parent: callbackParams.parent,
351
+ target: ko.utils.unwrapObservable(obj)
352
+ };
353
+
354
+ if (ko.isWriteableObservable(obj)) {
355
+ params.observable = obj;
356
+ }
357
+
358
+ return options[parentName].update(params);
359
+ }
360
+
361
+ var alreadyMapped = visitedObjects.get(rootObject);
362
+ if (alreadyMapped) {
363
+ return alreadyMapped;
364
+ }
365
+
366
+ parentName = parentName || "";
367
+
368
+ if (!isArray) {
369
+ // For atomic types, do a direct update on the observable
370
+ if (!canHaveProperties(rootObject)) {
371
+ switch (exports.getType(rootObject)) {
372
+ case "function":
373
+ if (hasUpdateCallback()) {
374
+ if (ko.isWriteableObservable(rootObject)) {
375
+ rootObject(updateCallback(rootObject));
376
+ mappedRootObject = rootObject;
377
+ } else {
378
+ mappedRootObject = updateCallback(rootObject);
379
+ }
380
+ } else {
381
+ mappedRootObject = rootObject;
382
+ }
383
+ break;
384
+ default:
385
+ if (ko.isWriteableObservable(mappedRootObject)) {
386
+ if (hasUpdateCallback()) {
387
+ var valueToWrite = updateCallback(mappedRootObject);
388
+ mappedRootObject(valueToWrite);
389
+ return valueToWrite;
390
+ } else {
391
+ var valueToWrite = ko.utils.unwrapObservable(rootObject);
392
+ mappedRootObject(valueToWrite);
393
+ return valueToWrite;
394
+ }
395
+ } else {
396
+ var hasCreateOrUpdateCallback = hasCreateCallback() || hasUpdateCallback();
397
+
398
+ if (hasCreateCallback()) {
399
+ mappedRootObject = createCallback();
400
+ } else {
401
+ mappedRootObject = ko.observable(ko.utils.unwrapObservable(rootObject));
402
+ }
403
+
404
+ if (hasUpdateCallback()) {
405
+ mappedRootObject(updateCallback(mappedRootObject));
406
+ }
407
+
408
+ if (hasCreateOrUpdateCallback) return mappedRootObject;
409
+ }
410
+ }
411
+
412
+ } else {
413
+ mappedRootObject = ko.utils.unwrapObservable(mappedRootObject);
414
+ if (!mappedRootObject) {
415
+ if (hasCreateCallback()) {
416
+ var result = createCallback();
417
+
418
+ if (hasUpdateCallback()) {
419
+ result = updateCallback(result);
420
+ }
421
+
422
+ return result;
423
+ } else {
424
+ if (hasUpdateCallback()) {
425
+ return updateCallback(result);
426
+ }
427
+
428
+ mappedRootObject = {};
429
+ }
430
+ }
431
+
432
+ if (hasUpdateCallback()) {
433
+ mappedRootObject = updateCallback(mappedRootObject);
434
+ }
435
+
436
+ visitedObjects.save(rootObject, mappedRootObject);
437
+ if (hasUpdateCallback()) return mappedRootObject;
438
+
439
+ // For non-atomic types, visit all properties and update recursively
440
+ visitPropertiesOrArrayEntries(rootObject, function (indexer) {
441
+ var fullPropertyName = parentPropertyName.length ? parentPropertyName + "." + indexer : indexer;
442
+
443
+ if (ko.utils.arrayIndexOf(options.ignore, fullPropertyName) != -1) {
444
+ return;
445
+ }
446
+
447
+ if (ko.utils.arrayIndexOf(options.copy, fullPropertyName) != -1) {
448
+ mappedRootObject[indexer] = rootObject[indexer];
449
+ return;
450
+ }
451
+
452
+ if(typeof rootObject[indexer] != "object" && typeof rootObject[indexer] != "array" && options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
453
+ {
454
+ mappedRootObject[indexer] = rootObject[indexer];
455
+ options.copiedProperties[fullPropertyName] = true;
456
+ return;
457
+ }
458
+
459
+ // In case we are adding an already mapped property, fill it with the previously mapped property value to prevent recursion.
460
+ // If this is a property that was generated by fromJS, we should use the options specified there
461
+ var prevMappedProperty = visitedObjects.get(rootObject[indexer]);
462
+ var retval = updateViewModel(mappedRootObject[indexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName, mappedRootObject);
463
+ var value = prevMappedProperty || retval;
464
+
465
+ if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
466
+ {
467
+ mappedRootObject[indexer] = value();
468
+ options.copiedProperties[fullPropertyName] = true;
469
+ return;
470
+ }
471
+
472
+ if (ko.isWriteableObservable(mappedRootObject[indexer])) {
473
+ value = ko.utils.unwrapObservable(value);
474
+ if (mappedRootObject[indexer]() !== value) {
475
+ mappedRootObject[indexer](value);
476
+ }
477
+ } else {
478
+ value = mappedRootObject[indexer] === undefined ? value : ko.utils.unwrapObservable(value);
479
+ mappedRootObject[indexer] = value;
480
+ }
481
+
482
+ options.mappedProperties[fullPropertyName] = true;
483
+ });
484
+ }
485
+ } else { //mappedRootObject is an array
486
+ var changes = [];
487
+
488
+ var hasKeyCallback = false;
489
+ var keyCallback = function (x) {
490
+ return x;
491
+ }
492
+ if (options[parentName] && options[parentName].key) {
493
+ keyCallback = options[parentName].key;
494
+ hasKeyCallback = true;
495
+ }
496
+
497
+ if (!ko.isObservable(mappedRootObject)) {
498
+ // When creating the new observable array, also add a bunch of utility functions that take the 'key' of the array items into account.
499
+ mappedRootObject = ko.observableArray([]);
500
+
501
+ mappedRootObject.mappedRemove = function (valueOrPredicate) {
502
+ var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
503
+ return value === keyCallback(valueOrPredicate);
504
+ };
505
+ return mappedRootObject.remove(function (item) {
506
+ return predicate(keyCallback(item));
507
+ });
508
+ }
509
+
510
+ mappedRootObject.mappedRemoveAll = function (arrayOfValues) {
511
+ var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
512
+ return mappedRootObject.remove(function (item) {
513
+ return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
514
+ });
515
+ }
516
+
517
+ mappedRootObject.mappedDestroy = function (valueOrPredicate) {
518
+ var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
519
+ return value === keyCallback(valueOrPredicate);
520
+ };
521
+ return mappedRootObject.destroy(function (item) {
522
+ return predicate(keyCallback(item));
523
+ });
524
+ }
525
+
526
+ mappedRootObject.mappedDestroyAll = function (arrayOfValues) {
527
+ var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
528
+ return mappedRootObject.destroy(function (item) {
529
+ return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
530
+ });
531
+ }
532
+
533
+ mappedRootObject.mappedIndexOf = function (item) {
534
+ var keys = filterArrayByKey(mappedRootObject(), keyCallback);
535
+ var key = keyCallback(item);
536
+ return ko.utils.arrayIndexOf(keys, key);
537
+ }
538
+
539
+ mappedRootObject.mappedGet = function (item) {
540
+ return mappedRootObject()[mappedRootObject.mappedIndexOf(item)];
541
+ }
542
+
543
+ mappedRootObject.mappedCreate = function (value) {
544
+ if (mappedRootObject.mappedIndexOf(value) !== -1) {
545
+ throw new Error("There already is an object with the key that you specified.");
546
+ }
547
+
548
+ var item = hasCreateCallback() ? createCallback(value) : value;
549
+ if (hasUpdateCallback()) {
550
+ var newValue = updateCallback(item, value);
551
+ if (ko.isWriteableObservable(item)) {
552
+ item(newValue);
553
+ } else {
554
+ item = newValue;
555
+ }
556
+ }
557
+ mappedRootObject.push(item);
558
+ return item;
559
+ }
560
+ }
561
+
562
+ var currentArrayKeys = filterArrayByKey(ko.utils.unwrapObservable(mappedRootObject), keyCallback).sort();
563
+ var newArrayKeys = filterArrayByKey(rootObject, keyCallback);
564
+ if (hasKeyCallback) newArrayKeys.sort();
565
+ var editScript = ko.utils.compareArrays(currentArrayKeys, newArrayKeys);
566
+
567
+ var ignoreIndexOf = {};
568
+
569
+ var i, j;
570
+
571
+ var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);
572
+ var itemsByKey = {};
573
+ var optimizedKeys = true;
574
+ for (i = 0, j = unwrappedRootObject.length; i < j; i++) {
575
+ var key = keyCallback(unwrappedRootObject[i]);
576
+ if (key === undefined || key instanceof Object) {
577
+ optimizedKeys = false;
578
+ break;
579
+ }
580
+ itemsByKey[key] = unwrappedRootObject[i];
581
+ }
582
+
583
+ var newContents = [];
584
+ var passedOver = 0;
585
+ for (i = 0, j = editScript.length; i < j; i++) {
586
+ var key = editScript[i];
587
+ var mappedItem;
588
+ var fullPropertyName = parentPropertyName + "[" + i + "]";
589
+ switch (key.status) {
590
+ case "added":
591
+ var item = optimizedKeys ? itemsByKey[key.value] : getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
592
+ mappedItem = updateViewModel(undefined, item, options, parentName, mappedRootObject, fullPropertyName, parent);
593
+ if(!hasCreateCallback()) {
594
+ mappedItem = ko.utils.unwrapObservable(mappedItem);
595
+ }
596
+
597
+ var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
598
+
599
+ if (mappedItem === emptyReturn) {
600
+ passedOver++;
601
+ } else {
602
+ newContents[index - passedOver] = mappedItem;
603
+ }
604
+
605
+ ignoreIndexOf[index] = true;
606
+ break;
607
+ case "retained":
608
+ var item = optimizedKeys ? itemsByKey[key.value] : getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
609
+ mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
610
+ updateViewModel(mappedItem, item, options, parentName, mappedRootObject, fullPropertyName, parent);
611
+
612
+ var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
613
+ newContents[index] = mappedItem;
614
+ ignoreIndexOf[index] = true;
615
+ break;
616
+ case "deleted":
617
+ mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
618
+ break;
619
+ }
620
+
621
+ changes.push({
622
+ event: key.status,
623
+ item: mappedItem
624
+ });
625
+ }
626
+
627
+ mappedRootObject(newContents);
628
+
629
+ if (options[parentName] && options[parentName].arrayChanged) {
630
+ ko.utils.arrayForEach(changes, function (change) {
631
+ options[parentName].arrayChanged(change.event, change.item);
632
+ });
633
+ }
634
+ }
635
+
636
+ return mappedRootObject;
637
+ }
638
+
639
+ function ignorableIndexOf(array, item, ignoreIndices) {
640
+ for (var i = 0, j = array.length; i < j; i++) {
641
+ if (ignoreIndices[i] === true) continue;
642
+ if (array[i] === item) return i;
643
+ }
644
+ return null;
645
+ }
646
+
647
+ function mapKey(item, callback) {
648
+ var mappedItem;
649
+ if (callback) mappedItem = callback(item);
650
+ if (exports.getType(mappedItem) === "undefined") mappedItem = item;
651
+
652
+ return ko.utils.unwrapObservable(mappedItem);
653
+ }
654
+
655
+ function getItemByKey(array, key, callback) {
656
+ array = ko.utils.unwrapObservable(array);
657
+ for (var i = 0, j = array.length; i < j; i++) {
658
+ var item = array[i];
659
+ if (mapKey(item, callback) === key) return item;
660
+ }
661
+
662
+ throw new Error("When calling ko.update*, the key '" + key + "' was not found!");
663
+ }
664
+
665
+ function filterArrayByKey(array, callback) {
666
+ return ko.utils.arrayMap(ko.utils.unwrapObservable(array), function (item) {
667
+ if (callback) {
668
+ return mapKey(item, callback);
669
+ } else {
670
+ return item;
671
+ }
672
+ });
673
+ }
674
+
675
+ function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
676
+ if (exports.getType(rootObject) === "array") {
677
+ for (var i = 0; i < rootObject.length; i++)
678
+ visitorCallback(i);
679
+ } else {
680
+ for (var propertyName in rootObject)
681
+ visitorCallback(propertyName);
682
+ }
683
+ };
684
+
685
+ function canHaveProperties(object) {
686
+ var type = exports.getType(object);
687
+ return ((type === "object") || (type === "array")) && (object !== null);
688
+ }
689
+
690
+ // Based on the parentName, this creates a fully classified name of a property
691
+
692
+ function getPropertyName(parentName, parent, indexer) {
693
+ var propertyName = parentName || "";
694
+ if (exports.getType(parent) === "array") {
695
+ if (parentName) {
696
+ propertyName += "[" + indexer + "]";
697
+ }
698
+ } else {
699
+ if (parentName) {
700
+ propertyName += ".";
701
+ }
702
+ propertyName += indexer;
703
+ }
704
+ return propertyName;
705
+ }
706
+
707
+ exports.visitModel = function (rootObject, callback, options) {
708
+ options = options || {};
709
+ options.visitedObjects = options.visitedObjects || new objectLookup();
710
+
711
+ var mappedRootObject;
712
+ var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);
713
+
714
+ if (!canHaveProperties(unwrappedRootObject)) {
715
+ return callback(rootObject, options.parentName);
716
+ } else {
717
+ options = fillOptions(options, unwrappedRootObject[mappingProperty]);
718
+
719
+ // Only do a callback, but ignore the results
720
+ callback(rootObject, options.parentName);
721
+ mappedRootObject = exports.getType(unwrappedRootObject) === "array" ? [] : {};
722
+ }
723
+
724
+ options.visitedObjects.save(rootObject, mappedRootObject);
725
+
726
+ var parentName = options.parentName;
727
+ visitPropertiesOrArrayEntries(unwrappedRootObject, function (indexer) {
728
+ if (options.ignore && ko.utils.arrayIndexOf(options.ignore, indexer) != -1) return;
729
+
730
+ var propertyValue = unwrappedRootObject[indexer];
731
+ options.parentName = getPropertyName(parentName, unwrappedRootObject, indexer);
732
+
733
+ // If we don't want to explicitly copy the unmapped property...
734
+ if (ko.utils.arrayIndexOf(options.copy, indexer) === -1) {
735
+ // ...find out if it's a property we want to explicitly include
736
+ if (ko.utils.arrayIndexOf(options.include, indexer) === -1) {
737
+ // The mapped properties object contains all the properties that were part of the original object.
738
+ // If a property does not exist, and it is not because it is part of an array (e.g. "myProp[3]"), then it should not be unmapped.
739
+ if (unwrappedRootObject[mappingProperty]
740
+ && unwrappedRootObject[mappingProperty].mappedProperties && !unwrappedRootObject[mappingProperty].mappedProperties[indexer]
741
+ && unwrappedRootObject[mappingProperty].copiedProperties && !unwrappedRootObject[mappingProperty].copiedProperties[indexer]
742
+ && !(exports.getType(unwrappedRootObject) === "array")) {
743
+ return;
744
+ }
745
+ }
746
+ }
747
+
748
+ var outputProperty;
749
+ switch (exports.getType(ko.utils.unwrapObservable(propertyValue))) {
750
+ case "object":
751
+ case "array":
752
+ case "undefined":
753
+ var previouslyMappedValue = options.visitedObjects.get(propertyValue);
754
+ mappedRootObject[indexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : exports.visitModel(propertyValue, callback, options);
755
+ break;
756
+ default:
757
+ mappedRootObject[indexer] = callback(propertyValue, options.parentName);
758
+ }
759
+ });
760
+
761
+ return mappedRootObject;
762
+ }
763
+
764
+ function simpleObjectLookup() {
765
+ var keys = [];
766
+ var values = [];
767
+ this.save = function (key, value) {
768
+ var existingIndex = ko.utils.arrayIndexOf(keys, key);
769
+ if (existingIndex >= 0) values[existingIndex] = value;
770
+ else {
771
+ keys.push(key);
772
+ values.push(value);
773
+ }
774
+ };
775
+ this.get = function (key) {
776
+ var existingIndex = ko.utils.arrayIndexOf(keys, key);
777
+ var value = (existingIndex >= 0) ? values[existingIndex] : undefined;
778
+ return value;
779
+ };
780
+ };
781
+
782
+ function objectLookup() {
783
+ var buckets = {};
784
+
785
+ var findBucket = function(key) {
786
+ var bucketKey;
787
+ try {
788
+ bucketKey = key;//JSON.stringify(key);
789
+ }
790
+ catch (e) {
791
+ bucketKey = "$$$";
792
+ }
793
+
794
+ var bucket = buckets[bucketKey];
795
+ if (bucket === undefined) {
796
+ bucket = new simpleObjectLookup();
797
+ buckets[bucketKey] = bucket;
798
+ }
799
+ return bucket;
800
+ };
801
+
802
+ this.save = function (key, value) {
803
+ findBucket(key).save(key, value);
804
+ };
805
+ this.get = function (key) {
806
+ return findBucket(key).get(key);
807
+ };
808
+ };
809
+ }));
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knockoutjs-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0.1
4
+ version: 2.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -44,6 +44,7 @@ files:
44
44
  - lib/knockoutjs-rails/version.rb
45
45
  - lib/knockoutjs-rails.rb
46
46
  - vendor/assets/javascripts/knockout.js
47
+ - vendor/assets/javascripts/knockout.mapping.js
47
48
  - MIT-LICENSE
48
49
  - README.md
49
50
  homepage: https://github.com/jswanner/knockoutjs-rails