@tko/utils 4.0.0-alpha7.4 → 4.0.0-beta1.3

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.
Files changed (59) hide show
  1. package/LICENSE +22 -0
  2. package/dist/array.js +156 -0
  3. package/dist/array.js.map +7 -0
  4. package/dist/async.js +20 -0
  5. package/dist/async.js.map +7 -0
  6. package/dist/bind-shim.js +18 -0
  7. package/dist/bind-shim.js.map +7 -0
  8. package/dist/css.js +27 -0
  9. package/dist/css.js.map +7 -0
  10. package/dist/dom/data.js +63 -0
  11. package/dist/dom/data.js.map +7 -0
  12. package/dist/dom/disposal.js +98 -0
  13. package/dist/dom/disposal.js.map +7 -0
  14. package/dist/dom/event.js +87 -0
  15. package/dist/dom/event.js.map +7 -0
  16. package/dist/dom/fixes.js +45 -0
  17. package/dist/dom/fixes.js.map +7 -0
  18. package/dist/dom/html.js +115 -0
  19. package/dist/dom/html.js.map +7 -0
  20. package/dist/dom/info.js +43 -0
  21. package/dist/dom/info.js.map +7 -0
  22. package/dist/dom/manipulation.js +55 -0
  23. package/dist/dom/manipulation.js.map +7 -0
  24. package/dist/dom/selectExtensions.js +62 -0
  25. package/dist/dom/selectExtensions.js.map +7 -0
  26. package/dist/dom/virtualElements.js +202 -0
  27. package/dist/dom/virtualElements.js.map +7 -0
  28. package/dist/error.js +22 -0
  29. package/dist/error.js.map +7 -0
  30. package/dist/function.js +16 -0
  31. package/dist/function.js.map +7 -0
  32. package/dist/ie.js +15 -0
  33. package/dist/ie.js.map +7 -0
  34. package/dist/index.cjs +1448 -0
  35. package/dist/index.cjs.map +7 -0
  36. package/dist/index.js +24 -0
  37. package/dist/index.js.map +7 -0
  38. package/dist/index.mjs +24 -0
  39. package/dist/index.mjs.map +7 -0
  40. package/dist/jquery.js +6 -0
  41. package/dist/jquery.js.map +7 -0
  42. package/dist/memoization.js +64 -0
  43. package/dist/memoization.js.map +7 -0
  44. package/dist/object.js +76 -0
  45. package/dist/object.js.map +7 -0
  46. package/dist/options.js +36 -0
  47. package/dist/options.js.map +7 -0
  48. package/dist/string.js +23 -0
  49. package/dist/string.js.map +7 -0
  50. package/dist/symbol.js +5 -0
  51. package/dist/symbol.js.map +7 -0
  52. package/dist/tasks.js +76 -0
  53. package/dist/tasks.js.map +7 -0
  54. package/helpers/jasmine-13-helper.ts +207 -0
  55. package/package.json +18 -26
  56. package/dist/utils.es6.js +0 -1628
  57. package/dist/utils.es6.js.map +0 -1
  58. package/dist/utils.js +0 -1645
  59. package/dist/utils.js.map +0 -1
