knockoutjs-rails 3.3.0.1 → 3.4.0
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/knockoutjs-rails/version.rb +1 -1
- data/vendor/assets/javascripts/knockout.js +833 -437
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89017fcfcfc1f72d9d72cf6c9f8274261cebc10c
|
4
|
+
data.tar.gz: 880e18223ec85fd7f468d975ca4132ce5d6eed0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e75993ebe967fe9a7464fc294ad0e8035f2132a79dca7510b50e37d4386acd28e0a11dec6867af00518f21951a66b762397f8ebe94db2502689a10ccc391415b
|
7
|
+
data.tar.gz: 64b06ff23c2a09e5ee820458b639389ebe2b61591cd43565582a3cc85837fb3ae565f816382fe32f03ad344518d171e1ddef4b84e70ca1079479a0776ba8a563
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@ application.js):
|
|
25
25
|
|
26
26
|
## Versioning
|
27
27
|
|
28
|
-
knockoutjs-rails
|
28
|
+
knockoutjs-rails 3.4.0 == Knockout.js 3.4.0
|
29
29
|
|
30
30
|
Every attempt is made to mirror the currently shipping Knockout.js version number wherever possible.
|
31
31
|
The major and minor version numbers will always represent the Knockout.js version, but the patch level
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* Knockout JavaScript library v3.
|
2
|
+
* Knockout JavaScript library v3.4.0
|
3
3
|
* (c) Steven Sanderson - http://knockoutjs.com/
|
4
4
|
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
5
5
|
*/
|
@@ -19,7 +19,7 @@ var DEBUG=true;
|
|
19
19
|
if (typeof define === 'function' && define['amd']) {
|
20
20
|
// [1] AMD anonymous module
|
21
21
|
define(['exports', 'require'], factory);
|
22
|
-
} else if (typeof
|
22
|
+
} else if (typeof exports === 'object' && typeof module === 'object') {
|
23
23
|
// [2] CommonJS/Node.js
|
24
24
|
factory(module['exports'] || exports); // module.exports is for Node.js
|
25
25
|
} else {
|
@@ -45,9 +45,16 @@ ko.exportSymbol = function(koPath, object) {
|
|
45
45
|
ko.exportProperty = function(owner, publicName, object) {
|
46
46
|
owner[publicName] = object;
|
47
47
|
};
|
48
|
-
ko.version = "3.
|
48
|
+
ko.version = "3.4.0";
|
49
49
|
|
50
50
|
ko.exportSymbol('version', ko.version);
|
51
|
+
// For any options that may affect various areas of Knockout and aren't directly associated with data binding.
|
52
|
+
ko.options = {
|
53
|
+
'deferUpdates': false,
|
54
|
+
'useOnlyNativeEvents': false
|
55
|
+
};
|
56
|
+
|
57
|
+
//ko.exportSymbol('options', ko.options); // 'options' isn't minified
|
51
58
|
ko.utils = (function () {
|
52
59
|
function objectForEach(obj, action) {
|
53
60
|
for (var prop in obj) {
|
@@ -74,6 +81,7 @@ ko.utils = (function () {
|
|
74
81
|
}
|
75
82
|
|
76
83
|
var canSetPrototype = ({ __proto__: [] } instanceof Array);
|
84
|
+
var canUseSymbols = !DEBUG && typeof Symbol === 'function';
|
77
85
|
|
78
86
|
// Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
|
79
87
|
var knownEvents = {}, knownEventTypesByEventName = {};
|
@@ -304,8 +312,11 @@ ko.utils = (function () {
|
|
304
312
|
// Rules:
|
305
313
|
// [A] Any leading nodes that have been removed should be ignored
|
306
314
|
// These most likely correspond to memoization nodes that were already removed during binding
|
307
|
-
// See https://github.com/
|
308
|
-
// [B]
|
315
|
+
// See https://github.com/knockout/knockout/pull/440
|
316
|
+
// [B] Any trailing nodes that have been remove should be ignored
|
317
|
+
// This prevents the code here from adding unrelated nodes to the array while processing rule [C]
|
318
|
+
// See https://github.com/knockout/knockout/pull/1903
|
319
|
+
// [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed,
|
309
320
|
// and include any nodes that have been inserted among the previous collection
|
310
321
|
|
311
322
|
if (continuousNodeArray.length) {
|
@@ -317,6 +328,10 @@ ko.utils = (function () {
|
|
317
328
|
continuousNodeArray.splice(0, 1);
|
318
329
|
|
319
330
|
// Rule [B]
|
331
|
+
while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
|
332
|
+
continuousNodeArray.length--;
|
333
|
+
|
334
|
+
// Rule [C]
|
320
335
|
if (continuousNodeArray.length > 1) {
|
321
336
|
var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1];
|
322
337
|
// Replace with the actual new continuous node set
|
@@ -324,8 +339,6 @@ ko.utils = (function () {
|
|
324
339
|
while (current !== last) {
|
325
340
|
continuousNodeArray.push(current);
|
326
341
|
current = current.nextSibling;
|
327
|
-
if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
|
328
|
-
return;
|
329
342
|
}
|
330
343
|
continuousNodeArray.push(last);
|
331
344
|
}
|
@@ -385,14 +398,38 @@ ko.utils = (function () {
|
|
385
398
|
return element && element.tagName && element.tagName.toLowerCase();
|
386
399
|
},
|
387
400
|
|
401
|
+
catchFunctionErrors: function (delegate) {
|
402
|
+
return ko['onError'] ? function () {
|
403
|
+
try {
|
404
|
+
return delegate.apply(this, arguments);
|
405
|
+
} catch (e) {
|
406
|
+
ko['onError'] && ko['onError'](e);
|
407
|
+
throw e;
|
408
|
+
}
|
409
|
+
} : delegate;
|
410
|
+
},
|
411
|
+
|
412
|
+
setTimeout: function (handler, timeout) {
|
413
|
+
return setTimeout(ko.utils.catchFunctionErrors(handler), timeout);
|
414
|
+
},
|
415
|
+
|
416
|
+
deferError: function (error) {
|
417
|
+
setTimeout(function () {
|
418
|
+
ko['onError'] && ko['onError'](error);
|
419
|
+
throw error;
|
420
|
+
}, 0);
|
421
|
+
},
|
422
|
+
|
388
423
|
registerEventHandler: function (element, eventType, handler) {
|
424
|
+
var wrappedHandler = ko.utils.catchFunctionErrors(handler);
|
425
|
+
|
389
426
|
var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
|
390
|
-
if (!mustUseAttachEvent && jQueryInstance) {
|
391
|
-
jQueryInstance(element)['bind'](eventType,
|
427
|
+
if (!ko.options['useOnlyNativeEvents'] && !mustUseAttachEvent && jQueryInstance) {
|
428
|
+
jQueryInstance(element)['bind'](eventType, wrappedHandler);
|
392
429
|
} else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
|
393
|
-
element.addEventListener(eventType,
|
430
|
+
element.addEventListener(eventType, wrappedHandler, false);
|
394
431
|
else if (typeof element.attachEvent != "undefined") {
|
395
|
-
var attachEventHandler = function (event) {
|
432
|
+
var attachEventHandler = function (event) { wrappedHandler.call(element, event); },
|
396
433
|
attachEventName = "on" + eventType;
|
397
434
|
element.attachEvent(attachEventName, attachEventHandler);
|
398
435
|
|
@@ -415,7 +452,7 @@ ko.utils = (function () {
|
|
415
452
|
// In both cases, we'll use the click method instead.
|
416
453
|
var useClickWorkaround = isClickOnCheckableElement(element, eventType);
|
417
454
|
|
418
|
-
if (jQueryInstance && !useClickWorkaround) {
|
455
|
+
if (!ko.options['useOnlyNativeEvents'] && jQueryInstance && !useClickWorkaround) {
|
419
456
|
jQueryInstance(element)['trigger'](eventType);
|
420
457
|
} else if (typeof document.createEvent == "function") {
|
421
458
|
if (typeof element.dispatchEvent == "function") {
|
@@ -515,6 +552,10 @@ ko.utils = (function () {
|
|
515
552
|
return result;
|
516
553
|
},
|
517
554
|
|
555
|
+
createSymbolOrString: function(identifier) {
|
556
|
+
return canUseSymbols ? Symbol(identifier) : identifier;
|
557
|
+
},
|
558
|
+
|
518
559
|
isIe6 : isIe6,
|
519
560
|
isIe7 : isIe7,
|
520
561
|
ieVersion : ieVersion,
|
@@ -793,7 +834,29 @@ ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
|
|
793
834
|
ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
|
794
835
|
ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
|
795
836
|
(function () {
|
796
|
-
var
|
837
|
+
var none = [0, "", ""],
|
838
|
+
table = [1, "<table>", "</table>"],
|
839
|
+
tbody = [2, "<table><tbody>", "</tbody></table>"],
|
840
|
+
tr = [3, "<table><tbody><tr>", "</tr></tbody></table>"],
|
841
|
+
select = [1, "<select multiple='multiple'>", "</select>"],
|
842
|
+
lookup = {
|
843
|
+
'thead': table,
|
844
|
+
'tbody': table,
|
845
|
+
'tfoot': table,
|
846
|
+
'tr': tbody,
|
847
|
+
'td': tr,
|
848
|
+
'th': tr,
|
849
|
+
'option': select,
|
850
|
+
'optgroup': select
|
851
|
+
},
|
852
|
+
|
853
|
+
// This is needed for old IE if you're *not* using either jQuery or innerShiv. Doesn't affect other cases.
|
854
|
+
mayRequireCreateElementHack = ko.utils.ieVersion <= 8;
|
855
|
+
|
856
|
+
function getWrap(tags) {
|
857
|
+
var m = tags.match(/^<([a-z]+)[ >]/);
|
858
|
+
return (m && lookup[m[1]]) || none;
|
859
|
+
}
|
797
860
|
|
798
861
|
function simpleHtmlParse(html, documentContext) {
|
799
862
|
documentContext || (documentContext = document);
|
@@ -808,25 +871,34 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
|
|
808
871
|
// (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
|
809
872
|
|
810
873
|
// Trim whitespace, otherwise indexOf won't work as expected
|
811
|
-
var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div")
|
812
|
-
|
813
|
-
|
814
|
-
var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
|
815
|
-
!tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
|
816
|
-
(!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
|
817
|
-
/* anything else */ [0, "", ""];
|
874
|
+
var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div"),
|
875
|
+
wrap = getWrap(tags),
|
876
|
+
depth = wrap[0];
|
818
877
|
|
819
878
|
// Go to html and back, then peel off extra wrappers
|
820
879
|
// Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
|
821
880
|
var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
|
822
881
|
if (typeof windowContext['innerShiv'] == "function") {
|
882
|
+
// Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
|
883
|
+
// support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
|
884
|
+
// somehow shims the native APIs so it just works anyway)
|
823
885
|
div.appendChild(windowContext['innerShiv'](markup));
|
824
886
|
} else {
|
887
|
+
if (mayRequireCreateElementHack) {
|
888
|
+
// The document.createElement('my-element') trick to enable custom elements in IE6-8
|
889
|
+
// only works if we assign innerHTML on an element associated with that document.
|
890
|
+
documentContext.appendChild(div);
|
891
|
+
}
|
892
|
+
|
825
893
|
div.innerHTML = markup;
|
894
|
+
|
895
|
+
if (mayRequireCreateElementHack) {
|
896
|
+
div.parentNode.removeChild(div);
|
897
|
+
}
|
826
898
|
}
|
827
899
|
|
828
900
|
// Move to the right depth
|
829
|
-
while (
|
901
|
+
while (depth--)
|
830
902
|
div = div.lastChild;
|
831
903
|
|
832
904
|
return ko.utils.makeArray(div.lastChild.childNodes);
|
@@ -858,8 +930,9 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
|
|
858
930
|
}
|
859
931
|
|
860
932
|
ko.utils.parseHtmlFragment = function(html, documentContext) {
|
861
|
-
return jQueryInstance ?
|
862
|
-
|
933
|
+
return jQueryInstance ?
|
934
|
+
jQueryHtmlParse(html, documentContext) : // As below, benefit from jQuery's optimisations where possible
|
935
|
+
simpleHtmlParse(html, documentContext); // ... otherwise, this simple logic will do in most common cases.
|
863
936
|
};
|
864
937
|
|
865
938
|
ko.utils.setHtml = function(node, html) {
|
@@ -959,6 +1032,114 @@ ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
|
|
959
1032
|
ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
|
960
1033
|
ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
|
961
1034
|
ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
|
1035
|
+
ko.tasks = (function () {
|
1036
|
+
var scheduler,
|
1037
|
+
taskQueue = [],
|
1038
|
+
taskQueueLength = 0,
|
1039
|
+
nextHandle = 1,
|
1040
|
+
nextIndexToProcess = 0;
|
1041
|
+
|
1042
|
+
if (window['MutationObserver']) {
|
1043
|
+
// Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+
|
1044
|
+
// From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
|
1045
|
+
scheduler = (function (callback) {
|
1046
|
+
var div = document.createElement("div");
|
1047
|
+
new MutationObserver(callback).observe(div, {attributes: true});
|
1048
|
+
return function () { div.classList.toggle("foo"); };
|
1049
|
+
})(scheduledProcess);
|
1050
|
+
} else if (document && "onreadystatechange" in document.createElement("script")) {
|
1051
|
+
// IE 6-10
|
1052
|
+
// From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
|
1053
|
+
scheduler = function (callback) {
|
1054
|
+
var script = document.createElement("script");
|
1055
|
+
script.onreadystatechange = function () {
|
1056
|
+
script.onreadystatechange = null;
|
1057
|
+
document.documentElement.removeChild(script);
|
1058
|
+
script = null;
|
1059
|
+
callback();
|
1060
|
+
};
|
1061
|
+
document.documentElement.appendChild(script);
|
1062
|
+
};
|
1063
|
+
} else {
|
1064
|
+
scheduler = function (callback) {
|
1065
|
+
setTimeout(callback, 0);
|
1066
|
+
};
|
1067
|
+
}
|
1068
|
+
|
1069
|
+
function processTasks() {
|
1070
|
+
if (taskQueueLength) {
|
1071
|
+
// Each mark represents the end of a logical group of tasks and the number of these groups is
|
1072
|
+
// limited to prevent unchecked recursion.
|
1073
|
+
var mark = taskQueueLength, countMarks = 0;
|
1074
|
+
|
1075
|
+
// nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
|
1076
|
+
for (var task; nextIndexToProcess < taskQueueLength; ) {
|
1077
|
+
if (task = taskQueue[nextIndexToProcess++]) {
|
1078
|
+
if (nextIndexToProcess > mark) {
|
1079
|
+
if (++countMarks >= 5000) {
|
1080
|
+
nextIndexToProcess = taskQueueLength; // skip all tasks remaining in the queue since any of them could be causing the recursion
|
1081
|
+
ko.utils.deferError(Error("'Too much recursion' after processing " + countMarks + " task groups."));
|
1082
|
+
break;
|
1083
|
+
}
|
1084
|
+
mark = taskQueueLength;
|
1085
|
+
}
|
1086
|
+
try {
|
1087
|
+
task();
|
1088
|
+
} catch (ex) {
|
1089
|
+
ko.utils.deferError(ex);
|
1090
|
+
}
|
1091
|
+
}
|
1092
|
+
}
|
1093
|
+
}
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
function scheduledProcess() {
|
1097
|
+
processTasks();
|
1098
|
+
|
1099
|
+
// Reset the queue
|
1100
|
+
nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
function scheduleTaskProcessing() {
|
1104
|
+
ko.tasks['scheduler'](scheduledProcess);
|
1105
|
+
}
|
1106
|
+
|
1107
|
+
var tasks = {
|
1108
|
+
'scheduler': scheduler, // Allow overriding the scheduler
|
1109
|
+
|
1110
|
+
schedule: function (func) {
|
1111
|
+
if (!taskQueueLength) {
|
1112
|
+
scheduleTaskProcessing();
|
1113
|
+
}
|
1114
|
+
|
1115
|
+
taskQueue[taskQueueLength++] = func;
|
1116
|
+
return nextHandle++;
|
1117
|
+
},
|
1118
|
+
|
1119
|
+
cancel: function (handle) {
|
1120
|
+
var index = handle - (nextHandle - taskQueueLength);
|
1121
|
+
if (index >= nextIndexToProcess && index < taskQueueLength) {
|
1122
|
+
taskQueue[index] = null;
|
1123
|
+
}
|
1124
|
+
},
|
1125
|
+
|
1126
|
+
// For testing only: reset the queue and return the previous queue length
|
1127
|
+
'resetForTesting': function () {
|
1128
|
+
var length = taskQueueLength - nextIndexToProcess;
|
1129
|
+
nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
|
1130
|
+
return length;
|
1131
|
+
},
|
1132
|
+
|
1133
|
+
runEarly: processTasks
|
1134
|
+
};
|
1135
|
+
|
1136
|
+
return tasks;
|
1137
|
+
})();
|
1138
|
+
|
1139
|
+
ko.exportSymbol('tasks', ko.tasks);
|
1140
|
+
ko.exportSymbol('tasks.schedule', ko.tasks.schedule);
|
1141
|
+
//ko.exportSymbol('tasks.cancel', ko.tasks.cancel); "cancel" isn't minified
|
1142
|
+
ko.exportSymbol('tasks.runEarly', ko.tasks.runEarly);
|
962
1143
|
ko.extenders = {
|
963
1144
|
'throttle': function(target, timeout) {
|
964
1145
|
// Throttling means two things:
|
@@ -974,7 +1155,7 @@ ko.extenders = {
|
|
974
1155
|
'read': target,
|
975
1156
|
'write': function(value) {
|
976
1157
|
clearTimeout(writeTimeoutInstance);
|
977
|
-
writeTimeoutInstance = setTimeout(function() {
|
1158
|
+
writeTimeoutInstance = ko.utils.setTimeout(function() {
|
978
1159
|
target(value);
|
979
1160
|
}, timeout);
|
980
1161
|
}
|
@@ -991,12 +1172,33 @@ ko.extenders = {
|
|
991
1172
|
method = options['method'];
|
992
1173
|
}
|
993
1174
|
|
1175
|
+
// rateLimit supersedes deferred updates
|
1176
|
+
target._deferUpdates = false;
|
1177
|
+
|
994
1178
|
limitFunction = method == 'notifyWhenChangesStop' ? debounce : throttle;
|
995
1179
|
target.limit(function(callback) {
|
996
1180
|
return limitFunction(callback, timeout);
|
997
1181
|
});
|
998
1182
|
},
|
999
1183
|
|
1184
|
+
'deferred': function(target, options) {
|
1185
|
+
if (options !== true) {
|
1186
|
+
throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.')
|
1187
|
+
}
|
1188
|
+
|
1189
|
+
if (!target._deferUpdates) {
|
1190
|
+
target._deferUpdates = true;
|
1191
|
+
target.limit(function (callback) {
|
1192
|
+
var handle;
|
1193
|
+
return function () {
|
1194
|
+
ko.tasks.cancel(handle);
|
1195
|
+
handle = ko.tasks.schedule(callback);
|
1196
|
+
target['notifySubscribers'](undefined, 'dirty');
|
1197
|
+
};
|
1198
|
+
});
|
1199
|
+
}
|
1200
|
+
},
|
1201
|
+
|
1000
1202
|
'notify': function(target, notifyWhen) {
|
1001
1203
|
target["equalityComparer"] = notifyWhen == "always" ?
|
1002
1204
|
null : // null equalityComparer means to always notify
|
@@ -1014,7 +1216,7 @@ function throttle(callback, timeout) {
|
|
1014
1216
|
var timeoutInstance;
|
1015
1217
|
return function () {
|
1016
1218
|
if (!timeoutInstance) {
|
1017
|
-
timeoutInstance = setTimeout(function() {
|
1219
|
+
timeoutInstance = ko.utils.setTimeout(function () {
|
1018
1220
|
timeoutInstance = undefined;
|
1019
1221
|
callback();
|
1020
1222
|
}, timeout);
|
@@ -1026,7 +1228,7 @@ function debounce(callback, timeout) {
|
|
1026
1228
|
var timeoutInstance;
|
1027
1229
|
return function () {
|
1028
1230
|
clearTimeout(timeoutInstance);
|
1029
|
-
timeoutInstance = setTimeout(callback, timeout);
|
1231
|
+
timeoutInstance = ko.utils.setTimeout(callback, timeout);
|
1030
1232
|
};
|
1031
1233
|
}
|
1032
1234
|
|
@@ -1058,14 +1260,29 @@ ko.subscription.prototype.dispose = function () {
|
|
1058
1260
|
};
|
1059
1261
|
|
1060
1262
|
ko.subscribable = function () {
|
1061
|
-
ko.utils.setPrototypeOfOrExtend(this,
|
1062
|
-
this
|
1063
|
-
this._versionNumber = 1;
|
1263
|
+
ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);
|
1264
|
+
ko_subscribable_fn.init(this);
|
1064
1265
|
}
|
1065
1266
|
|
1066
1267
|
var defaultEvent = "change";
|
1067
1268
|
|
1269
|
+
// Moved out of "limit" to avoid the extra closure
|
1270
|
+
function limitNotifySubscribers(value, event) {
|
1271
|
+
if (!event || event === defaultEvent) {
|
1272
|
+
this._limitChange(value);
|
1273
|
+
} else if (event === 'beforeChange') {
|
1274
|
+
this._limitBeforeChange(value);
|
1275
|
+
} else {
|
1276
|
+
this._origNotifySubscribers(value, event);
|
1277
|
+
}
|
1278
|
+
}
|
1279
|
+
|
1068
1280
|
var ko_subscribable_fn = {
|
1281
|
+
init: function(instance) {
|
1282
|
+
instance._subscriptions = {};
|
1283
|
+
instance._versionNumber = 1;
|
1284
|
+
},
|
1285
|
+
|
1069
1286
|
subscribe: function (callback, callbackTarget, event) {
|
1070
1287
|
var self = this;
|
1071
1288
|
|
@@ -1122,40 +1339,34 @@ var ko_subscribable_fn = {
|
|
1122
1339
|
|
1123
1340
|
limit: function(limitFunction) {
|
1124
1341
|
var self = this, selfIsObservable = ko.isObservable(self),
|
1125
|
-
|
1342
|
+
ignoreBeforeChange, previousValue, pendingValue, beforeChange = 'beforeChange';
|
1126
1343
|
|
1127
1344
|
if (!self._origNotifySubscribers) {
|
1128
1345
|
self._origNotifySubscribers = self["notifySubscribers"];
|
1129
|
-
self["notifySubscribers"] =
|
1130
|
-
if (!event || event === defaultEvent) {
|
1131
|
-
self._rateLimitedChange(value);
|
1132
|
-
} else if (event === beforeChange) {
|
1133
|
-
self._rateLimitedBeforeChange(value);
|
1134
|
-
} else {
|
1135
|
-
self._origNotifySubscribers(value, event);
|
1136
|
-
}
|
1137
|
-
};
|
1346
|
+
self["notifySubscribers"] = limitNotifySubscribers;
|
1138
1347
|
}
|
1139
1348
|
|
1140
1349
|
var finish = limitFunction(function() {
|
1350
|
+
self._notificationIsPending = false;
|
1351
|
+
|
1141
1352
|
// If an observable provided a reference to itself, access it to get the latest value.
|
1142
1353
|
// This allows computed observables to delay calculating their value until needed.
|
1143
1354
|
if (selfIsObservable && pendingValue === self) {
|
1144
1355
|
pendingValue = self();
|
1145
1356
|
}
|
1146
|
-
|
1357
|
+
ignoreBeforeChange = false;
|
1147
1358
|
if (self.isDifferent(previousValue, pendingValue)) {
|
1148
1359
|
self._origNotifySubscribers(previousValue = pendingValue);
|
1149
1360
|
}
|
1150
1361
|
});
|
1151
1362
|
|
1152
|
-
self.
|
1153
|
-
|
1363
|
+
self._limitChange = function(value) {
|
1364
|
+
self._notificationIsPending = ignoreBeforeChange = true;
|
1154
1365
|
pendingValue = value;
|
1155
1366
|
finish();
|
1156
1367
|
};
|
1157
|
-
self.
|
1158
|
-
if (!
|
1368
|
+
self._limitBeforeChange = function(value) {
|
1369
|
+
if (!ignoreBeforeChange) {
|
1159
1370
|
previousValue = value;
|
1160
1371
|
self._origNotifySubscribers(value, beforeChange);
|
1161
1372
|
}
|
@@ -1172,7 +1383,8 @@ var ko_subscribable_fn = {
|
|
1172
1383
|
} else {
|
1173
1384
|
var total = 0;
|
1174
1385
|
ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
|
1175
|
-
|
1386
|
+
if (eventName !== 'dirty')
|
1387
|
+
total += subscriptions.length;
|
1176
1388
|
});
|
1177
1389
|
return total;
|
1178
1390
|
}
|
@@ -1239,7 +1451,7 @@ ko.computedContext = ko.dependencyDetection = (function () {
|
|
1239
1451
|
if (currentFrame) {
|
1240
1452
|
if (!ko.isSubscribable(subscribable))
|
1241
1453
|
throw new Error("Only subscribable things can act as dependencies");
|
1242
|
-
currentFrame.callback(subscribable, subscribable._id || (subscribable._id = getId()));
|
1454
|
+
currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
|
1243
1455
|
}
|
1244
1456
|
},
|
1245
1457
|
|
@@ -1267,21 +1479,19 @@ ko.computedContext = ko.dependencyDetection = (function () {
|
|
1267
1479
|
ko.exportSymbol('computedContext', ko.computedContext);
|
1268
1480
|
ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);
|
1269
1481
|
ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);
|
1270
|
-
ko.exportSymbol('computedContext.isSleeping', ko.computedContext.isSleeping);
|
1271
1482
|
|
1272
1483
|
ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);
|
1273
|
-
|
1274
|
-
var _latestValue = initialValue;
|
1484
|
+
var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');
|
1275
1485
|
|
1486
|
+
ko.observable = function (initialValue) {
|
1276
1487
|
function observable() {
|
1277
1488
|
if (arguments.length > 0) {
|
1278
1489
|
// Write
|
1279
1490
|
|
1280
1491
|
// Ignore writes if the value hasn't changed
|
1281
|
-
if (observable.isDifferent(
|
1492
|
+
if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
|
1282
1493
|
observable.valueWillMutate();
|
1283
|
-
|
1284
|
-
if (DEBUG) observable._latestValue = _latestValue;
|
1494
|
+
observable[observableLatestValue] = arguments[0];
|
1285
1495
|
observable.valueHasMutated();
|
1286
1496
|
}
|
1287
1497
|
return this; // Permits chained assignments
|
@@ -1289,37 +1499,46 @@ ko.observable = function (initialValue) {
|
|
1289
1499
|
else {
|
1290
1500
|
// Read
|
1291
1501
|
ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
|
1292
|
-
return
|
1502
|
+
return observable[observableLatestValue];
|
1293
1503
|
}
|
1294
1504
|
}
|
1295
|
-
ko.subscribable.call(observable);
|
1296
|
-
ko.utils.setPrototypeOfOrExtend(observable, ko.observable['fn']);
|
1297
1505
|
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1506
|
+
observable[observableLatestValue] = initialValue;
|
1507
|
+
|
1508
|
+
// Inherit from 'subscribable'
|
1509
|
+
if (!ko.utils.canSetPrototype) {
|
1510
|
+
// 'subscribable' won't be on the prototype chain unless we put it there directly
|
1511
|
+
ko.utils.extend(observable, ko.subscribable['fn']);
|
1512
|
+
}
|
1513
|
+
ko.subscribable['fn'].init(observable);
|
1514
|
+
|
1515
|
+
// Inherit from 'observable'
|
1516
|
+
ko.utils.setPrototypeOfOrExtend(observable, observableFn);
|
1302
1517
|
|
1303
|
-
ko.
|
1304
|
-
|
1305
|
-
|
1518
|
+
if (ko.options['deferUpdates']) {
|
1519
|
+
ko.extenders['deferred'](observable, true);
|
1520
|
+
}
|
1306
1521
|
|
1307
1522
|
return observable;
|
1308
1523
|
}
|
1309
1524
|
|
1310
|
-
|
1311
|
-
|
1525
|
+
// Define prototype for observables
|
1526
|
+
var observableFn = {
|
1527
|
+
'equalityComparer': valuesArePrimitiveAndEqual,
|
1528
|
+
peek: function() { return this[observableLatestValue]; },
|
1529
|
+
valueHasMutated: function () { this['notifySubscribers'](this[observableLatestValue]); },
|
1530
|
+
valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }
|
1312
1531
|
};
|
1313
1532
|
|
1314
|
-
var protoProperty = ko.observable.protoProperty = "__ko_proto__";
|
1315
|
-
ko.observable['fn'][protoProperty] = ko.observable;
|
1316
|
-
|
1317
1533
|
// Note that for browsers that don't support proto assignment, the
|
1318
1534
|
// inheritance chain is created manually in the ko.observable constructor
|
1319
1535
|
if (ko.utils.canSetPrototype) {
|
1320
|
-
ko.utils.setPrototypeOf(
|
1536
|
+
ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);
|
1321
1537
|
}
|
1322
1538
|
|
1539
|
+
var protoProperty = ko.observable.protoProperty = '__ko_proto__';
|
1540
|
+
observableFn[protoProperty] = ko.observable;
|
1541
|
+
|
1323
1542
|
ko.hasPrototype = function(instance, prototype) {
|
1324
1543
|
if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
|
1325
1544
|
if (instance[protoProperty] === prototype) return true;
|
@@ -1331,20 +1550,23 @@ ko.isObservable = function (instance) {
|
|
1331
1550
|
}
|
1332
1551
|
ko.isWriteableObservable = function (instance) {
|
1333
1552
|
// Observable
|
1334
|
-
if ((typeof instance ==
|
1553
|
+
if ((typeof instance == 'function') && instance[protoProperty] === ko.observable)
|
1335
1554
|
return true;
|
1336
1555
|
// Writeable dependent observable
|
1337
|
-
if ((typeof instance ==
|
1556
|
+
if ((typeof instance == 'function') && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
|
1338
1557
|
return true;
|
1339
1558
|
// Anything else
|
1340
1559
|
return false;
|
1341
1560
|
}
|
1342
1561
|
|
1343
|
-
|
1344
1562
|
ko.exportSymbol('observable', ko.observable);
|
1345
1563
|
ko.exportSymbol('isObservable', ko.isObservable);
|
1346
1564
|
ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
|
1347
1565
|
ko.exportSymbol('isWritableObservable', ko.isWriteableObservable);
|
1566
|
+
ko.exportSymbol('observable.fn', observableFn);
|
1567
|
+
ko.exportProperty(observableFn, 'peek', observableFn.peek);
|
1568
|
+
ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);
|
1569
|
+
ko.exportProperty(observableFn, 'valueWillMutate', observableFn.valueWillMutate);
|
1348
1570
|
ko.observableArray = function (initialValues) {
|
1349
1571
|
initialValues = initialValues || [];
|
1350
1572
|
|
@@ -1436,6 +1658,12 @@ ko.observableArray['fn'] = {
|
|
1436
1658
|
}
|
1437
1659
|
};
|
1438
1660
|
|
1661
|
+
// Note that for browsers that don't support proto assignment, the
|
1662
|
+
// inheritance chain is created manually in the ko.observableArray constructor
|
1663
|
+
if (ko.utils.canSetPrototype) {
|
1664
|
+
ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);
|
1665
|
+
}
|
1666
|
+
|
1439
1667
|
// Populate ko.observableArray.fn with read/write functions from native arrays
|
1440
1668
|
// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
|
1441
1669
|
// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
|
@@ -1448,7 +1676,8 @@ ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "uns
|
|
1448
1676
|
this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
|
1449
1677
|
var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
|
1450
1678
|
this.valueHasMutated();
|
1451
|
-
return
|
1679
|
+
// The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
|
1680
|
+
return methodCallResult === underlyingArray ? this : methodCallResult;
|
1452
1681
|
};
|
1453
1682
|
});
|
1454
1683
|
|
@@ -1460,15 +1689,16 @@ ko.utils.arrayForEach(["slice"], function (methodName) {
|
|
1460
1689
|
};
|
1461
1690
|
});
|
1462
1691
|
|
1463
|
-
// Note that for browsers that don't support proto assignment, the
|
1464
|
-
// inheritance chain is created manually in the ko.observableArray constructor
|
1465
|
-
if (ko.utils.canSetPrototype) {
|
1466
|
-
ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);
|
1467
|
-
}
|
1468
|
-
|
1469
1692
|
ko.exportSymbol('observableArray', ko.observableArray);
|
1470
1693
|
var arrayChangeEventName = 'arrayChange';
|
1471
|
-
ko.extenders['trackArrayChanges'] = function(target) {
|
1694
|
+
ko.extenders['trackArrayChanges'] = function(target, options) {
|
1695
|
+
// Use the provided options--each call to trackArrayChanges overwrites the previously set options
|
1696
|
+
target.compareArrayOptions = {};
|
1697
|
+
if (options && typeof options == "object") {
|
1698
|
+
ko.utils.extend(target.compareArrayOptions, options);
|
1699
|
+
}
|
1700
|
+
target.compareArrayOptions['sparse'] = true;
|
1701
|
+
|
1472
1702
|
// Only modify the target observable once
|
1473
1703
|
if (target.cacheDiffForKnownOperation) {
|
1474
1704
|
return;
|
@@ -1545,7 +1775,7 @@ ko.extenders['trackArrayChanges'] = function(target) {
|
|
1545
1775
|
// plugin, which without this check would not be compatible with arrayChange notifications. Normally,
|
1546
1776
|
// notifications are issued immediately so we wouldn't be queueing up more than one.
|
1547
1777
|
if (!cachedDiff || pendingNotifications > 1) {
|
1548
|
-
cachedDiff = ko.utils.compareArrays(previousContents, currentContents,
|
1778
|
+
cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions);
|
1549
1779
|
}
|
1550
1780
|
|
1551
1781
|
return cachedDiff;
|
@@ -1605,41 +1835,162 @@ ko.extenders['trackArrayChanges'] = function(target) {
|
|
1605
1835
|
cachedDiff = diff;
|
1606
1836
|
};
|
1607
1837
|
};
|
1838
|
+
var computedState = ko.utils.createSymbolOrString('_state');
|
1839
|
+
|
1608
1840
|
ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
|
1609
|
-
|
1610
|
-
_needsEvaluation = true,
|
1611
|
-
_isBeingEvaluated = false,
|
1612
|
-
_suppressDisposalUntilDisposeWhenReturnsFalse = false,
|
1613
|
-
_isDisposed = false,
|
1614
|
-
readFunction = evaluatorFunctionOrOptions,
|
1615
|
-
pure = false,
|
1616
|
-
isSleeping = false;
|
1617
|
-
|
1618
|
-
if (readFunction && typeof readFunction == "object") {
|
1841
|
+
if (typeof evaluatorFunctionOrOptions === "object") {
|
1619
1842
|
// Single-parameter syntax - everything is on this "options" param
|
1620
|
-
options =
|
1621
|
-
readFunction = options["read"];
|
1843
|
+
options = evaluatorFunctionOrOptions;
|
1622
1844
|
} else {
|
1623
1845
|
// Multi-parameter syntax - construct the options according to the params passed
|
1624
1846
|
options = options || {};
|
1625
|
-
if (
|
1626
|
-
|
1847
|
+
if (evaluatorFunctionOrOptions) {
|
1848
|
+
options["read"] = evaluatorFunctionOrOptions;
|
1849
|
+
}
|
1627
1850
|
}
|
1628
|
-
if (typeof
|
1629
|
-
throw
|
1851
|
+
if (typeof options["read"] != "function")
|
1852
|
+
throw Error("Pass a function that returns the value of the ko.computed");
|
1853
|
+
|
1854
|
+
var writeFunction = options["write"];
|
1855
|
+
var state = {
|
1856
|
+
latestValue: undefined,
|
1857
|
+
isStale: true,
|
1858
|
+
isBeingEvaluated: false,
|
1859
|
+
suppressDisposalUntilDisposeWhenReturnsFalse: false,
|
1860
|
+
isDisposed: false,
|
1861
|
+
pure: false,
|
1862
|
+
isSleeping: false,
|
1863
|
+
readFunction: options["read"],
|
1864
|
+
evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
|
1865
|
+
disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
|
1866
|
+
disposeWhen: options["disposeWhen"] || options.disposeWhen,
|
1867
|
+
domNodeDisposalCallback: null,
|
1868
|
+
dependencyTracking: {},
|
1869
|
+
dependenciesCount: 0,
|
1870
|
+
evaluationTimeoutInstance: null
|
1871
|
+
};
|
1630
1872
|
|
1631
|
-
function
|
1632
|
-
if (
|
1633
|
-
|
1873
|
+
function computedObservable() {
|
1874
|
+
if (arguments.length > 0) {
|
1875
|
+
if (typeof writeFunction === "function") {
|
1876
|
+
// Writing a value
|
1877
|
+
writeFunction.apply(state.evaluatorFunctionTarget, arguments);
|
1878
|
+
} else {
|
1879
|
+
throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
|
1880
|
+
}
|
1881
|
+
return this; // Permits chained assignments
|
1882
|
+
} else {
|
1883
|
+
// Reading the value
|
1884
|
+
ko.dependencyDetection.registerDependency(computedObservable);
|
1885
|
+
if (state.isStale || (state.isSleeping && computedObservable.haveDependenciesChanged())) {
|
1886
|
+
computedObservable.evaluateImmediate();
|
1887
|
+
}
|
1888
|
+
return state.latestValue;
|
1634
1889
|
}
|
1890
|
+
}
|
1635
1891
|
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1892
|
+
computedObservable[computedState] = state;
|
1893
|
+
computedObservable.hasWriteFunction = typeof writeFunction === "function";
|
1894
|
+
|
1895
|
+
// Inherit from 'subscribable'
|
1896
|
+
if (!ko.utils.canSetPrototype) {
|
1897
|
+
// 'subscribable' won't be on the prototype chain unless we put it there directly
|
1898
|
+
ko.utils.extend(computedObservable, ko.subscribable['fn']);
|
1899
|
+
}
|
1900
|
+
ko.subscribable['fn'].init(computedObservable);
|
1901
|
+
|
1902
|
+
// Inherit from 'computed'
|
1903
|
+
ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);
|
1904
|
+
|
1905
|
+
if (options['pure']) {
|
1906
|
+
state.pure = true;
|
1907
|
+
state.isSleeping = true; // Starts off sleeping; will awake on the first subscription
|
1908
|
+
ko.utils.extend(computedObservable, pureComputedOverrides);
|
1909
|
+
} else if (options['deferEvaluation']) {
|
1910
|
+
ko.utils.extend(computedObservable, deferEvaluationOverrides);
|
1911
|
+
}
|
1912
|
+
|
1913
|
+
if (ko.options['deferUpdates']) {
|
1914
|
+
ko.extenders['deferred'](computedObservable, true);
|
1915
|
+
}
|
1916
|
+
|
1917
|
+
if (DEBUG) {
|
1918
|
+
// #1731 - Aid debugging by exposing the computed's options
|
1919
|
+
computedObservable["_options"] = options;
|
1920
|
+
}
|
1921
|
+
|
1922
|
+
if (state.disposeWhenNodeIsRemoved) {
|
1923
|
+
// Since this computed is associated with a DOM node, and we don't want to dispose the computed
|
1924
|
+
// until the DOM node is *removed* from the document (as opposed to never having been in the document),
|
1925
|
+
// we'll prevent disposal until "disposeWhen" first returns false.
|
1926
|
+
state.suppressDisposalUntilDisposeWhenReturnsFalse = true;
|
1927
|
+
|
1928
|
+
// disposeWhenNodeIsRemoved: true can be used to opt into the "only dispose after first false result"
|
1929
|
+
// behaviour even if there's no specific node to watch. In that case, clear the option so we don't try
|
1930
|
+
// to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't
|
1931
|
+
// be documented or used by application code, as it's likely to change in a future version of KO.
|
1932
|
+
if (!state.disposeWhenNodeIsRemoved.nodeType) {
|
1933
|
+
state.disposeWhenNodeIsRemoved = null;
|
1934
|
+
}
|
1935
|
+
}
|
1936
|
+
|
1937
|
+
// Evaluate, unless sleeping or deferEvaluation is true
|
1938
|
+
if (!state.isSleeping && !options['deferEvaluation']) {
|
1939
|
+
computedObservable.evaluateImmediate();
|
1940
|
+
}
|
1941
|
+
|
1942
|
+
// Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
|
1943
|
+
// removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
|
1944
|
+
if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {
|
1945
|
+
ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {
|
1946
|
+
computedObservable.dispose();
|
1947
|
+
});
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
return computedObservable;
|
1951
|
+
};
|
1952
|
+
|
1953
|
+
// Utility function that disposes a given dependencyTracking entry
|
1954
|
+
function computedDisposeDependencyCallback(id, entryToDispose) {
|
1955
|
+
if (entryToDispose !== null && entryToDispose.dispose) {
|
1956
|
+
entryToDispose.dispose();
|
1957
|
+
}
|
1958
|
+
}
|
1959
|
+
|
1960
|
+
// This function gets called each time a dependency is detected while evaluating a computed.
|
1961
|
+
// It's factored out as a shared function to avoid creating unnecessary function instances during evaluation.
|
1962
|
+
function computedBeginDependencyDetectionCallback(subscribable, id) {
|
1963
|
+
var computedObservable = this.computedObservable,
|
1964
|
+
state = computedObservable[computedState];
|
1965
|
+
if (!state.isDisposed) {
|
1966
|
+
if (this.disposalCount && this.disposalCandidates[id]) {
|
1967
|
+
// Don't want to dispose this subscription, as it's still being used
|
1968
|
+
computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]);
|
1969
|
+
this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway
|
1970
|
+
--this.disposalCount;
|
1971
|
+
} else if (!state.dependencyTracking[id]) {
|
1972
|
+
// Brand new subscription - add it
|
1973
|
+
computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable));
|
1974
|
+
}
|
1639
1975
|
}
|
1976
|
+
}
|
1977
|
+
|
1978
|
+
var computedFn = {
|
1979
|
+
"equalityComparer": valuesArePrimitiveAndEqual,
|
1980
|
+
getDependenciesCount: function () {
|
1981
|
+
return this[computedState].dependenciesCount;
|
1982
|
+
},
|
1983
|
+
addDependencyTracking: function (id, target, trackingObj) {
|
1984
|
+
if (this[computedState].pure && target === this) {
|
1985
|
+
throw Error("A 'pure' computed must not be called recursively");
|
1986
|
+
}
|
1640
1987
|
|
1641
|
-
|
1642
|
-
|
1988
|
+
this[computedState].dependencyTracking[id] = trackingObj;
|
1989
|
+
trackingObj._order = this[computedState].dependenciesCount++;
|
1990
|
+
trackingObj._version = target.getVersion();
|
1991
|
+
},
|
1992
|
+
haveDependenciesChanged: function () {
|
1993
|
+
var id, dependency, dependencyTracking = this[computedState].dependencyTracking;
|
1643
1994
|
for (id in dependencyTracking) {
|
1644
1995
|
if (dependencyTracking.hasOwnProperty(id)) {
|
1645
1996
|
dependency = dependencyTracking[id];
|
@@ -1648,38 +1999,57 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
|
|
1648
1999
|
}
|
1649
2000
|
}
|
1650
2001
|
}
|
1651
|
-
}
|
1652
|
-
|
1653
|
-
|
1654
|
-
if (
|
1655
|
-
|
1656
|
-
if (dependency.dispose)
|
1657
|
-
dependency.dispose();
|
1658
|
-
});
|
2002
|
+
},
|
2003
|
+
markDirty: function () {
|
2004
|
+
// Process "dirty" events if we can handle delayed notifications
|
2005
|
+
if (this._evalDelayed && !this[computedState].isBeingEvaluated) {
|
2006
|
+
this._evalDelayed();
|
1659
2007
|
}
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
2008
|
+
},
|
2009
|
+
isActive: function () {
|
2010
|
+
return this[computedState].isStale || this[computedState].dependenciesCount > 0;
|
2011
|
+
},
|
2012
|
+
respondToChange: function () {
|
2013
|
+
// Ignore "change" events if we've already scheduled a delayed notification
|
2014
|
+
if (!this._notificationIsPending) {
|
2015
|
+
this.evaluatePossiblyAsync();
|
2016
|
+
}
|
2017
|
+
},
|
2018
|
+
subscribeToDependency: function (target) {
|
2019
|
+
if (target._deferUpdates && !this[computedState].disposeWhenNodeIsRemoved) {
|
2020
|
+
var dirtySub = target.subscribe(this.markDirty, this, 'dirty'),
|
2021
|
+
changeSub = target.subscribe(this.respondToChange, this);
|
2022
|
+
return {
|
2023
|
+
_target: target,
|
2024
|
+
dispose: function () {
|
2025
|
+
dirtySub.dispose();
|
2026
|
+
changeSub.dispose();
|
2027
|
+
}
|
2028
|
+
};
|
2029
|
+
} else {
|
2030
|
+
return target.subscribe(this.evaluatePossiblyAsync, this);
|
2031
|
+
}
|
2032
|
+
},
|
2033
|
+
evaluatePossiblyAsync: function () {
|
2034
|
+
var computedObservable = this,
|
2035
|
+
throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
|
1669
2036
|
if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
|
1670
|
-
clearTimeout(evaluationTimeoutInstance);
|
1671
|
-
evaluationTimeoutInstance = setTimeout(function () {
|
1672
|
-
evaluateImmediate(true /*notifyChange*/);
|
2037
|
+
clearTimeout(this[computedState].evaluationTimeoutInstance);
|
2038
|
+
this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {
|
2039
|
+
computedObservable.evaluateImmediate(true /*notifyChange*/);
|
1673
2040
|
}, throttleEvaluationTimeout);
|
1674
|
-
} else if (
|
1675
|
-
|
2041
|
+
} else if (computedObservable._evalDelayed) {
|
2042
|
+
computedObservable._evalDelayed();
|
1676
2043
|
} else {
|
1677
|
-
evaluateImmediate(true /*notifyChange*/);
|
2044
|
+
computedObservable.evaluateImmediate(true /*notifyChange*/);
|
1678
2045
|
}
|
1679
|
-
}
|
2046
|
+
},
|
2047
|
+
evaluateImmediate: function (notifyChange) {
|
2048
|
+
var computedObservable = this,
|
2049
|
+
state = computedObservable[computedState],
|
2050
|
+
disposeWhen = state.disposeWhen;
|
1680
2051
|
|
1681
|
-
|
1682
|
-
if (_isBeingEvaluated) {
|
2052
|
+
if (state.isBeingEvaluated) {
|
1683
2053
|
// If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
|
1684
2054
|
// This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
|
1685
2055
|
// certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
|
@@ -1688,297 +2058,239 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
|
|
1688
2058
|
}
|
1689
2059
|
|
1690
2060
|
// Do not evaluate (and possibly capture new dependencies) if disposed
|
1691
|
-
if (
|
2061
|
+
if (state.isDisposed) {
|
1692
2062
|
return;
|
1693
2063
|
}
|
1694
2064
|
|
1695
|
-
if (disposeWhen && disposeWhen()) {
|
1696
|
-
// See comment
|
1697
|
-
if (!
|
1698
|
-
dispose();
|
2065
|
+
if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) {
|
2066
|
+
// See comment above about suppressDisposalUntilDisposeWhenReturnsFalse
|
2067
|
+
if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) {
|
2068
|
+
computedObservable.dispose();
|
1699
2069
|
return;
|
1700
2070
|
}
|
1701
2071
|
} else {
|
1702
2072
|
// It just did return false, so we can stop suppressing now
|
1703
|
-
|
2073
|
+
state.suppressDisposalUntilDisposeWhenReturnsFalse = false;
|
1704
2074
|
}
|
1705
2075
|
|
1706
|
-
|
1707
|
-
|
2076
|
+
state.isBeingEvaluated = true;
|
1708
2077
|
try {
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
isInitial = pure ? undefined : !_dependenciesCount; // If we're evaluating when there are no previous dependencies, it must be the first time
|
1714
|
-
|
1715
|
-
ko.dependencyDetection.begin({
|
1716
|
-
callback: function(subscribable, id) {
|
1717
|
-
if (!_isDisposed) {
|
1718
|
-
if (disposalCount && disposalCandidates[id]) {
|
1719
|
-
// Don't want to dispose this subscription, as it's still being used
|
1720
|
-
addDependencyTracking(id, subscribable, disposalCandidates[id]);
|
1721
|
-
delete disposalCandidates[id];
|
1722
|
-
--disposalCount;
|
1723
|
-
} else if (!dependencyTracking[id]) {
|
1724
|
-
// Brand new subscription - add it
|
1725
|
-
addDependencyTracking(id, subscribable, isSleeping ? { _target: subscribable } : subscribable.subscribe(evaluatePossiblyAsync));
|
1726
|
-
}
|
1727
|
-
}
|
1728
|
-
},
|
1729
|
-
computed: dependentObservable,
|
1730
|
-
isInitial: isInitial
|
1731
|
-
});
|
2078
|
+
this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);
|
2079
|
+
} finally {
|
2080
|
+
state.isBeingEvaluated = false;
|
2081
|
+
}
|
1732
2082
|
|
1733
|
-
|
1734
|
-
|
2083
|
+
if (!state.dependenciesCount) {
|
2084
|
+
computedObservable.dispose();
|
2085
|
+
}
|
2086
|
+
},
|
2087
|
+
evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {
|
2088
|
+
// This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else.
|
2089
|
+
// Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate,
|
2090
|
+
// which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least).
|
2091
|
+
|
2092
|
+
var computedObservable = this,
|
2093
|
+
state = computedObservable[computedState];
|
2094
|
+
|
2095
|
+
// Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
|
2096
|
+
// Then, during evaluation, we cross off any that are in fact still being used.
|
2097
|
+
var isInitial = state.pure ? undefined : !state.dependenciesCount, // If we're evaluating when there are no previous dependencies, it must be the first time
|
2098
|
+
dependencyDetectionContext = {
|
2099
|
+
computedObservable: computedObservable,
|
2100
|
+
disposalCandidates: state.dependencyTracking,
|
2101
|
+
disposalCount: state.dependenciesCount
|
2102
|
+
};
|
1735
2103
|
|
1736
|
-
|
1737
|
-
|
2104
|
+
ko.dependencyDetection.begin({
|
2105
|
+
callbackTarget: dependencyDetectionContext,
|
2106
|
+
callback: computedBeginDependencyDetectionCallback,
|
2107
|
+
computed: computedObservable,
|
2108
|
+
isInitial: isInitial
|
2109
|
+
});
|
1738
2110
|
|
1739
|
-
|
1740
|
-
|
2111
|
+
state.dependencyTracking = {};
|
2112
|
+
state.dependenciesCount = 0;
|
1741
2113
|
|
1742
|
-
|
1743
|
-
if (disposalCount && !isSleeping) {
|
1744
|
-
ko.utils.objectForEach(disposalCandidates, function(id, toDispose) {
|
1745
|
-
if (toDispose.dispose)
|
1746
|
-
toDispose.dispose();
|
1747
|
-
});
|
1748
|
-
}
|
2114
|
+
var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext);
|
1749
2115
|
|
1750
|
-
|
2116
|
+
if (computedObservable.isDifferent(state.latestValue, newValue)) {
|
2117
|
+
if (!state.isSleeping) {
|
2118
|
+
computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
|
1751
2119
|
}
|
1752
2120
|
|
1753
|
-
|
1754
|
-
if (!isSleeping) {
|
1755
|
-
notify(_latestValue, "beforeChange");
|
1756
|
-
}
|
1757
|
-
|
1758
|
-
_latestValue = newValue;
|
1759
|
-
if (DEBUG) dependentObservable._latestValue = _latestValue;
|
2121
|
+
state.latestValue = newValue;
|
1760
2122
|
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
}
|
2123
|
+
if (state.isSleeping) {
|
2124
|
+
computedObservable.updateVersion();
|
2125
|
+
} else if (notifyChange) {
|
2126
|
+
computedObservable["notifySubscribers"](state.latestValue);
|
1766
2127
|
}
|
2128
|
+
}
|
1767
2129
|
|
1768
|
-
|
1769
|
-
|
1770
|
-
}
|
1771
|
-
} finally {
|
1772
|
-
_isBeingEvaluated = false;
|
2130
|
+
if (isInitial) {
|
2131
|
+
computedObservable["notifySubscribers"](state.latestValue, "awake");
|
1773
2132
|
}
|
2133
|
+
},
|
2134
|
+
evaluateImmediate_CallReadThenEndDependencyDetection: function (state, dependencyDetectionContext) {
|
2135
|
+
// This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic.
|
2136
|
+
// You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection
|
2137
|
+
// can be independent of try/finally blocks, which contributes to saving about 40% off the CPU
|
2138
|
+
// overhead of computed evaluation (on V8 at least).
|
1774
2139
|
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
2140
|
+
try {
|
2141
|
+
var readFunction = state.readFunction;
|
2142
|
+
return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
|
2143
|
+
} finally {
|
2144
|
+
ko.dependencyDetection.end();
|
1778
2145
|
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
// Writing a value
|
1783
|
-
writeFunction.apply(evaluatorFunctionTarget, arguments);
|
1784
|
-
} else {
|
1785
|
-
throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
|
2146
|
+
// For each subscription no longer being used, remove it from the active subscriptions list and dispose it
|
2147
|
+
if (dependencyDetectionContext.disposalCount && !state.isSleeping) {
|
2148
|
+
ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);
|
1786
2149
|
}
|
1787
|
-
return this; // Permits chained assignments
|
1788
|
-
} else {
|
1789
|
-
// Reading the value
|
1790
|
-
ko.dependencyDetection.registerDependency(dependentObservable);
|
1791
|
-
if (_needsEvaluation || (isSleeping && haveDependenciesChanged())) {
|
1792
|
-
evaluateImmediate();
|
1793
|
-
}
|
1794
|
-
return _latestValue;
|
1795
|
-
}
|
1796
|
-
}
|
1797
2150
|
|
1798
|
-
|
2151
|
+
state.isStale = false;
|
2152
|
+
}
|
2153
|
+
},
|
2154
|
+
peek: function () {
|
1799
2155
|
// Peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set.
|
1800
|
-
|
1801
|
-
|
2156
|
+
var state = this[computedState];
|
2157
|
+
if ((state.isStale && !state.dependenciesCount) || (state.isSleeping && this.haveDependenciesChanged())) {
|
2158
|
+
this.evaluateImmediate();
|
1802
2159
|
}
|
1803
|
-
return
|
1804
|
-
}
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
function notify(value, event) {
|
1811
|
-
dependentObservable["notifySubscribers"](value, event);
|
1812
|
-
}
|
1813
|
-
|
1814
|
-
// By here, "options" is always non-null
|
1815
|
-
var writeFunction = options["write"],
|
1816
|
-
disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
|
1817
|
-
disposeWhenOption = options["disposeWhen"] || options.disposeWhen,
|
1818
|
-
disposeWhen = disposeWhenOption,
|
1819
|
-
dispose = disposeComputed,
|
1820
|
-
dependencyTracking = {},
|
1821
|
-
_dependenciesCount = 0,
|
1822
|
-
evaluationTimeoutInstance = null;
|
1823
|
-
|
1824
|
-
if (!evaluatorFunctionTarget)
|
1825
|
-
evaluatorFunctionTarget = options["owner"];
|
1826
|
-
|
1827
|
-
ko.subscribable.call(dependentObservable);
|
1828
|
-
ko.utils.setPrototypeOfOrExtend(dependentObservable, ko.dependentObservable['fn']);
|
1829
|
-
|
1830
|
-
dependentObservable.peek = peek;
|
1831
|
-
dependentObservable.getDependenciesCount = function () { return _dependenciesCount; };
|
1832
|
-
dependentObservable.hasWriteFunction = typeof writeFunction === "function";
|
1833
|
-
dependentObservable.dispose = function () { dispose(); };
|
1834
|
-
dependentObservable.isActive = isActive;
|
1835
|
-
|
1836
|
-
// Replace the limit function with one that delays evaluation as well.
|
1837
|
-
var originalLimit = dependentObservable.limit;
|
1838
|
-
dependentObservable.limit = function(limitFunction) {
|
1839
|
-
originalLimit.call(dependentObservable, limitFunction);
|
1840
|
-
dependentObservable._evalRateLimited = function() {
|
1841
|
-
dependentObservable._rateLimitedBeforeChange(_latestValue);
|
2160
|
+
return state.latestValue;
|
2161
|
+
},
|
2162
|
+
limit: function (limitFunction) {
|
2163
|
+
// Override the limit function with one that delays evaluation as well
|
2164
|
+
ko.subscribable['fn'].limit.call(this, limitFunction);
|
2165
|
+
this._evalDelayed = function () {
|
2166
|
+
this._limitBeforeChange(this[computedState].latestValue);
|
1842
2167
|
|
1843
|
-
|
2168
|
+
this[computedState].isStale = true; // Mark as dirty
|
1844
2169
|
|
1845
|
-
// Pass the observable to the
|
2170
|
+
// Pass the observable to the "limit" code, which will access it when
|
1846
2171
|
// it's time to do the notification.
|
1847
|
-
|
2172
|
+
this._limitChange(this);
|
1848
2173
|
}
|
1849
|
-
}
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
// Next, subscribe to each one
|
1870
|
-
ko.utils.arrayForEach(dependeciesOrder, function(id, order) {
|
1871
|
-
var dependency = dependencyTracking[id],
|
1872
|
-
subscription = dependency._target.subscribe(evaluatePossiblyAsync);
|
1873
|
-
subscription._order = order;
|
1874
|
-
subscription._version = dependency._version;
|
1875
|
-
dependencyTracking[id] = subscription;
|
1876
|
-
});
|
1877
|
-
}
|
1878
|
-
if (!_isDisposed) { // test since evaluating could trigger disposal
|
1879
|
-
notify(_latestValue, "awake");
|
1880
|
-
}
|
1881
|
-
}
|
1882
|
-
};
|
2174
|
+
},
|
2175
|
+
dispose: function () {
|
2176
|
+
var state = this[computedState];
|
2177
|
+
if (!state.isSleeping && state.dependencyTracking) {
|
2178
|
+
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
|
2179
|
+
if (dependency.dispose)
|
2180
|
+
dependency.dispose();
|
2181
|
+
});
|
2182
|
+
}
|
2183
|
+
if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {
|
2184
|
+
ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);
|
2185
|
+
}
|
2186
|
+
state.dependencyTracking = null;
|
2187
|
+
state.dependenciesCount = 0;
|
2188
|
+
state.isDisposed = true;
|
2189
|
+
state.isStale = false;
|
2190
|
+
state.isSleeping = false;
|
2191
|
+
state.disposeWhenNodeIsRemoved = null;
|
2192
|
+
}
|
2193
|
+
};
|
1883
2194
|
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
2195
|
+
var pureComputedOverrides = {
|
2196
|
+
beforeSubscriptionAdd: function (event) {
|
2197
|
+
// If asleep, wake up the computed by subscribing to any dependencies.
|
2198
|
+
var computedObservable = this,
|
2199
|
+
state = computedObservable[computedState];
|
2200
|
+
if (!state.isDisposed && state.isSleeping && event == 'change') {
|
2201
|
+
state.isSleeping = false;
|
2202
|
+
if (state.isStale || computedObservable.haveDependenciesChanged()) {
|
2203
|
+
state.dependencyTracking = null;
|
2204
|
+
state.dependenciesCount = 0;
|
2205
|
+
state.isStale = true;
|
2206
|
+
computedObservable.evaluateImmediate();
|
2207
|
+
} else {
|
2208
|
+
// First put the dependencies in order
|
2209
|
+
var dependeciesOrder = [];
|
2210
|
+
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
|
2211
|
+
dependeciesOrder[dependency._order] = id;
|
2212
|
+
});
|
2213
|
+
// Next, subscribe to each one
|
2214
|
+
ko.utils.arrayForEach(dependeciesOrder, function (id, order) {
|
2215
|
+
var dependency = state.dependencyTracking[id],
|
2216
|
+
subscription = computedObservable.subscribeToDependency(dependency._target);
|
2217
|
+
subscription._order = order;
|
2218
|
+
subscription._version = dependency._version;
|
2219
|
+
state.dependencyTracking[id] = subscription;
|
1895
2220
|
});
|
1896
|
-
isSleeping = true;
|
1897
|
-
notify(undefined, "asleep");
|
1898
2221
|
}
|
1899
|
-
|
1900
|
-
|
2222
|
+
if (!state.isDisposed) { // test since evaluating could trigger disposal
|
2223
|
+
computedObservable["notifySubscribers"](state.latestValue, "awake");
|
2224
|
+
}
|
2225
|
+
}
|
2226
|
+
},
|
2227
|
+
afterSubscriptionRemove: function (event) {
|
2228
|
+
var state = this[computedState];
|
2229
|
+
if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
|
2230
|
+
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
|
2231
|
+
if (dependency.dispose) {
|
2232
|
+
state.dependencyTracking[id] = {
|
2233
|
+
_target: dependency._target,
|
2234
|
+
_order: dependency._order,
|
2235
|
+
_version: dependency._version
|
2236
|
+
};
|
2237
|
+
dependency.dispose();
|
2238
|
+
}
|
2239
|
+
});
|
2240
|
+
state.isSleeping = true;
|
2241
|
+
this["notifySubscribers"](undefined, "asleep");
|
2242
|
+
}
|
2243
|
+
},
|
2244
|
+
getVersion: function () {
|
1901
2245
|
// Because a pure computed is not automatically updated while it is sleeping, we can't
|
1902
2246
|
// simply return the version number. Instead, we check if any of the dependencies have
|
1903
2247
|
// changed and conditionally re-evaluate the computed observable.
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
evaluateImmediate();
|
1908
|
-
}
|
1909
|
-
return dependentObservable._originalGetVersion();
|
1910
|
-
};
|
1911
|
-
} else if (options['deferEvaluation']) {
|
1912
|
-
// This will force a computed with deferEvaluation to evaluate when the first subscriptions is registered.
|
1913
|
-
dependentObservable.beforeSubscriptionAdd = function (event) {
|
1914
|
-
if (event == 'change' || event == 'beforeChange') {
|
1915
|
-
peek();
|
1916
|
-
}
|
2248
|
+
var state = this[computedState];
|
2249
|
+
if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) {
|
2250
|
+
this.evaluateImmediate();
|
1917
2251
|
}
|
2252
|
+
return ko.subscribable['fn'].getVersion.call(this);
|
1918
2253
|
}
|
2254
|
+
};
|
1919
2255
|
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
// Add a "disposeWhen" callback that, on each evaluation, disposes if the node was removed without using ko.removeNode.
|
1926
|
-
if (disposeWhenNodeIsRemoved) {
|
1927
|
-
// Since this computed is associated with a DOM node, and we don't want to dispose the computed
|
1928
|
-
// until the DOM node is *removed* from the document (as opposed to never having been in the document),
|
1929
|
-
// we'll prevent disposal until "disposeWhen" first returns false.
|
1930
|
-
_suppressDisposalUntilDisposeWhenReturnsFalse = true;
|
1931
|
-
|
1932
|
-
// Only watch for the node's disposal if the value really is a node. It might not be,
|
1933
|
-
// e.g., { disposeWhenNodeIsRemoved: true } can be used to opt into the "only dispose
|
1934
|
-
// after first false result" behaviour even if there's no specific node to watch. This
|
1935
|
-
// technique is intended for KO's internal use only and shouldn't be documented or used
|
1936
|
-
// by application code, as it's likely to change in a future version of KO.
|
1937
|
-
if (disposeWhenNodeIsRemoved.nodeType) {
|
1938
|
-
disposeWhen = function () {
|
1939
|
-
return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || (disposeWhenOption && disposeWhenOption());
|
1940
|
-
};
|
2256
|
+
var deferEvaluationOverrides = {
|
2257
|
+
beforeSubscriptionAdd: function (event) {
|
2258
|
+
// This will force a computed with deferEvaluation to evaluate when the first subscription is registered.
|
2259
|
+
if (event == 'change' || event == 'beforeChange') {
|
2260
|
+
this.peek();
|
1941
2261
|
}
|
1942
2262
|
}
|
1943
|
-
|
1944
|
-
// Evaluate, unless sleeping or deferEvaluation is true
|
1945
|
-
if (!isSleeping && !options['deferEvaluation'])
|
1946
|
-
evaluateImmediate();
|
1947
|
-
|
1948
|
-
// Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
|
1949
|
-
// removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
|
1950
|
-
if (disposeWhenNodeIsRemoved && isActive() && disposeWhenNodeIsRemoved.nodeType) {
|
1951
|
-
dispose = function() {
|
1952
|
-
ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, dispose);
|
1953
|
-
disposeComputed();
|
1954
|
-
};
|
1955
|
-
ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
|
1956
|
-
}
|
1957
|
-
|
1958
|
-
return dependentObservable;
|
1959
2263
|
};
|
1960
2264
|
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
2265
|
+
// Note that for browsers that don't support proto assignment, the
|
2266
|
+
// inheritance chain is created manually in the ko.computed constructor
|
2267
|
+
if (ko.utils.canSetPrototype) {
|
2268
|
+
ko.utils.setPrototypeOf(computedFn, ko.subscribable['fn']);
|
2269
|
+
}
|
1964
2270
|
|
2271
|
+
// Set the proto chain values for ko.hasPrototype
|
1965
2272
|
var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
|
1966
|
-
ko.
|
2273
|
+
ko.computed[protoProp] = ko.observable;
|
2274
|
+
computedFn[protoProp] = ko.computed;
|
1967
2275
|
|
1968
|
-
ko.
|
1969
|
-
|
2276
|
+
ko.isComputed = function (instance) {
|
2277
|
+
return ko.hasPrototype(instance, ko.computed);
|
1970
2278
|
};
|
1971
|
-
ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
|
1972
2279
|
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1977
|
-
}
|
2280
|
+
ko.isPureComputed = function (instance) {
|
2281
|
+
return ko.hasPrototype(instance, ko.computed)
|
2282
|
+
&& instance[computedState] && instance[computedState].pure;
|
2283
|
+
};
|
1978
2284
|
|
1979
|
-
ko.exportSymbol('
|
1980
|
-
ko.exportSymbol('
|
2285
|
+
ko.exportSymbol('computed', ko.computed);
|
2286
|
+
ko.exportSymbol('dependentObservable', ko.computed); // export ko.dependentObservable for backwards compatibility (1.x)
|
1981
2287
|
ko.exportSymbol('isComputed', ko.isComputed);
|
2288
|
+
ko.exportSymbol('isPureComputed', ko.isPureComputed);
|
2289
|
+
ko.exportSymbol('computed.fn', computedFn);
|
2290
|
+
ko.exportProperty(computedFn, 'peek', computedFn.peek);
|
2291
|
+
ko.exportProperty(computedFn, 'dispose', computedFn.dispose);
|
2292
|
+
ko.exportProperty(computedFn, 'isActive', computedFn.isActive);
|
2293
|
+
ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);
|
1982
2294
|
|
1983
2295
|
ko.pureComputed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget) {
|
1984
2296
|
if (typeof evaluatorFunctionOrOptions === 'function') {
|
@@ -2016,7 +2328,7 @@ ko.exportSymbol('pureComputed', ko.pureComputed);
|
|
2016
2328
|
visitedObjects = visitedObjects || new objectLookup();
|
2017
2329
|
|
2018
2330
|
rootObject = mapInputCallback(rootObject);
|
2019
|
-
var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
|
2331
|
+
var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
|
2020
2332
|
if (!canHaveProperties)
|
2021
2333
|
return rootObject;
|
2022
2334
|
|
@@ -2629,14 +2941,16 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
2629
2941
|
(function () {
|
2630
2942
|
ko.bindingHandlers = {};
|
2631
2943
|
|
2632
|
-
// The following element types will not be recursed into during binding.
|
2633
|
-
// may consider adding <template> to this list, because such elements' contents are always
|
2634
|
-
// intended to be bound in a different context from where they appear in the document.
|
2944
|
+
// The following element types will not be recursed into during binding.
|
2635
2945
|
var bindingDoesNotRecurseIntoElementTypes = {
|
2636
2946
|
// Don't want bindings that operate on text nodes to mutate <script> and <textarea> contents,
|
2637
|
-
// because it's unexpected and a potential XSS issue
|
2947
|
+
// because it's unexpected and a potential XSS issue.
|
2948
|
+
// Also bindings should not operate on <template> elements since this breaks in Internet Explorer
|
2949
|
+
// and because such elements' contents are always intended to be bound in a different context
|
2950
|
+
// from where they appear in the document.
|
2638
2951
|
'script': true,
|
2639
|
-
'textarea': true
|
2952
|
+
'textarea': true,
|
2953
|
+
'template': true
|
2640
2954
|
};
|
2641
2955
|
|
2642
2956
|
// Use an overridable method for retrieving binding handlers so that a plugins may support dynamically created handlers
|
@@ -2653,7 +2967,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
2653
2967
|
// any child contexts, must be updated when the view model is changed.
|
2654
2968
|
function updateContext() {
|
2655
2969
|
// Most of the time, the context will directly get a view model object, but if a function is given,
|
2656
|
-
// we call the function to retrieve the view model. If the function accesses any
|
2970
|
+
// we call the function to retrieve the view model. If the function accesses any observables or returns
|
2657
2971
|
// an observable, the dependency is tracked, and those observables can later cause the binding
|
2658
2972
|
// context to be updated.
|
2659
2973
|
var dataItemOrObservable = isFunc ? dataItemOrAccessor() : dataItemOrAccessor,
|
@@ -2735,7 +3049,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
2735
3049
|
}
|
2736
3050
|
|
2737
3051
|
// Extend the binding context hierarchy with a new view model object. If the parent context is watching
|
2738
|
-
// any
|
3052
|
+
// any observables, the new child context will automatically get a dependency on the parent context.
|
2739
3053
|
// But this does not mean that the $data value of the child context will also get updated. If the child
|
2740
3054
|
// view model also depends on the parent view model, you must provide a function that returns the correct
|
2741
3055
|
// view model on each update.
|
@@ -2929,7 +3243,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
2929
3243
|
var bindingsUpdater = ko.dependentObservable(
|
2930
3244
|
function() {
|
2931
3245
|
bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext);
|
2932
|
-
// Register a dependency on the binding context to support
|
3246
|
+
// Register a dependency on the binding context to support observable view models.
|
2933
3247
|
if (bindings && bindingContext._subscribable)
|
2934
3248
|
bindingContext._subscribable();
|
2935
3249
|
return bindings;
|
@@ -3107,7 +3421,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
3107
3421
|
callback(cachedDefinition.definition);
|
3108
3422
|
});
|
3109
3423
|
} else {
|
3110
|
-
|
3424
|
+
ko.tasks.schedule(function() { callback(cachedDefinition.definition); });
|
3111
3425
|
}
|
3112
3426
|
} else {
|
3113
3427
|
// Join the loading process that is already underway, or start a new one.
|
@@ -3140,19 +3454,19 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
3140
3454
|
delete loadingSubscribablesCache[componentName];
|
3141
3455
|
|
3142
3456
|
// For API consistency, all loads complete asynchronously. However we want to avoid
|
3143
|
-
// adding an extra
|
3144
|
-
// async)
|
3457
|
+
// adding an extra task schedule if it's unnecessary (i.e., the completion is already
|
3458
|
+
// async).
|
3145
3459
|
//
|
3146
|
-
// You can bypass the 'always
|
3460
|
+
// You can bypass the 'always asynchronous' feature by putting the synchronous:true
|
3147
3461
|
// flag on your component configuration when you register it.
|
3148
3462
|
if (completedAsync || isSynchronousComponent) {
|
3149
3463
|
// Note that notifySubscribers ignores any dependencies read within the callback.
|
3150
3464
|
// See comment in loaderRegistryBehaviors.js for reasoning
|
3151
3465
|
subscribable['notifySubscribers'](definition);
|
3152
3466
|
} else {
|
3153
|
-
|
3467
|
+
ko.tasks.schedule(function() {
|
3154
3468
|
subscribable['notifySubscribers'](definition);
|
3155
|
-
}
|
3469
|
+
});
|
3156
3470
|
}
|
3157
3471
|
});
|
3158
3472
|
completedAsync = true;
|
@@ -3257,16 +3571,16 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
3257
3571
|
}
|
3258
3572
|
|
3259
3573
|
defaultConfigRegistry[componentName] = config;
|
3260
|
-
}
|
3574
|
+
};
|
3261
3575
|
|
3262
3576
|
ko.components.isRegistered = function(componentName) {
|
3263
|
-
return componentName
|
3264
|
-
}
|
3577
|
+
return defaultConfigRegistry.hasOwnProperty(componentName);
|
3578
|
+
};
|
3265
3579
|
|
3266
3580
|
ko.components.unregister = function(componentName) {
|
3267
3581
|
delete defaultConfigRegistry[componentName];
|
3268
3582
|
ko.components.clearCachedDefinition(componentName);
|
3269
|
-
}
|
3583
|
+
};
|
3270
3584
|
|
3271
3585
|
ko.components.defaultLoader = {
|
3272
3586
|
'getConfig': function(componentName, callback) {
|
@@ -3464,7 +3778,12 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
3464
3778
|
// you can for example map specific tagNames to components that are not preregistered.
|
3465
3779
|
ko.components['getComponentNameForNode'] = function(node) {
|
3466
3780
|
var tagNameLower = ko.utils.tagNameLower(node);
|
3467
|
-
|
3781
|
+
if (ko.components.isRegistered(tagNameLower)) {
|
3782
|
+
// Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
|
3783
|
+
if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]" || (ko.utils.ieVersion <= 8 && node.tagName === tagNameLower)) {
|
3784
|
+
return tagNameLower;
|
3785
|
+
}
|
3786
|
+
}
|
3468
3787
|
};
|
3469
3788
|
|
3470
3789
|
ko.components.addBindingsForCustomElement = function(allBindings, node, bindingContext, valueAccessors) {
|
@@ -3581,7 +3900,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
|
|
3581
3900
|
if (typeof currentViewModelDispose === 'function') {
|
3582
3901
|
currentViewModelDispose.call(currentViewModel);
|
3583
3902
|
}
|
3584
|
-
|
3903
|
+
currentViewModel = null;
|
3585
3904
|
// Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
|
3586
3905
|
currentLoadingOperationId = null;
|
3587
3906
|
},
|
@@ -3725,21 +4044,25 @@ ko.bindingHandlers['checked'] = {
|
|
3725
4044
|
}
|
3726
4045
|
|
3727
4046
|
var modelValue = ko.dependencyDetection.ignore(valueAccessor);
|
3728
|
-
if (
|
4047
|
+
if (valueIsArray) {
|
4048
|
+
var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue;
|
3729
4049
|
if (oldElemValue !== elemValue) {
|
3730
4050
|
// When we're responding to the checkedValue changing, and the element is
|
3731
4051
|
// currently checked, replace the old elem value with the new elem value
|
3732
4052
|
// in the model array.
|
3733
4053
|
if (isChecked) {
|
3734
|
-
ko.utils.addOrRemoveItem(
|
3735
|
-
ko.utils.addOrRemoveItem(
|
4054
|
+
ko.utils.addOrRemoveItem(writableValue, elemValue, true);
|
4055
|
+
ko.utils.addOrRemoveItem(writableValue, oldElemValue, false);
|
3736
4056
|
}
|
3737
4057
|
|
3738
4058
|
oldElemValue = elemValue;
|
3739
4059
|
} else {
|
3740
4060
|
// When we're responding to the user having checked/unchecked a checkbox,
|
3741
4061
|
// add/remove the element value to the model array.
|
3742
|
-
ko.utils.addOrRemoveItem(
|
4062
|
+
ko.utils.addOrRemoveItem(writableValue, elemValue, isChecked);
|
4063
|
+
}
|
4064
|
+
if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
|
4065
|
+
modelValue(writableValue);
|
3743
4066
|
}
|
3744
4067
|
} else {
|
3745
4068
|
ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
|
@@ -3751,7 +4074,7 @@ ko.bindingHandlers['checked'] = {
|
|
3751
4074
|
// It runs in response to changes in the bound (checked) value.
|
3752
4075
|
var modelValue = ko.utils.unwrapObservable(valueAccessor());
|
3753
4076
|
|
3754
|
-
if (
|
4077
|
+
if (valueIsArray) {
|
3755
4078
|
// When a checkbox is bound to an array, being checked represents its value being present in that array
|
3756
4079
|
element.checked = ko.utils.arrayIndexOf(modelValue, checkedValue()) >= 0;
|
3757
4080
|
} else if (isCheckbox) {
|
@@ -3771,9 +4094,11 @@ ko.bindingHandlers['checked'] = {
|
|
3771
4094
|
return;
|
3772
4095
|
}
|
3773
4096
|
|
3774
|
-
var
|
3775
|
-
|
3776
|
-
|
4097
|
+
var rawValue = valueAccessor(),
|
4098
|
+
valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),
|
4099
|
+
rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),
|
4100
|
+
oldElemValue = valueIsArray ? checkedValue() : undefined,
|
4101
|
+
useCheckedValue = isRadio || valueIsArray;
|
3777
4102
|
|
3778
4103
|
// IE 6 won't allow radio buttons to be selected unless they have a name
|
3779
4104
|
if (isRadio && !element.name)
|
@@ -3787,6 +4112,8 @@ ko.bindingHandlers['checked'] = {
|
|
3787
4112
|
|
3788
4113
|
// The second responds to changes in the model value (the one associated with the checked binding)
|
3789
4114
|
ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
|
4115
|
+
|
4116
|
+
rawValue = undefined;
|
3790
4117
|
}
|
3791
4118
|
};
|
3792
4119
|
ko.expressionRewriting.twoWayBindings['checked'] = true;
|
@@ -3807,7 +4134,7 @@ ko.bindingHandlers['css'] = {
|
|
3807
4134
|
ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
|
3808
4135
|
});
|
3809
4136
|
} else {
|
3810
|
-
value = String(value || ''); // Make sure we don't try to store or set a non-string value
|
4137
|
+
value = ko.utils.stringTrim(String(value || '')); // Make sure we don't try to store or set a non-string value
|
3811
4138
|
ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
|
3812
4139
|
element[classesWrittenByBindingKey] = value;
|
3813
4140
|
ko.utils.toggleDomNodeCssClass(element, value, true);
|
@@ -3958,10 +4285,20 @@ ko.bindingHandlers['hasfocus'] = {
|
|
3958
4285
|
ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
|
3959
4286
|
},
|
3960
4287
|
'update': function(element, valueAccessor) {
|
3961
|
-
var value = !!ko.utils.unwrapObservable(valueAccessor());
|
4288
|
+
var value = !!ko.utils.unwrapObservable(valueAccessor());
|
4289
|
+
|
3962
4290
|
if (!element[hasfocusUpdatingProperty] && element[hasfocusLastValue] !== value) {
|
3963
4291
|
value ? element.focus() : element.blur();
|
3964
|
-
|
4292
|
+
|
4293
|
+
// In IE, the blur method doesn't always cause the element to lose focus (for example, if the window is not in focus).
|
4294
|
+
// Setting focus to the body element does seem to be reliable in IE, but should only be used if we know that the current
|
4295
|
+
// element was focused already.
|
4296
|
+
if (!value && element[hasfocusLastValue]) {
|
4297
|
+
element.ownerDocument.body.focus();
|
4298
|
+
}
|
4299
|
+
|
4300
|
+
// For IE, which doesn't reliably fire "focus" or "blur" events synchronously
|
4301
|
+
ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]);
|
3965
4302
|
}
|
3966
4303
|
}
|
3967
4304
|
};
|
@@ -4206,13 +4543,19 @@ ko.bindingHandlers['selectedOptions'] = {
|
|
4206
4543
|
if (ko.utils.tagNameLower(element) != "select")
|
4207
4544
|
throw new Error("values binding applies only to SELECT elements");
|
4208
4545
|
|
4209
|
-
var newValue = ko.utils.unwrapObservable(valueAccessor())
|
4546
|
+
var newValue = ko.utils.unwrapObservable(valueAccessor()),
|
4547
|
+
previousScrollTop = element.scrollTop;
|
4548
|
+
|
4210
4549
|
if (newValue && typeof newValue.length == "number") {
|
4211
4550
|
ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
|
4212
4551
|
var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
|
4213
|
-
|
4552
|
+
if (node.selected != isSelected) { // This check prevents flashing of the select element in IE
|
4553
|
+
ko.utils.setOptionNodeSelectionState(node, isSelected);
|
4554
|
+
}
|
4214
4555
|
});
|
4215
4556
|
}
|
4557
|
+
|
4558
|
+
element.scrollTop = previousScrollTop;
|
4216
4559
|
}
|
4217
4560
|
};
|
4218
4561
|
ko.expressionRewriting.twoWayBindings['selectedOptions'] = true;
|
@@ -4331,10 +4674,14 @@ ko.bindingHandlers['textInput'] = {
|
|
4331
4674
|
// such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
|
4332
4675
|
elementValueBeforeEvent = element.value;
|
4333
4676
|
var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel;
|
4334
|
-
timeoutHandle = setTimeout(handler, 4);
|
4677
|
+
timeoutHandle = ko.utils.setTimeout(handler, 4);
|
4335
4678
|
}
|
4336
4679
|
};
|
4337
4680
|
|
4681
|
+
// IE9 will mess up the DOM if you handle events synchronously which results in DOM changes (such as other bindings);
|
4682
|
+
// so we'll make sure all updates are asynchronous
|
4683
|
+
var ieUpdateModel = ko.utils.ieVersion == 9 ? deferUpdateModel : updateModel;
|
4684
|
+
|
4338
4685
|
var updateView = function () {
|
4339
4686
|
var modelValue = ko.utils.unwrapObservable(valueAccessor());
|
4340
4687
|
|
@@ -4343,7 +4690,7 @@ ko.bindingHandlers['textInput'] = {
|
|
4343
4690
|
}
|
4344
4691
|
|
4345
4692
|
if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {
|
4346
|
-
setTimeout(updateView, 4);
|
4693
|
+
ko.utils.setTimeout(updateView, 4);
|
4347
4694
|
return;
|
4348
4695
|
}
|
4349
4696
|
|
@@ -4376,7 +4723,7 @@ ko.bindingHandlers['textInput'] = {
|
|
4376
4723
|
// when using autocomplete, we'll use 'propertychange' for it also.
|
4377
4724
|
onEvent('propertychange', function(event) {
|
4378
4725
|
if (event.propertyName === 'value') {
|
4379
|
-
|
4726
|
+
ieUpdateModel(event);
|
4380
4727
|
}
|
4381
4728
|
});
|
4382
4729
|
|
@@ -4393,7 +4740,7 @@ ko.bindingHandlers['textInput'] = {
|
|
4393
4740
|
// out of the field, and cutting or deleting text using the context menu. 'selectionchange'
|
4394
4741
|
// can detect all of those except dragging text out of the field, for which we use 'dragend'.
|
4395
4742
|
// These are also needed in IE8 because of the bug described above.
|
4396
|
-
registerForSelectionChangeEvent(element,
|
4743
|
+
registerForSelectionChangeEvent(element, ieUpdateModel); // 'selectionchange' covers cut, paste, drop, delete, etc.
|
4397
4744
|
onEvent('dragend', deferUpdateModel);
|
4398
4745
|
}
|
4399
4746
|
} else {
|
@@ -4506,7 +4853,7 @@ ko.bindingHandlers['value'] = {
|
|
4506
4853
|
// techniques like rateLimit can trigger model changes at critical moments that will
|
4507
4854
|
// override the user's inputs, causing keystrokes to be lost.
|
4508
4855
|
elementValueBeforeEvent = ko.selectExtensions.readValue(element);
|
4509
|
-
setTimeout(valueUpdateHandler, 0);
|
4856
|
+
ko.utils.setTimeout(valueUpdateHandler, 0);
|
4510
4857
|
};
|
4511
4858
|
eventName = eventName.substring("after".length);
|
4512
4859
|
}
|
@@ -4518,7 +4865,7 @@ ko.bindingHandlers['value'] = {
|
|
4518
4865
|
var elementValue = ko.selectExtensions.readValue(element);
|
4519
4866
|
|
4520
4867
|
if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {
|
4521
|
-
setTimeout(updateFromModel, 0);
|
4868
|
+
ko.utils.setTimeout(updateFromModel, 0);
|
4522
4869
|
return;
|
4523
4870
|
}
|
4524
4871
|
|
@@ -4540,7 +4887,7 @@ ko.bindingHandlers['value'] = {
|
|
4540
4887
|
// Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
|
4541
4888
|
// right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
|
4542
4889
|
// to apply the value as well.
|
4543
|
-
setTimeout(applyValueAction, 0);
|
4890
|
+
ko.utils.setTimeout(applyValueAction, 0);
|
4544
4891
|
}
|
4545
4892
|
} else {
|
4546
4893
|
ko.selectExtensions.writeValue(element, newValue);
|
@@ -4732,14 +5079,29 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
|
|
4732
5079
|
|
4733
5080
|
// ---- ko.templateSources.domElement -----
|
4734
5081
|
|
5082
|
+
// template types
|
5083
|
+
var templateScript = 1,
|
5084
|
+
templateTextArea = 2,
|
5085
|
+
templateTemplate = 3,
|
5086
|
+
templateElement = 4;
|
5087
|
+
|
4735
5088
|
ko.templateSources.domElement = function(element) {
|
4736
5089
|
this.domElement = element;
|
5090
|
+
|
5091
|
+
if (element) {
|
5092
|
+
var tagNameLower = ko.utils.tagNameLower(element);
|
5093
|
+
this.templateType =
|
5094
|
+
tagNameLower === "script" ? templateScript :
|
5095
|
+
tagNameLower === "textarea" ? templateTextArea :
|
5096
|
+
// For browsers with proper <template> element support, where the .content property gives a document fragment
|
5097
|
+
tagNameLower == "template" && element.content && element.content.nodeType === 11 ? templateTemplate :
|
5098
|
+
templateElement;
|
5099
|
+
}
|
4737
5100
|
}
|
4738
5101
|
|
4739
5102
|
ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
|
4740
|
-
var
|
4741
|
-
|
4742
|
-
: tagNameLower === "textarea" ? "value"
|
5103
|
+
var elemContentsProperty = this.templateType === templateScript ? "text"
|
5104
|
+
: this.templateType === templateTextArea ? "value"
|
4743
5105
|
: "innerHTML";
|
4744
5106
|
|
4745
5107
|
if (arguments.length == 0) {
|
@@ -4762,12 +5124,34 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
|
|
4762
5124
|
}
|
4763
5125
|
};
|
4764
5126
|
|
5127
|
+
var templatesDomDataKey = ko.utils.domData.nextKey();
|
5128
|
+
function getTemplateDomData(element) {
|
5129
|
+
return ko.utils.domData.get(element, templatesDomDataKey) || {};
|
5130
|
+
}
|
5131
|
+
function setTemplateDomData(element, data) {
|
5132
|
+
ko.utils.domData.set(element, templatesDomDataKey, data);
|
5133
|
+
}
|
5134
|
+
|
5135
|
+
ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
|
5136
|
+
var element = this.domElement;
|
5137
|
+
if (arguments.length == 0) {
|
5138
|
+
var templateData = getTemplateDomData(element),
|
5139
|
+
containerData = templateData.containerData;
|
5140
|
+
return containerData || (
|
5141
|
+
this.templateType === templateTemplate ? element.content :
|
5142
|
+
this.templateType === templateElement ? element :
|
5143
|
+
undefined);
|
5144
|
+
} else {
|
5145
|
+
var valueToWrite = arguments[0];
|
5146
|
+
setTemplateDomData(element, {containerData: valueToWrite});
|
5147
|
+
}
|
5148
|
+
};
|
5149
|
+
|
4765
5150
|
// ---- ko.templateSources.anonymousTemplate -----
|
4766
5151
|
// Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
|
4767
5152
|
// For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
|
4768
5153
|
// Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
|
4769
5154
|
|
4770
|
-
var anonymousTemplatesDomDataKey = ko.utils.domData.nextKey();
|
4771
5155
|
ko.templateSources.anonymousTemplate = function(element) {
|
4772
5156
|
this.domElement = element;
|
4773
5157
|
}
|
@@ -4775,22 +5159,13 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
|
|
4775
5159
|
ko.templateSources.anonymousTemplate.prototype.constructor = ko.templateSources.anonymousTemplate;
|
4776
5160
|
ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
|
4777
5161
|
if (arguments.length == 0) {
|
4778
|
-
var templateData =
|
5162
|
+
var templateData = getTemplateDomData(this.domElement);
|
4779
5163
|
if (templateData.textData === undefined && templateData.containerData)
|
4780
5164
|
templateData.textData = templateData.containerData.innerHTML;
|
4781
5165
|
return templateData.textData;
|
4782
5166
|
} else {
|
4783
5167
|
var valueToWrite = arguments[0];
|
4784
|
-
|
4785
|
-
}
|
4786
|
-
};
|
4787
|
-
ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
|
4788
|
-
if (arguments.length == 0) {
|
4789
|
-
var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
|
4790
|
-
return templateData.containerData;
|
4791
|
-
} else {
|
4792
|
-
var valueToWrite = arguments[0];
|
4793
|
-
ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
|
5168
|
+
setTemplateDomData(this.domElement, {textData: valueToWrite});
|
4794
5169
|
}
|
4795
5170
|
};
|
4796
5171
|
|
@@ -5132,7 +5507,7 @@ ko.utils.compareArrays = (function () {
|
|
5132
5507
|
oldArray = oldArray || [];
|
5133
5508
|
newArray = newArray || [];
|
5134
5509
|
|
5135
|
-
if (oldArray.length
|
5510
|
+
if (oldArray.length < newArray.length)
|
5136
5511
|
return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, options);
|
5137
5512
|
else
|
5138
5513
|
return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, options);
|
@@ -5195,7 +5570,7 @@ ko.utils.compareArrays = (function () {
|
|
5195
5570
|
|
5196
5571
|
// Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
|
5197
5572
|
// smlIndexMax keeps the time complexity of this algorithm linear.
|
5198
|
-
ko.utils.findMovesInArrayComparison(
|
5573
|
+
ko.utils.findMovesInArrayComparison(notInBig, notInSml, !options['dontLimitMoves'] && smlIndexMax * 10);
|
5199
5574
|
|
5200
5575
|
return editScript.reverse();
|
5201
5576
|
}
|
@@ -5236,7 +5611,8 @@ ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
|
|
5236
5611
|
return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
|
5237
5612
|
}
|
5238
5613
|
|
5239
|
-
var lastMappingResultDomDataKey = ko.utils.domData.nextKey()
|
5614
|
+
var lastMappingResultDomDataKey = ko.utils.domData.nextKey(),
|
5615
|
+
deletedItemDummyValue = ko.utils.domData.nextKey();
|
5240
5616
|
|
5241
5617
|
ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
|
5242
5618
|
// Compare the provided array against the previous one
|
@@ -5290,14 +5666,25 @@ ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
|
|
5290
5666
|
mapData = lastMappingResult[lastMappingResultIndex];
|
5291
5667
|
|
5292
5668
|
// Stop tracking changes to the mapping for these nodes
|
5293
|
-
if (mapData.dependentObservable)
|
5669
|
+
if (mapData.dependentObservable) {
|
5294
5670
|
mapData.dependentObservable.dispose();
|
5671
|
+
mapData.dependentObservable = undefined;
|
5672
|
+
}
|
5295
5673
|
|
5296
5674
|
// Queue these nodes for later removal
|
5297
|
-
|
5298
|
-
|
5299
|
-
|
5300
|
-
|
5675
|
+
if (ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode).length) {
|
5676
|
+
if (options['beforeRemove']) {
|
5677
|
+
newMappingResult.push(mapData);
|
5678
|
+
itemsToProcess.push(mapData);
|
5679
|
+
if (mapData.arrayEntry === deletedItemDummyValue) {
|
5680
|
+
mapData = null;
|
5681
|
+
} else {
|
5682
|
+
itemsForBeforeRemoveCallbacks[i] = mapData;
|
5683
|
+
}
|
5684
|
+
}
|
5685
|
+
if (mapData) {
|
5686
|
+
nodesToDelete.push.apply(nodesToDelete, mapData.mappedNodes);
|
5687
|
+
}
|
5301
5688
|
}
|
5302
5689
|
}
|
5303
5690
|
lastMappingResultIndex++;
|
@@ -5321,6 +5708,9 @@ ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
|
|
5321
5708
|
}
|
5322
5709
|
}
|
5323
5710
|
|
5711
|
+
// Store a copy of the array items we just considered so we can difference it next time
|
5712
|
+
ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
|
5713
|
+
|
5324
5714
|
// Call beforeMove first before any changes have been made to the DOM
|
5325
5715
|
callCallback(options['beforeMove'], itemsForMoveCallbacks);
|
5326
5716
|
|
@@ -5353,12 +5743,18 @@ ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
|
|
5353
5743
|
// Perhaps we'll make that change in the future if this scenario becomes more common.
|
5354
5744
|
callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
|
5355
5745
|
|
5746
|
+
// Replace the stored values of deleted items with a dummy value. This provides two benefits: it marks this item
|
5747
|
+
// as already "removed" so we won't call beforeRemove for it again, and it ensures that the item won't match up
|
5748
|
+
// with an actual item in the array and appear as "retained" or "moved".
|
5749
|
+
for (i = 0; i < itemsForBeforeRemoveCallbacks.length; ++i) {
|
5750
|
+
if (itemsForBeforeRemoveCallbacks[i]) {
|
5751
|
+
itemsForBeforeRemoveCallbacks[i].arrayEntry = deletedItemDummyValue;
|
5752
|
+
}
|
5753
|
+
}
|
5754
|
+
|
5356
5755
|
// Finally call afterMove and afterAdd callbacks
|
5357
5756
|
callCallback(options['afterMove'], itemsForMoveCallbacks);
|
5358
5757
|
callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
|
5359
|
-
|
5360
|
-
// Store a copy of the array items we just considered so we can difference it next time
|
5361
|
-
ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
|
5362
5758
|
}
|
5363
5759
|
})();
|
5364
5760
|
|