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 +6 -1
- data/lib/knockoutjs-rails/version.rb +1 -1
- data/vendor/assets/javascripts/knockout.js +24 -18
- data/vendor/assets/javascripts/knockout.mapping.js +809 -0
- metadata +2 -1
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.
|
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,4 +1,4 @@
|
|
1
|
-
// Knockout JavaScript library v2.2.
|
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.
|
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
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
elem
|
719
|
-
|
720
|
-
|
721
|
-
|
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.
|
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
|