package/dist/utils.es6.js DELETED
@@ -1,1628 +0,0 @@
1
- /*!
2
- * TKO Utilities 🥊 @tko/utils@4.0.0-alpha7.4
3
- * (c) The Knockout.js Team - https://tko.io
4
- * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5
- */
6
-
7
- //
8
- // Array utilities
9
- //
10
- // Note that the array functions may be called with
11
- // Array-like things, such as NodeList.
12
-
13
- const {isArray} = Array;
14
-
15
- function arrayForEach (array, action, thisArg) {
16
- if (arguments.length > 2) { action = action.bind(thisArg); }
17
- for (let i = 0, j = array.length; i < j; ++i) {
18
- action(array[i], i, array);
19
- }
20
- }
21
-
22
- function arrayIndexOf (array, item) {
23
- return (isArray(array) ? array : [...array]).indexOf(item)
24
- }
25
-
26
- function arrayFirst (array, predicate, predicateOwner) {
27
- return (isArray(array) ? array : [...array])
28
- .find(predicate, predicateOwner)
29
- }
30
-
31
- function arrayMap (array = [], mapping, thisArg) {
32
- if (arguments.length > 2) { mapping = mapping.bind(thisArg); }
33
- return array === null ? [] : Array.from(array, mapping)
34
- }
35
-
36
- function arrayRemoveItem (array, itemToRemove) {
37
- var index = arrayIndexOf(array, itemToRemove);
38
- if (index > 0) {
39
- array.splice(index, 1);
40
- } else if (index === 0) {
41
- array.shift();
42
- }
43
- }
44
-
45
- function arrayGetDistinctValues (array = []) {
46
- const seen = new Set();
47
- if (array === null) { return [] }
48
- return (isArray(array) ? array : [...array])
49
- .filter(item => seen.has(item) ? false : seen.add(item))
50
- }
51
-
52
- function arrayFilter (array, predicate, thisArg) {
53
- if (arguments.length > 2) { predicate = predicate.bind(thisArg); }
54
- return array === null ? [] : (isArray(array) ? array : [...array]).filter(predicate)
55
- }
56
-
57
- function arrayPushAll (array, valuesToPush) {
58
- if (isArray(valuesToPush)) {
59
- array.push.apply(array, valuesToPush);
60
- } else {
61
- for (var i = 0, j = valuesToPush.length; i < j; i++) { array.push(valuesToPush[i]); }
62
- }
63
- return array
64
- }
65
-
66
- function addOrRemoveItem (array, value, included) {
67
- var existingEntryIndex = arrayIndexOf(typeof array.peek === 'function' ? array.peek() : array, value);
68
- if (existingEntryIndex < 0) {
69
- if (included) { array.push(value); }
70
- } else {
71
- if (!included) { array.splice(existingEntryIndex, 1); }
72
- }
73
- }
74
-
75
- function makeArray (arrayLikeObject) {
76
- return Array.from(arrayLikeObject)
77
- }
78
-
79
- function range (min, max) {
80
- min = typeof min === 'function' ? min() : min;
81
- max = typeof max === 'function' ? max() : max;
82
- var result = [];
83
- for (var i = min; i <= max; i++) { result.push(i); }
84
- return result
85
- }
86
-
87
- // Go through the items that have been added and deleted and try to find matches between them.
88
- function findMovesInArrayComparison (left, right, limitFailedCompares) {
89
- if (left.length && right.length) {
90
- var failedCompares, l, r, leftItem, rightItem;
91
- for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
92
- for (r = 0; rightItem = right[r]; ++r) {
93
- if (leftItem.value === rightItem.value) {
94
- leftItem.moved = rightItem.index;
95
- rightItem.moved = leftItem.index;
96
- right.splice(r, 1); // This item is marked as moved; so remove it from right list
97
- failedCompares = r = 0; // Reset failed compares count because we're checking for consecutive failures
98
- break
99
- }
100
- }
101
- failedCompares += r;
102
- }
103
- }
104
- }
105
-
106
- const statusNotInOld = 'added';
107
- const statusNotInNew = 'deleted';
108
-
109
- // Simple calculation based on Levenshtein distance.
110
- function compareArrays (oldArray, newArray, options) {
111
- // For backward compatibility, if the third arg is actually a bool, interpret
112
- // it as the old parameter 'dontLimitMoves'. Newer code should use { dontLimitMoves: true }.
113
- options = (typeof options === 'boolean') ? { dontLimitMoves: options } : (options || {});
114
- oldArray = oldArray || [];
115
- newArray = newArray || [];
116
-
117
- if (oldArray.length < newArray.length) { return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, options) } else { return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, options) }
118
- }
119
-
120
- function compareSmallArrayToBigArray (smlArray, bigArray, statusNotInSml, statusNotInBig, options) {
121
- var myMin = Math.min,
122
- myMax = Math.max,
123
- editDistanceMatrix = [],
124
- smlIndex, smlIndexMax = smlArray.length,
125
- bigIndex, bigIndexMax = bigArray.length,
126
- compareRange = (bigIndexMax - smlIndexMax) || 1,
127
- maxDistance = smlIndexMax + bigIndexMax + 1,
128
- thisRow, lastRow,
129
- bigIndexMaxForRow, bigIndexMinForRow;
130
-
131
- for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
132
- lastRow = thisRow;
133
- editDistanceMatrix.push(thisRow = []);
134
- bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
135
- bigIndexMinForRow = myMax(0, smlIndex - 1);
136
- for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
137
- if (!bigIndex) {
138
- thisRow[bigIndex] = smlIndex + 1;
139
- } else if (!smlIndex) {
140
- // Top row - transform empty array into new array via additions
141
- thisRow[bigIndex] = bigIndex + 1;
142
- } else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1]) {
143
- thisRow[bigIndex] = lastRow[bigIndex - 1];
144
- } else { // copy value (no edit)
145
- var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
146
- var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
147
- thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
148
- }
149
- }
150
- }
151
-
152
- var editScript = [], meMinusOne, notInSml = [], notInBig = [];
153
- for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
154
- meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
155
- if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex - 1]) {
156
- notInSml.push(editScript[editScript.length] = { // added
157
- 'status': statusNotInSml,
158
- 'value': bigArray[--bigIndex],
159
- 'index': bigIndex });
160
- } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
161
- notInBig.push(editScript[editScript.length] = { // deleted
162
- 'status': statusNotInBig,
163
- 'value': smlArray[--smlIndex],
164
- 'index': smlIndex });
165
- } else {
166
- --bigIndex;
167
- --smlIndex;
168
- if (!options.sparse) {
169
- editScript.push({
170
- 'status': 'retained',
171
- 'value': bigArray[bigIndex] });
172
- }
173
- }
174
- }
175
-
176
- // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
177
- // smlIndexMax keeps the time complexity of this algorithm linear.
178
- findMovesInArrayComparison(notInBig, notInSml, !options.dontLimitMoves && smlIndexMax * 10);
179
-
180
- return editScript.reverse()
181
- }
182
-
183
- //
184
- // This becomes ko.options
185
- // --
186
- //
187
- // This is the root 'options', which must be extended by others.
188
-
189
- var _global;
190
-
191
- try { _global = window; } catch (e) { _global = global; }
192
-
193
- var options = {
194
- deferUpdates: false,
195
-
196
- useOnlyNativeEvents: false,
197
-
198
- protoProperty: '__ko_proto__',
199
-
200
- // Modify the default attribute from `data-bind`.
201
- defaultBindingAttribute: 'data-bind',
202
-
203
- // Enable/disable <!-- ko binding: ... -> style bindings
204
- allowVirtualElements: true,
205
-
206
- // Global variables that can be accessed from bindings.
207
- bindingGlobals: _global,
208
-
209
- // An instance of the binding provider.
210
- bindingProviderInstance: null,
211
-
212
- // Whether the `with` binding creates a child context when used with `as`.
213
- createChildContextWithAs: false,
214
-
215
- // jQuery will be automatically set to _global.jQuery in applyBindings
216
- // if it is (strictly equal to) undefined. Set it to false or null to
217
- // disable automatically setting jQuery.
218
- jQuery: _global && _global.jQuery,
219
-
220
- Promise: _global && _global.Promise,
221
-
222
- taskScheduler: null,
223
-
224
- debug: false,
225
-
226
- global: _global,
227
- document: _global.document,
228
-
229
- // Filters for bindings
230
- // data-bind="expression | filter_1 | filter_2"
231
- filters: {},
232
-
233
- // Used by the template binding.
234
- includeDestroyed: false,
235
- foreachHidesDestroyed: false,
236
-
237
- onError: function (e) { throw e },
238
-
239
- set: function (name, value) {
240
- options[name] = value;
241
- },
242
-
243
- // Overload getBindingHandler to have a custom lookup function.
244
- getBindingHandler (/* key */) {},
245
- cleanExternalData (/* node, callback */) {}
246
- };
247
-
248
- Object.defineProperty(options, '$', {
249
- get: function () { return options.jQuery }
250
- });
251
-
252
- //
253
-
254
- function catchFunctionErrors (delegate) {
255
- if (!options.onError) { return delegate }
256
- return (...args) => {
257
- try {
258
- return delegate(...args)
259
- } catch (err) {
260
- options.onError(err);
261
- }
262
- }
263
- }
264
-
265
- function deferError (error) {
266
- safeSetTimeout(function () { throw error }, 0);
267
- }
268
-
269
- function safeSetTimeout (handler, timeout) {
270
- return setTimeout(catchFunctionErrors(handler), timeout)
271
- }
272
-
273
- //
274
-
275
- function throttle (callback, timeout) {
276
- var timeoutInstance;
277
- return function (...args) {
278
- if (!timeoutInstance) {
279
- timeoutInstance = safeSetTimeout(function () {
280
- timeoutInstance = undefined;
281
- callback(...args);
282
- }, timeout);
283
- }
284
- }
285
- }
286
-
287
- function debounce (callback, timeout) {
288
- var timeoutInstance;
289
- return function (...args) {
290
- clearTimeout(timeoutInstance);
291
- timeoutInstance = safeSetTimeout(() => callback(...args), timeout);
292
- }
293
- }
294
-
295
- //
296
-
297
- const ieVersion = options.document && (function () {
298
- var version = 3, div = options.document.createElement('div'), iElems = div.getElementsByTagName('i');
299
-
300
- // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
301
- while (
302
- div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
303
- iElems[0]
304
- ) {}
305
-
306
- if (!version) {
307
- const userAgent = window.navigator.userAgent;
308
- // Detect IE 10/11
309
- return ua.match(/MSIE ([^ ]+)/) || ua.match(/rv:([^ )]+)/)
310
- }
311
- return version > 4 ? version : undefined
312
- }());
313
-
314
- const isIe6 = ieVersion === 6;
315
- const isIe7 = ieVersion === 7;
316
-
317
- //
318
- // Object functions
319
- //
320
-
321
- function hasOwnProperty(obj, propName) {
322
- return Object.prototype.hasOwnProperty.call(obj, propName)
323
- }
324
-
325
- function extend (target, source) {
326
- if (source) {
327
- for (var prop in source) {
328
- if (hasOwnProperty(source, prop)) {
329
- target[prop] = source[prop];
330
- }
331
- }
332
- }
333
- return target
334
- }
335
-
336
- function objectForEach (obj, action) {
337
- for (var prop in obj) {
338
- if (hasOwnProperty(obj, prop)) {
339
- action(prop, obj[prop]);
340
- }
341
- }
342
- }
343
-
344
- function objectMap (source, mapping, thisArg) {
345
- if (!source) { return source }
346
- if (arguments.length > 2) { mapping = mapping.bind(thisArg); }
347
- var target = {};
348
- for (var prop in source) {
349
- if (hasOwnProperty(source, prop)) {
350
- target[prop] = mapping(source[prop], prop, source);
351
- }
352
- }
353
- return target
354
- }
355
- function getObjectOwnProperty (obj, propName) {
356
- return hasOwnProperty(obj, propName) ? obj[propName] : undefined
357
- }
358
-
359
- function clonePlainObjectDeep (obj, seen) {
360
- if (!seen) { seen = []; }
361
-
362
- if (!obj || typeof obj !== 'object' ||
363
- obj.constructor !== Object ||
364
- seen.indexOf(obj) !== -1) {
365
- return obj
366
- }
367
-
368
- // Anything that makes it below is a plain object that has not yet
369
- // been seen/cloned.
370
- seen.push(obj);
371
-
372
- var result = {};
373
- for (var prop in obj) {
374
- if (hasOwnProperty(obj, prop)) {
375
- result[prop] = clonePlainObjectDeep(obj[prop], seen);
376
- }
377
- }
378
- return result
379
- }
380
-
381
- /**
382
- * JSON.stringify, but inserts `...` for objects that are referenced
383
- * multiple times, preventing infinite recursion.
384
- */
385
- function safeStringify (value) {
386
- const seen = new Set();
387
- return JSON.stringify(value, (k, v) => {
388
- if (seen.has(v)) { return '...' }
389
- if (typeof v === 'object') { seen.add(v); }
390
- return v
391
- })
392
- }
393
-
394
-
395
- /**
396
- * Promises/A+ compliant isThenable (per section 1.2)
397
- */
398
- function isThenable (object) {
399
- const objectType = typeof object;
400
- const thenableType = objectType === 'object' || objectType === 'function';
401
- return thenableType && object !== null && typeof object.then === 'function'
402
- }
403
-
404
- function testOverwrite () {
405
- try {
406
- Object.defineProperty(function x () {}, 'length', {});
407
- return true
408
- } catch (e) {
409
- return false
410
- }
411
- }
412
-
413
- const functionSupportsLengthOverwrite = testOverwrite();
414
-
415
- function overwriteLengthPropertyIfSupported (fn, descriptor) {
416
- if (functionSupportsLengthOverwrite) {
417
- Object.defineProperty(fn, 'length', descriptor);
418
- }
419
- }
420
-
421
- //
422
- // String (and JSON)
423
- //
424
-
425
- function stringTrim (string) {
426
- return string === null || string === undefined ? ''
427
- : string.trim
428
- ? string.trim()
429
- : string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, '')
430
- }
431
-
432
- function stringStartsWith (string, startsWith) {
433
- string = string || '';
434
- if (startsWith.length > string.length) { return false }
435
- return string.substring(0, startsWith.length) === startsWith
436
- }
437
-
438
- function parseJson (jsonString) {
439
- if (typeof jsonString === 'string') {
440
- jsonString = stringTrim(jsonString);
441
- if (jsonString) {
442
- if (JSON && JSON.parse) // Use native parsing where available
443
- { return JSON.parse(jsonString) }
444
- return (new Function('return ' + jsonString))() // Fallback on less safe parsing for older browsers
445
- }
446
- }
447
- return null
448
- }
449
-
450
- //
451
- // ES6 Symbols
452
- //
453
-
454
- var useSymbols = typeof Symbol === 'function';
455
-
456
- function createSymbolOrString (identifier) {
457
- return useSymbols ? Symbol(identifier) : identifier
458
- }
459
-
460
- //
461
-
462
- // For details on the pattern for changing node classes
463
- // see: https://github.com/knockout/knockout/issues/1597
464
- var cssClassNameRegex = /\S+/g;
465
-
466
- function toggleDomNodeCssClass (node, classNames, shouldHaveClass) {
467
- var addOrRemoveFn;
468
- if (!classNames) { return }
469
- if (typeof node.classList === 'object') {
470
- addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];
471
- arrayForEach(classNames.match(cssClassNameRegex), function (className) {
472
- addOrRemoveFn.call(node.classList, className);
473
- });
474
- } else if (typeof node.className['baseVal'] === 'string') {
475
- // SVG tag .classNames is an SVGAnimatedString instance
476
- toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);
477
- } else {
478
- // node.className ought to be a string.
479
- toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);
480
- }
481
- }
482
-
483
- function toggleObjectClassPropertyString (obj, prop, classNames, shouldHaveClass) {
484
- // obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.
485
- var currentClassNames = obj[prop].match(cssClassNameRegex) || [];
486
- arrayForEach(classNames.match(cssClassNameRegex), function (className) {
487
- addOrRemoveItem(currentClassNames, className, shouldHaveClass);
488
- });
489
- obj[prop] = currentClassNames.join(' ');
490
- }
491
-
492
- //
493
-
494
- var jQueryInstance = options.global && options.global.jQuery;
495
-
496
- function jQuerySetInstance (jquery) {
497
- options.jQuery = jQueryInstance = jquery;
498
- }
499
-
500
- //
501
-
502
- function domNodeIsContainedBy (node, containedByNode) {
503
- if (node === containedByNode) { return true }
504
- if (node.nodeType === 11) { return false } // Fixes issue #1162 - can't use node.contains for document fragments on IE8
505
- if (containedByNode.contains) { return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node) }
506
- if (containedByNode.compareDocumentPosition) { return (containedByNode.compareDocumentPosition(node) & 16) == 16 }
507
- while (node && node != containedByNode) {
508
- node = node.parentNode;
509
- }
510
- return !!node
511
- }
512
-
513
- function domNodeIsAttachedToDocument (node) {
514
- return domNodeIsContainedBy(node, node.ownerDocument.documentElement)
515
- }
516
-
517
- function anyDomNodeIsAttachedToDocument (nodes) {
518
- return !!arrayFirst(nodes, domNodeIsAttachedToDocument)
519
- }
520
-
521
- function tagNameLower (element) {
522
- // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
523
- // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
524
- // we don't need to do the .toLowerCase() as it will always be lower case anyway.
525
- return element && element.tagName && element.tagName.toLowerCase()
526
- }
527
-
528
- function isDomElement (obj) {
529
- if (window.HTMLElement) {
530
- return obj instanceof HTMLElement
531
- } else {
532
- return obj && obj.tagName && obj.nodeType === 1
533
- }
534
- }
535
-
536
- function isDocumentFragment (obj) {
537
- if (window.DocumentFragment) {
538
- return obj instanceof DocumentFragment
539
- } else {
540
- return obj && obj.nodeType === 11
541
- }
542
- }
543
-
544
- //
545
-
546
- const datastoreTime = new Date().getTime();
547
- const dataStoreKeyExpandoPropertyName = `__ko__${datastoreTime}`;
548
- const dataStoreSymbol = Symbol('Knockout data');
549
- var dataStore;
550
- let uniqueId = 0;
551
-
552
- /*
553
- * We considered using WeakMap, but it has a problem in IE 11 and Edge that
554
- * prevents using it cross-window, so instead we just store the data directly
555
- * on the node. See https://github.com/knockout/knockout/issues/2141
556
- */
557
- const modern = {
558
- getDataForNode (node, createIfNotFound) {
559
- let dataForNode = node[dataStoreSymbol];
560
- if (!dataForNode && createIfNotFound) {
561
- dataForNode = node[dataStoreSymbol] = {};
562
- }
563
- return dataForNode
564
- },
565
-
566
- clear (node) {
567
- if (node[dataStoreSymbol]) {
568
- delete node[dataStoreSymbol];
569
- return true
570
- }
571
- return false
572
- }
573
- };
574
-
575
- /**
576
- * Old IE versions have memory issues if you store objects on the node, so we
577
- * use a separate data storage and link to it from the node using a string key.
578
- */
579
- const IE = {
580
- getDataforNode (node, createIfNotFound) {
581
- let dataStoreKey = node[dataStoreKeyExpandoPropertyName];
582
- const hasExistingDataStore = dataStoreKey && (dataStoreKey !== 'null') && dataStore[dataStoreKey];
583
- if (!hasExistingDataStore) {
584
- if (!createIfNotFound) {
585
- return undefined
586
- }
587
- dataStoreKey = node[dataStoreKeyExpandoPropertyName] = 'ko' + uniqueId++;
588
- dataStore[dataStoreKey] = {};
589
- }
590
- return dataStore[dataStoreKey]
591
- },
592
-
593
- clear (node) {
594
- const dataStoreKey = node[dataStoreKeyExpandoPropertyName];
595
- if (dataStoreKey) {
596
- delete dataStore[dataStoreKey];
597
- node[dataStoreKeyExpandoPropertyName] = null;
598
- return true // Exposing 'did clean' flag purely so specs can infer whether things have been cleaned up as intended
599
- }
600
- return false
601
- }
602
- };
603
-
604
- const {getDataForNode, clear} = ieVersion ? IE : modern;
605
-
606
- /**
607
- * Create a unique key-string identifier.
608
- */
609
- function nextKey () {
610
- return (uniqueId++) + dataStoreKeyExpandoPropertyName
611
- }
612
-
613
- function get (node, key) {
614
- const dataForNode = getDataForNode(node, false);
615
- return dataForNode && dataForNode[key]
616
- }
617
-
618
- function set (node, key, value) {
619
- // Make sure we don't actually create a new domData key if we are actually deleting a value
620
- var dataForNode = getDataForNode(node, value !== undefined /* createIfNotFound */);
621
- dataForNode && (dataForNode[key] = value);
622
- }
623
-
624
- function getOrSet (node, key, value) {
625
- const dataForNode = getDataForNode(node, true, /* createIfNotFound */);
626
- return dataForNode[key] || (dataForNode[key] = value)
627
- }
628
-
629
- var data = /*#__PURE__*/Object.freeze({
630
- nextKey: nextKey,
631
- get: get,
632
- set: set,
633
- getOrSet: getOrSet,
634
- clear: clear
635
- });
636
-
637
- //
638
-
639
- var domDataKey = nextKey();
640
- // Node types:
641
- // 1: Element
642
- // 8: Comment
643
- // 9: Document
644
- var cleanableNodeTypes = { 1: true, 8: true, 9: true };
645
- var cleanableNodeTypesWithDescendants = { 1: true, 9: true };
646
-
647
- function getDisposeCallbacksCollection (node, createIfNotFound) {
648
- var allDisposeCallbacks = get(node, domDataKey);
649
- if ((allDisposeCallbacks === undefined) && createIfNotFound) {
650
- allDisposeCallbacks = [];
651
- set(node, domDataKey, allDisposeCallbacks);
652
- }
653
- return allDisposeCallbacks
654
- }
655
- function destroyCallbacksCollection (node) {
656
- set(node, domDataKey, undefined);
657
- }
658
-
659
- function cleanSingleNode (node) {
660
- // Run all the dispose callbacks
661
- var callbacks = getDisposeCallbacksCollection(node, false);
662
- if (callbacks) {
663
- callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
664
- for (let i = 0; i < callbacks.length; i++) { callbacks[i](node); }
665
- }
666
-
667
- // Erase the DOM data
668
- clear(node);
669
-
670
- // Perform cleanup needed by external libraries (currently only jQuery, but can be extended)
671
- for (let i = 0, j = otherNodeCleanerFunctions.length; i < j; ++i) {
672
- otherNodeCleanerFunctions[i](node);
673
- }
674
-
675
- if (options.cleanExternalData) {
676
- options.cleanExternalData(node);
677
- }
678
-
679
- // Clear any immediate-child comment nodes, as these wouldn't have been found by
680
- // node.getElementsByTagName('*') in cleanNode() (comment nodes aren't elements)
681
- if (cleanableNodeTypesWithDescendants[node.nodeType]) {
682
- cleanNodesInList(node.childNodes, true /* onlyComments */);
683
- }
684
- }
685
-
686
- function cleanNodesInList (nodeList, onlyComments) {
687
- const cleanedNodes = [];
688
- let lastCleanedNode;
689
- for (var i = 0; i < nodeList.length; i++) {
690
- if (!onlyComments || nodeList[i].nodeType === 8) {
691
- cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]);
692
- if (nodeList[i] !== lastCleanedNode) {
693
- while (i-- && arrayIndexOf(cleanedNodes, nodeList[i]) === -1) {}
694
- }
695
- }
696
- }
697
- }
698
-
699
- // Exports
700
- function addDisposeCallback (node, callback) {
701
- if (typeof callback !== 'function') { throw new Error('Callback must be a function') }
702
- getDisposeCallbacksCollection(node, true).push(callback);
703
- }
704
-
705
- function removeDisposeCallback (node, callback) {
706
- var callbacksCollection = getDisposeCallbacksCollection(node, false);
707
- if (callbacksCollection) {
708
- arrayRemoveItem(callbacksCollection, callback);
709
- if (callbacksCollection.length === 0) { destroyCallbacksCollection(node); }
710
- }
711
- }
712
-
713
- function cleanNode (node) {
714
- // First clean this node, where applicable
715
- if (cleanableNodeTypes[node.nodeType]) {
716
- cleanSingleNode(node);
717
-
718
- // ... then its descendants, where applicable
719
- if (cleanableNodeTypesWithDescendants[node.nodeType]) {
720
- cleanNodesInList(node.getElementsByTagName("*"));
721
- }
722
- }
723
- return node
724
- }
725
-
726
- function removeNode (node) {
727
- cleanNode(node);
728
- if (node.parentNode) { node.parentNode.removeChild(node); }
729
- }
730
-
731
- // Expose supplemental node cleaning functions.
732
- const otherNodeCleanerFunctions = [];
733
-
734
- function addCleaner (fn) {
735
- otherNodeCleanerFunctions.push(fn);
736
- }
737
-
738
- function removeCleaner (fn) {
739
- const fnIndex = otherNodeCleanerFunctions.indexOf(fn);
740
- if (fnIndex >= 0) { otherNodeCleanerFunctions.splice(fnIndex, 1); }
741
- }
742
-
743
- // Special support for jQuery here because it's so commonly used.
744
- // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
745
- // so notify it to tear down any resources associated with the node & descendants here.
746
- function cleanjQueryData (node) {
747
- var jQueryCleanNodeFn = jQueryInstance ? jQueryInstance.cleanData : null;
748
-
749
- if (jQueryCleanNodeFn) {
750
- jQueryCleanNodeFn([node]);
751
- }
752
- }
753
-
754
- otherNodeCleanerFunctions.push(cleanjQueryData);
755
-
756
- //
757
-
758
- // 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)
759
- var knownEvents = {},
760
- knownEventTypesByEventName = {};
761
-
762
- var keyEventTypeName = (options.global.navigator && /Firefox\/2/i.test(options.global.navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
763
-
764
- knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
765
-
766
- knownEvents['MouseEvents'] = [
767
- 'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover',
768
- 'mouseout', 'mouseenter', 'mouseleave'];
769
-
770
- objectForEach(knownEvents, function (eventType, knownEventsForType) {
771
- if (knownEventsForType.length) {
772
- for (var i = 0, j = knownEventsForType.length; i < j; i++) { knownEventTypesByEventName[knownEventsForType[i]] = eventType; }
773
- }
774
- });
775
-
776
- function isClickOnCheckableElement (element, eventType) {
777
- if ((tagNameLower(element) !== 'input') || !element.type) return false
778
- if (eventType.toLowerCase() != 'click') return false
779
- var inputType = element.type;
780
- return (inputType == 'checkbox') || (inputType == 'radio')
781
- }
782
-
783
- // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
784
- var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true };
785
- let jQueryEventAttachName;
786
-
787
- function registerEventHandler (element, eventType, handler, eventOptions = false) {
788
- const wrappedHandler = catchFunctionErrors(handler);
789
- const mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
790
- const mustUseNative = Boolean(eventOptions);
791
-
792
- if (!options.useOnlyNativeEvents && !mustUseAttachEvent && !mustUseNative && jQueryInstance) {
793
- if (!jQueryEventAttachName) {
794
- jQueryEventAttachName = (typeof jQueryInstance(element).on === 'function') ? 'on' : 'bind';
795
- }
796
- jQueryInstance(element)[jQueryEventAttachName](eventType, wrappedHandler);
797
- } else if (!mustUseAttachEvent && typeof element.addEventListener === 'function') {
798
- element.addEventListener(eventType, wrappedHandler, eventOptions);
799
- } else if (typeof element.attachEvent !== 'undefined') {
800
- const attachEventHandler = function (event) { wrappedHandler.call(element, event); };
801
- const attachEventName = 'on' + eventType;
802
- element.attachEvent(attachEventName, attachEventHandler);
803
-
804
- // IE does not dispose attachEvent handlers automatically (unlike with addEventListener)
805
- // so to avoid leaks, we have to remove them manually. See bug #856
806
- addDisposeCallback(element, function () {
807
- element.detachEvent(attachEventName, attachEventHandler);
808
- });
809
- } else {
810
- throw new Error("Browser doesn't support addEventListener or attachEvent")
811
- }
812
- }
813
-
814
- function triggerEvent (element, eventType) {
815
- if (!(element && element.nodeType)) { throw new Error('element must be a DOM node when calling triggerEvent') }
816
-
817
- // For click events on checkboxes and radio buttons, jQuery toggles the element checked state *after* the
818
- // event handler runs instead of *before*. (This was fixed in 1.9 for checkboxes but not for radio buttons.)
819
- // IE doesn't change the checked state when you trigger the click event using "fireEvent".
820
- // In both cases, we'll use the click method instead.
821
- var useClickWorkaround = isClickOnCheckableElement(element, eventType);
822
-
823
- if (!options.useOnlyNativeEvents && jQueryInstance && !useClickWorkaround) {
824
- jQueryInstance(element).trigger(eventType);
825
- } else if (typeof document.createEvent === 'function') {
826
- if (typeof element.dispatchEvent === 'function') {
827
- var eventCategory = knownEventTypesByEventName[eventType] || 'HTMLEvents';
828
- var event = document.createEvent(eventCategory);
829
- event.initEvent(eventType, true, true, options.global, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
830
- element.dispatchEvent(event);
831
- } else { throw new Error("The supplied element doesn't support dispatchEvent") }
832
- } else if (useClickWorkaround && element.click) {
833
- element.click();
834
- } else if (typeof element.fireEvent !== 'undefined') {
835
- element.fireEvent('on' + eventType);
836
- } else {
837
- throw new Error("Browser doesn't support triggering events")
838
- }
839
- }
840
-
841
- //
842
-
843
- function moveCleanedNodesToContainerElement (nodes) {
844
- // Ensure it's a real array, as we're about to reparent the nodes and
845
- // we don't want the underlying collection to change while we're doing that.
846
- var nodesArray = makeArray(nodes);
847
- var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;
848
-
849
- var container = templateDocument.createElement('div');
850
- for (var i = 0, j = nodesArray.length; i < j; i++) {
851
- container.appendChild(cleanNode(nodesArray[i]));
852
- }
853
- return container
854
- }
855
-
856
- function cloneNodes (nodesArray, shouldCleanNodes) {
857
- for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
858
- var clonedNode = nodesArray[i].cloneNode(true);
859
- newNodesArray.push(shouldCleanNodes ? cleanNode(clonedNode) : clonedNode);
860
- }
861
- return newNodesArray
862
- }
863
-
864
- function setDomNodeChildren (domNode, childNodes) {
865
- emptyDomNode(domNode);
866
- if (childNodes) {
867
- for (var i = 0, j = childNodes.length; i < j; i++) { domNode.appendChild(childNodes[i]); }
868
- }
869
- }
870
-
871
- function replaceDomNodes (nodeToReplaceOrNodeArray, newNodesArray) {
872
- var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
873
- if (nodesToReplaceArray.length > 0) {
874
- var insertionPoint = nodesToReplaceArray[0];
875
- var parent = insertionPoint.parentNode;
876
- for (var i = 0, j = newNodesArray.length; i < j; i++) { parent.insertBefore(newNodesArray[i], insertionPoint); }
877
- for (i = 0, j = nodesToReplaceArray.length; i < j; i++) {
878
- removeNode(nodesToReplaceArray[i]);
879
- }
880
- }
881
- }
882
-
883
- function setElementName (element, name) {
884
- element.name = name;
885
-
886
- // Workaround IE 6/7 issue
887
- // - https://github.com/SteveSanderson/knockout/issues/197
888
- // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
889
- if (ieVersion <= 7) {
890
- try {
891
- element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
892
- } catch (e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
893
- }
894
- }
895
-
896
- function emptyDomNode (domNode) {
897
- while (domNode.firstChild) {
898
- removeNode(domNode.firstChild);
899
- }
900
- }
901
-
902
- //
903
-
904
- function fixUpContinuousNodeArray (continuousNodeArray, parentNode) {
905
- // Before acting on a set of nodes that were previously outputted by a template function, we have to reconcile
906
- // them against what is in the DOM right now. It may be that some of the nodes have already been removed, or that
907
- // new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
908
- // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
909
- // So, this function translates the old "map" output array into its best guess of the set of current DOM nodes.
910
- //
911
- // Rules:
912
- // [A] Any leading nodes that have been removed should be ignored
913
- // These most likely correspond to memoization nodes that were already removed during binding
914
- // See https://github.com/knockout/knockout/pull/440
915
- // [B] Any trailing nodes that have been remove should be ignored
916
- // This prevents the code here from adding unrelated nodes to the array while processing rule [C]
917
- // See https://github.com/knockout/knockout/pull/1903
918
- // [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed,
919
- // and include any nodes that have been inserted among the previous collection
920
-
921
- if (continuousNodeArray.length) {
922
- // The parent node can be a virtual element; so get the real parent node
923
- parentNode = (parentNode.nodeType === 8 && parentNode.parentNode) || parentNode;
924
-
925
- // Rule [A]
926
- while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode) { continuousNodeArray.splice(0, 1); }
927
-
928
- // Rule [B]
929
- while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode) { continuousNodeArray.length--; }
930
-
931
- // Rule [C]
932
- if (continuousNodeArray.length > 1) {
933
- var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1];
934
- // Replace with the actual new continuous node set
935
- continuousNodeArray.length = 0;
936
- while (current !== last) {
937
- continuousNodeArray.push(current);
938
- current = current.nextSibling;
939
- }
940
- continuousNodeArray.push(last);
941
- }
942
- }
943
- return continuousNodeArray
944
- }
945
-
946
- function setOptionNodeSelectionState (optionNode, isSelected) {
947
- // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
948
- if (ieVersion < 7) { optionNode.setAttribute('selected', isSelected); } else { optionNode.selected = isSelected; }
949
- }
950
-
951
- function forceRefresh (node) {
952
- // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
953
- if (ieVersion >= 9) {
954
- // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
955
- var elem = node.nodeType == 1 ? node : node.parentNode;
956
- if (elem.style) { elem.style.zoom = elem.style.zoom; }
957
- }
958
- }
959
-
960
- function ensureSelectElementIsRenderedCorrectly (selectElement) {
961
- // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
962
- // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
963
- // Also fixes IE7 and IE8 bug that causes selects to be zero width if enclosed by 'if' or 'with'. (See issue #839)
964
- if (ieVersion) {
965
- var originalWidth = selectElement.style.width;
966
- selectElement.style.width = 0;
967
- selectElement.style.width = originalWidth;
968
- }
969
- }
970
-
971
- /* eslint no-cond-assign: 0 */
972
-
973
- var commentNodesHaveTextProperty = options.document && options.document.createComment('test').text === '<!--test-->';
974
-
975
- var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+([\s\S]+))?\s*-->$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/;
976
- var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
977
- var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
978
-
979
- function isStartComment (node) {
980
- return (node.nodeType == 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue)
981
- }
982
-
983
- function isEndComment (node) {
984
- return (node.nodeType == 8) && endCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue)
985
- }
986
-
987
- function isUnmatchedEndComment (node) {
988
- return isEndComment(node) && !get(node, matchedEndCommentDataKey)
989
- }
990
-
991
- const matchedEndCommentDataKey = '__ko_matchedEndComment__';
992
-
993
- function getVirtualChildren (startComment, allowUnbalanced) {
994
- var currentNode = startComment;
995
- var depth = 1;
996
- var children = [];
997
- while (currentNode = currentNode.nextSibling) {
998
- if (isEndComment(currentNode)) {
999
- set(currentNode, matchedEndCommentDataKey, true);
1000
- depth--;
1001
- if (depth === 0) { return children }
1002
- }
1003
-
1004
- children.push(currentNode);
1005
-
1006
- if (isStartComment(currentNode)) { depth++; }
1007
- }
1008
- if (!allowUnbalanced) { throw new Error('Cannot find closing comment tag to match: ' + startComment.nodeValue) }
1009
- return null
1010
- }
1011
-
1012
- function getMatchingEndComment (startComment, allowUnbalanced) {
1013
- var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
1014
- if (allVirtualChildren) {
1015
- if (allVirtualChildren.length > 0) { return allVirtualChildren[allVirtualChildren.length - 1].nextSibling }
1016
- return startComment.nextSibling
1017
- } else { return null } // Must have no matching end comment, and allowUnbalanced is true
1018
- }
1019
-
1020
- function getUnbalancedChildTags (node) {
1021
- // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
1022
- // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
1023
- var childNode = node.firstChild, captureRemaining = null;
1024
- if (childNode) {
1025
- do {
1026
- if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
1027
- { captureRemaining.push(childNode); } else if (isStartComment(childNode)) {
1028
- var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
1029
- if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
1030
- { childNode = matchingEndComment; } else { captureRemaining = [childNode]; } // It's unbalanced, so start capturing from this point
1031
- } else if (isEndComment(childNode)) {
1032
- captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
1033
- }
1034
- } while (childNode = childNode.nextSibling)
1035
- }
1036
- return captureRemaining
1037
- }
1038
-
1039
- var allowedBindings = {};
1040
- var hasBindingValue = isStartComment;
1041
-
1042
- function childNodes (node) {
1043
- return isStartComment(node) ? getVirtualChildren(node) : node.childNodes
1044
- }
1045
-
1046
- function emptyNode (node) {
1047
- if (!isStartComment(node)) { emptyDomNode(node); } else {
1048
- var virtualChildren = childNodes(node);
1049
- for (var i = 0, j = virtualChildren.length; i < j; i++) { removeNode(virtualChildren[i]); }
1050
- }
1051
- }
1052
-
1053
- function setDomNodeChildren$1 (node, childNodes) {
1054
- if (!isStartComment(node)) { setDomNodeChildren(node, childNodes); } else {
1055
- emptyNode(node);
1056
- const endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
1057
- const parentNode = endCommentNode.parentNode;
1058
- for (var i = 0, j = childNodes.length; i < j; ++i) {
1059
- parentNode.insertBefore(childNodes[i], endCommentNode);
1060
- }
1061
- }
1062
- }
1063
-
1064
- function prepend (containerNode, nodeToPrepend) {
1065
- if (!isStartComment(containerNode)) {
1066
- if (containerNode.firstChild) { containerNode.insertBefore(nodeToPrepend, containerNode.firstChild); } else { containerNode.appendChild(nodeToPrepend); }
1067
- } else {
1068
- // Start comments must always have a parent and at least one following sibling (the end comment)
1069
- containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
1070
- }
1071
- }
1072
-
1073
- function insertAfter (containerNode, nodeToInsert, insertAfterNode) {
1074
- if (!insertAfterNode) {
1075
- prepend(containerNode, nodeToInsert);
1076
- } else if (!isStartComment(containerNode)) {
1077
- // Insert after insertion point
1078
- if (insertAfterNode.nextSibling) { containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling); } else { containerNode.appendChild(nodeToInsert); }
1079
- } else {
1080
- // Children of start comments must always have a parent and at least one following sibling (the end comment)
1081
- containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
1082
- }
1083
- }
1084
-
1085
- function firstChild (node) {
1086
- if (!isStartComment(node)) {
1087
- if (node.firstChild && isEndComment(node.firstChild)) {
1088
- throw new Error('Found invalid end comment, as the first child of ' + node.outerHTML)
1089
- }
1090
- return node.firstChild
1091
- }
1092
- if (!node.nextSibling || isEndComment(node.nextSibling)) {
1093
- return null
1094
- }
1095
- return node.nextSibling
1096
- }
1097
-
1098
- function lastChild (node) {
1099
- let nextChild = firstChild(node);
1100
- let lastChildNode;
1101
-
1102
- do {
1103
- lastChildNode = nextChild;
1104
- } while (nextChild = nextSibling(nextChild))
1105
-
1106
- return lastChildNode
1107
- }
1108
-
1109
- function nextSibling (node) {
1110
- if (isStartComment(node)) {
1111
- node = getMatchingEndComment(node);
1112
- }
1113
-
1114
- if (node.nextSibling && isEndComment(node.nextSibling)) {
1115
- if (isUnmatchedEndComment(node.nextSibling)) {
1116
- throw Error('Found end comment without a matching opening comment, as next sibling of ' + node.outerHTML)
1117
- }
1118
- return null
1119
- } else {
1120
- return node.nextSibling
1121
- }
1122
- }
1123
-
1124
- function previousSibling (node) {
1125
- var depth = 0;
1126
- do {
1127
- if (node.nodeType === 8) {
1128
- if (isStartComment(node)) {
1129
- if (--depth === 0) {
1130
- return node
1131
- }
1132
- } else if (isEndComment(node)) {
1133
- depth++;
1134
- }
1135
- } else {
1136
- if (depth === 0) { return node }
1137
- }
1138
- } while (node = node.previousSibling)
1139
- }
1140
-
1141
- function virtualNodeBindingValue (node) {
1142
- var regexMatch = (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
1143
- return regexMatch ? regexMatch[1] : null
1144
- }
1145
-
1146
- function normaliseVirtualElementDomStructure (elementVerified) {
1147
- // Workaround for https://github.com/SteveSanderson/knockout/issues/155
1148
- // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
1149
- // that are direct descendants of <ul> into the preceding <li>)
1150
- if (!htmlTagsWithOptionallyClosingChildren[tagNameLower(elementVerified)]) { return }
1151
-
1152
- // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
1153
- // must be intended to appear *after* that child, so move them there.
1154
- var childNode = elementVerified.firstChild;
1155
- if (childNode) {
1156
- do {
1157
- if (childNode.nodeType === 1) {
1158
- var unbalancedTags = getUnbalancedChildTags(childNode);
1159
- if (unbalancedTags) {
1160
- // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
1161
- var nodeToInsertBefore = childNode.nextSibling;
1162
- for (var i = 0; i < unbalancedTags.length; i++) {
1163
- if (nodeToInsertBefore) { elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore); } else { elementVerified.appendChild(unbalancedTags[i]); }
1164
- }
1165
- }
1166
- }
1167
- } while (childNode = childNode.nextSibling)
1168
- }
1169
- }
1170
-
1171
- var virtualElements = /*#__PURE__*/Object.freeze({
1172
- startCommentRegex: startCommentRegex,
1173
- endCommentRegex: endCommentRegex,
1174
- isStartComment: isStartComment,
1175
- isEndComment: isEndComment,
1176
- getVirtualChildren: getVirtualChildren,
1177
- allowedBindings: allowedBindings,
1178
- hasBindingValue: hasBindingValue,
1179
- childNodes: childNodes,
1180
- emptyNode: emptyNode,
1181
- setDomNodeChildren: setDomNodeChildren$1,
1182
- prepend: prepend,
1183
- insertAfter: insertAfter,
1184
- firstChild: firstChild,
1185
- lastChild: lastChild,
1186
- nextSibling: nextSibling,
1187
- previousSibling: previousSibling,
1188
- virtualNodeBindingValue: virtualNodeBindingValue,
1189
- normaliseVirtualElementDomStructure: normaliseVirtualElementDomStructure
1190
- });
1191
-
1192
- //
1193
-
1194
- var none = [0, '', ''],
1195
- table = [1, '<table>', '</table>'],
1196
- tbody = [2, '<table><tbody>', '</tbody></table>'],
1197
- colgroup = [ 2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
1198
- tr = [3, '<table><tbody><tr>', '</tr></tbody></table>'],
1199
- select = [1, "<select multiple='multiple'>", '</select>'],
1200
- fieldset = [1, '<fieldset>', '</fieldset>'],
1201
- map = [1, '<map>', '</map>'],
1202
- object = [1, '<object>', '</object>'],
1203
- lookup = {
1204
- 'area': map,
1205
- 'col': colgroup,
1206
- 'colgroup': table,
1207
- 'caption': table,
1208
- 'legend': fieldset,
1209
- 'thead': table,
1210
- 'tbody': table,
1211
- 'tfoot': table,
1212
- 'tr': tbody,
1213
- 'td': tr,
1214
- 'th': tr,
1215
- 'option': select,
1216
- 'optgroup': select,
1217
- 'param': object
1218
- },
1219
-
1220
- // The canonical way to test that the HTML5 <template> tag is supported
1221
- supportsTemplateTag = options.document && 'content' in options.document.createElement('template');
1222
-
1223
- function getWrap (tags) {
1224
- const m = tags.match(/^(?:<!--.*?-->\s*?)*?<([a-z]+)[\s>]/);
1225
- return (m && lookup[m[1]]) || none
1226
- }
1227
-
1228
- function simpleHtmlParse (html, documentContext) {
1229
- documentContext || (documentContext = document);
1230
- var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;
1231
-
1232
- // Based on jQuery's "clean" function, but only accounting for table-related elements.
1233
- // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
1234
-
1235
- // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
1236
- // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
1237
- // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
1238
- // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
1239
-
1240
- // Trim whitespace, otherwise indexOf won't work as expected
1241
- var tags = stringTrim(html).toLowerCase(), div = documentContext.createElement('div'),
1242
- wrap = getWrap(tags),
1243
- depth = wrap[0];
1244
-
1245
- // Go to html and back, then peel off extra wrappers
1246
- // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
1247
- var markup = 'ignored<div>' + wrap[1] + html + wrap[2] + '</div>';
1248
- if (typeof windowContext['innerShiv'] === 'function') {
1249
- // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
1250
- // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
1251
- // somehow shims the native APIs so it just works anyway)
1252
- div.appendChild(windowContext['innerShiv'](markup));
1253
- } else {
1254
- div.innerHTML = markup;
1255
- }
1256
-
1257
- // Move to the right depth
1258
- while (depth--) { div = div.lastChild; }
1259
-
1260
- return makeArray(div.lastChild.childNodes)
1261
- }
1262
-
1263
- function templateHtmlParse (html, documentContext) {
1264
- if (!documentContext) { documentContext = document; }
1265
- var template = documentContext.createElement('template');
1266
- template.innerHTML = html;
1267
- return makeArray(template.content.childNodes)
1268
- }
1269
-
1270
- function jQueryHtmlParse (html, documentContext) {
1271
- // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
1272
- if (jQueryInstance.parseHTML) {
1273
- return jQueryInstance.parseHTML(html, documentContext) || [] // Ensure we always return an array and never null
1274
- } else {
1275
- // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
1276
- var elems = jQueryInstance.clean([html], documentContext);
1277
-
1278
- // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
1279
- // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
1280
- // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
1281
- if (elems && elems[0]) {
1282
- // Find the top-most parent element that's a direct child of a document fragment
1283
- var elem = elems[0];
1284
- while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */) { elem = elem.parentNode; }
1285
- // ... then detach it
1286
- if (elem.parentNode) { elem.parentNode.removeChild(elem); }
1287
- }
1288
-
1289
- return elems
1290
- }
1291
- }
1292
-
1293
- /**
1294
- * parseHtmlFragment converts a string into an array of DOM Nodes.
1295
- * If supported, it uses <template>-tag parsing, falling back on
1296
- * jQuery parsing (if jQuery is present), and finally on a
1297
- * straightforward parser.
1298
- *
1299
- * @param {string} html To be parsed.
1300
- * @param {Object} documentContext That owns the executing code.
1301
- * @return {[DOMNode]} Parsed DOM Nodes
1302
- */
1303
- function parseHtmlFragment (html, documentContext) {
1304
- // Prefer <template>-tag based HTML parsing.
1305
- return supportsTemplateTag ? templateHtmlParse(html, documentContext)
1306
-
1307
- // Benefit from jQuery's on old browsers, where possible
1308
- // NOTE: jQuery's HTML parsing fails on element names like tr-*.
1309
- // See: https://github.com/jquery/jquery/pull/1988
1310
- : (jQueryInstance ? jQueryHtmlParse(html, documentContext)
1311
-
1312
- // ... otherwise, this simple logic will do in most common cases.
1313
- : simpleHtmlParse(html, documentContext))
1314
- }
1315
-
1316
- function parseHtmlForTemplateNodes (html, documentContext) {
1317
- const nodes = parseHtmlFragment(html, documentContext);
1318
- return (nodes.length && nodes[0].parentElement) || moveCleanedNodesToContainerElement(nodes)
1319
- }
1320
-
1321
- /**
1322
- * setHtml empties the node's contents, unwraps the HTML, and
1323
- * sets the node's HTML using jQuery.html or parseHtmlFragment
1324
- *
1325
- * @param {DOMNode} node Node in which HTML needs to be set
1326
- * @param {DOMNode} html HTML to be inserted in node
1327
- * @returns undefined
1328
- */
1329
- function setHtml (node, html) {
1330
- emptyDomNode(node);
1331
-
1332
- // There's few cases where we would want to display a stringified
1333
- // function, so we unwrap it.
1334
- if (typeof html === 'function') {
1335
- html = html();
1336
- }
1337
-
1338
- if ((html !== null) && (html !== undefined)) {
1339
- if (typeof html !== 'string') { html = html.toString(); }
1340
-
1341
- // If the browser supports <template> tags, prefer that, as
1342
- // it obviates all the complex workarounds of jQuery.
1343
- //
1344
- // However, jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
1345
- // for example <tr> elements which are not normally allowed to exist on their own.
1346
- // If you've referenced jQuery (and template tags are not supported) we'll use that rather than duplicating its code.
1347
- if (jQueryInstance && !supportsTemplateTag) {
1348
- jQueryInstance(node).html(html);
1349
- } else {
1350
- // ... otherwise, use KO's own parsing logic.
1351
- var parsedNodes = parseHtmlFragment(html, node.ownerDocument);
1352
-
1353
- if (node.nodeType === 8) {
1354
- if (html === null) {
1355
- emptyNode(node);
1356
- } else {
1357
- setDomNodeChildren$1(node, parsedNodes);
1358
- }
1359
- } else {
1360
- for (var i = 0; i < parsedNodes.length; i++) { node.appendChild(parsedNodes[i]); }
1361
- }
1362
- }
1363
- }
1364
- }
1365
-
1366
-
1367
- function setTextContent (element, textContent) {
1368
- var value = typeof textContent === 'function' ? textContent() : textContent;
1369
- if ((value === null) || (value === undefined)) { value = ''; }
1370
-
1371
- // We need there to be exactly one child: a text node.
1372
- // If there are no children, more than one, or if it's not a text node,
1373
- // we'll clear everything and create a single text node.
1374
- var innerTextNode = firstChild(element);
1375
- if (!innerTextNode || innerTextNode.nodeType != 3 || nextSibling(innerTextNode)) {
1376
- setDomNodeChildren$1(element, [element.ownerDocument.createTextNode(value)]);
1377
- } else {
1378
- innerTextNode.data = value;
1379
- }
1380
-
1381
- forceRefresh(element);
1382
- }
1383
-
1384
- var hasDomDataExpandoProperty = Symbol('Knockout selectExtensions hasDomDataProperty');
1385
-
1386
- // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
1387
- // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
1388
- // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
1389
- //
1390
- var selectExtensions = {
1391
- optionValueDomDataKey: nextKey(),
1392
-
1393
- readValue: function (element) {
1394
- switch (tagNameLower(element)) {
1395
- case 'option':
1396
- if (element[hasDomDataExpandoProperty] === true) { return get(element, selectExtensions.optionValueDomDataKey) }
1397
- return element.value
1398
- case 'select':
1399
- return element.selectedIndex >= 0 ? selectExtensions.readValue(element.options[element.selectedIndex]) : undefined
1400
- default:
1401
- return element.value
1402
- }
1403
- },
1404
-
1405
- writeValue: function (element, value, allowUnset) {
1406
- switch (tagNameLower(element)) {
1407
- case 'option':
1408
- if (typeof value === 'string') {
1409
- set(element, selectExtensions.optionValueDomDataKey, undefined);
1410
- if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
1411
- delete element[hasDomDataExpandoProperty];
1412
- }
1413
- element.value = value;
1414
- } else {
1415
- // Store arbitrary object using DomData
1416
- set(element, selectExtensions.optionValueDomDataKey, value);
1417
- element[hasDomDataExpandoProperty] = true;
1418
- // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
1419
- element.value = typeof value === 'number' ? value : '';
1420
- }
1421
-
1422
- break
1423
- case 'select':
1424
- if (value === '' || value === null) {
1425
- // A blank string or null value will select the caption
1426
- value = undefined;
1427
- }
1428
- var selection = -1;
1429
- for (let i = 0, n = element.options.length, optionValue; i < n; ++i) {
1430
- optionValue = selectExtensions.readValue(element.options[i]);
1431
- // Include special check to handle selecting a caption with a blank string value
1432
- if (optionValue === value || (optionValue === '' && value === undefined)) {
1433
- selection = i;
1434
- break
1435
- }
1436
- }
1437
- if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) {
1438
- element.selectedIndex = selection;
1439
- if (ieVersion === 6) {
1440
- // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
1441
- // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
1442
- // to apply the value as well.
1443
- safeSetTimeout(() => { element.selectedIndex = selection; }, 0);
1444
- }
1445
- }
1446
- break
1447
- default:
1448
- if ((value === null) || (value === undefined)) { value = ''; }
1449
- element.value = value;
1450
- break
1451
- }
1452
- }
1453
- };
1454
-
1455
- //
1456
-
1457
- var memos = {};
1458
-
1459
- function randomMax8HexChars () {
1460
- return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1)
1461
- }
1462
-
1463
- function generateRandomId () {
1464
- return randomMax8HexChars() + randomMax8HexChars()
1465
- }
1466
-
1467
- function findMemoNodes (rootNode, appendToArray) {
1468
- if (!rootNode) { return }
1469
- if (rootNode.nodeType == 8) {
1470
- var memoId = parseMemoText(rootNode.nodeValue);
1471
- if (memoId != null) { appendToArray.push({ domNode: rootNode, memoId: memoId }); }
1472
- } else if (rootNode.nodeType == 1) {
1473
- for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++) { findMemoNodes(childNodes[i], appendToArray); }
1474
- }
1475
- }
1476
-
1477
- function memoize (callback) {
1478
- if (typeof callback !== 'function') { throw new Error('You can only pass a function to memoization.memoize()') }
1479
- var memoId = generateRandomId();
1480
- memos[memoId] = callback;
1481
- return '<!--[ko_memo:' + memoId + ']-->'
1482
- }
1483
-
1484
- function unmemoize (memoId, callbackParams) {
1485
- var callback = memos[memoId];
1486
- if (callback === undefined) { throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.") }
1487
- try {
1488
- callback.apply(null, callbackParams || []);
1489
- return true
1490
- } finally { delete memos[memoId]; }
1491
- }
1492
-
1493
- function unmemoizeDomNodeAndDescendants (domNode, extraCallbackParamsArray) {
1494
- var memos = [];
1495
- findMemoNodes(domNode, memos);
1496
- for (var i = 0, j = memos.length; i < j; i++) {
1497
- var node = memos[i].domNode;
1498
- var combinedParams = [node];
1499
- if (extraCallbackParamsArray) { arrayPushAll(combinedParams, extraCallbackParamsArray); }
1500
- unmemoize(memos[i].memoId, combinedParams);
1501
- node.nodeValue = ''; // Neuter this node so we don't try to unmemoize it again
1502
- if (node.parentNode) { node.parentNode.removeChild(node); } // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
1503
- }
1504
- }
1505
-
1506
- function parseMemoText (memoText) {
1507
- var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
1508
- return match ? match[1] : null
1509
- }
1510
-
1511
- var memoization = /*#__PURE__*/Object.freeze({
1512
- memoize: memoize,
1513
- unmemoize: unmemoize,
1514
- unmemoizeDomNodeAndDescendants: unmemoizeDomNodeAndDescendants,
1515
- parseMemoText: parseMemoText
1516
- });
1517
-
1518
- //
1519
-
1520
- var taskQueue = [],
1521
- taskQueueLength = 0,
1522
- nextHandle = 1,
1523
- nextIndexToProcess = 0,
1524
- w = options.global;
1525
-
1526
- if (w && w.MutationObserver && !(w.navigator && w.navigator.standalone)) {
1527
- // Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+, node
1528
- // From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
1529
- options.taskScheduler = (function (callback) {
1530
- var div = w.document.createElement('div');
1531
- new w.MutationObserver(callback).observe(div, {attributes: true});
1532
- return function () { div.classList.toggle('foo'); }
1533
- })(scheduledProcess);
1534
- } else if (w && w.document && 'onreadystatechange' in w.document.createElement('script')) {
1535
- // IE 6-10
1536
- // From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
1537
- options.taskScheduler = function (callback) {
1538
- var script = document.createElement('script');
1539
- script.onreadystatechange = function () {
1540
- script.onreadystatechange = null;
1541
- document.documentElement.removeChild(script);
1542
- script = null;
1543
- callback();
1544
- };
1545
- document.documentElement.appendChild(script);
1546
- };
1547
- } else {
1548
- options.taskScheduler = function (callback) {
1549
- setTimeout(callback, 0);
1550
- };
1551
- }
1552
-
1553
- function processTasks () {
1554
- if (taskQueueLength) {
1555
- // Each mark represents the end of a logical group of tasks and the number of these groups is
1556
- // limited to prevent unchecked recursion.
1557
- var mark = taskQueueLength, countMarks = 0;
1558
-
1559
- // nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
1560
- for (var task; nextIndexToProcess < taskQueueLength;) {
1561
- if (task = taskQueue[nextIndexToProcess++]) {
1562
- if (nextIndexToProcess > mark) {
1563
- if (++countMarks >= 5000) {
1564
- nextIndexToProcess = taskQueueLength; // skip all tasks remaining in the queue since any of them could be causing the recursion
1565
- deferError(Error("'Too much recursion' after processing " + countMarks + ' task groups.'));
1566
- break
1567
- }
1568
- mark = taskQueueLength;
1569
- }
1570
- try {
1571
- task();
1572
- } catch (ex) {
1573
- deferError(ex);
1574
- }
1575
- }
1576
- }
1577
- }
1578
- }
1579
-
1580
- function scheduledProcess () {
1581
- processTasks();
1582
-
1583
- // Reset the queue
1584
- nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
1585
- }
1586
-
1587
- function scheduleTaskProcessing () {
1588
- options.taskScheduler(scheduledProcess);
1589
- }
1590
-
1591
- function schedule (func) {
1592
- if (!taskQueueLength) {
1593
- scheduleTaskProcessing();
1594
- }
1595
-
1596
- taskQueue[taskQueueLength++] = func;
1597
- return nextHandle++
1598
- }
1599
-
1600
- function cancel (handle) {
1601
- var index = handle - (nextHandle - taskQueueLength);
1602
- if (index >= nextIndexToProcess && index < taskQueueLength) {
1603
- taskQueue[index] = null;
1604
- }
1605
- }
1606
-
1607
- // For testing only: reset the queue and return the previous queue length
1608
- function resetForTesting () {
1609
- var length = taskQueueLength - nextIndexToProcess;
1610
- nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
1611
- return length
1612
- }
1613
-
1614
- var tasks = /*#__PURE__*/Object.freeze({
1615
- schedule: schedule,
1616
- cancel: cancel,
1617
- resetForTesting: resetForTesting,
1618
- runEarly: processTasks
1619
- });
1620
-
1621
- /*
1622
- tko.util
1623
- ===
1624
-
1625
- */
1626
-
1627
- export { tasks, virtualElements, data as domData, memoization, jQuerySetInstance, options, arrayForEach, arrayIndexOf, arrayFirst, arrayMap, arrayRemoveItem, arrayGetDistinctValues, arrayFilter, arrayPushAll, addOrRemoveItem, makeArray, range, findMovesInArrayComparison, compareArrays, throttle, debounce, catchFunctionErrors, deferError, safeSetTimeout, ieVersion, isIe6, isIe7, hasOwnProperty, extend, objectForEach, objectMap, getObjectOwnProperty, clonePlainObjectDeep, safeStringify, isThenable, functionSupportsLengthOverwrite, overwriteLengthPropertyIfSupported, stringTrim, stringStartsWith, parseJson, useSymbols, createSymbolOrString, toggleDomNodeCssClass, registerEventHandler, triggerEvent, domNodeIsContainedBy, domNodeIsAttachedToDocument, anyDomNodeIsAttachedToDocument, tagNameLower, isDomElement, isDocumentFragment, moveCleanedNodesToContainerElement, cloneNodes, setDomNodeChildren, replaceDomNodes, setElementName, emptyDomNode, fixUpContinuousNodeArray, setOptionNodeSelectionState, forceRefresh, ensureSelectElementIsRenderedCorrectly, parseHtmlFragment, parseHtmlForTemplateNodes, setHtml, setTextContent, addDisposeCallback, removeDisposeCallback, cleanNode, removeNode, otherNodeCleanerFunctions, addCleaner, removeCleaner, cleanjQueryData, selectExtensions };
1628
- //# sourceMappingURL=utils.es6.js.map