knockoutjs-rails 2.2.0.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|