knockoutjs-rails 3.3.0.1 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|