/*! * Knockout JavaScript library v3.5.1-sm * (c) The Knockout.js team - http://knockoutjs.com/ * License: MIT (http://www.opensource.org/licenses/mit-license.php) */ (()=>{ var DEBUG=true; (window => { // (0, eval)('this') is a robust way of getting a reference to the global object // For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023 var document = window['document'], navigator = window['navigator'], JSON = window["JSON"], koExports = {}; // Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler). // In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable. var ko = typeof koExports !== 'undefined' ? koExports : {}; // Google Closure Compiler helpers (used only to make the minified file smaller) ko.exportSymbol = (koPath, object) => { var tokens = koPath.split("."); // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable) // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko) var target = ko; for (var i = 0; i < tokens.length - 1; i++) target = target[tokens[i]]; target[tokens[tokens.length - 1]] = object; }; ko.exportProperty = (owner, publicName, object) => owner[publicName] = object; ko.version = "3.5.1-sm"; ko.exportSymbol('version', ko.version); ko.utils = { arrayRemoveItem: (array, itemToRemove) => { var index = array.indexOf(itemToRemove); if (index > 0) { array.splice(index, 1); } else if (index === 0) { array.shift(); } }, extend: (target, source) => { source && Object.entries(source).forEach(prop => target[prop[0]] = prop[1]); return target; }, objectForEach: (obj, action) => obj && Object.entries(obj).forEach(prop => action(prop[0], prop[1])), objectMap: (source, mapping, mappingOwner) => { if (!source) return source; var target = {}; Object.entries(source).forEach(prop => target[prop[0]] = mapping.call(mappingOwner, prop[1], prop[0], source) ); return target; }, emptyDomNode: domNode => { while (domNode.firstChild) { ko.removeNode(domNode.firstChild); } }, moveCleanedNodesToContainerElement: nodes => { // Ensure it's a real array, as we're about to reparent the nodes and // we don't want the underlying collection to change while we're doing that. var nodesArray = [...nodes]; var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document; var container = templateDocument.createElement('div'); nodes.forEach(node => container.append(ko.cleanNode(node))); return container; }, cloneNodes: (nodesArray, shouldCleanNodes) => Array.prototype.map.call(nodesArray, shouldCleanNodes ? node => ko.cleanNode(node.cloneNode(true)) : node => node.cloneNode(true)), setDomNodeChildren: (domNode, childNodes) => { ko.utils.emptyDomNode(domNode); childNodes && domNode.append(...childNodes); }, fixUpContinuousNodeArray: (continuousNodeArray, parentNode) => { // Before acting on a set of nodes that were previously outputted by a template function, we have to reconcile // them against what is in the DOM right now. It may be that some of the nodes have already been removed, or that // new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding. // So, this function translates the old "map" output array into its best guess of the set of current DOM nodes. // // Rules: // [A] Any leading nodes that have been removed should be ignored // These most likely correspond to memoization nodes that were already removed during binding // See https://github.com/knockout/knockout/pull/440 // [B] Any trailing nodes that have been remove should be ignored // This prevents the code here from adding unrelated nodes to the array while processing rule [C] // See https://github.com/knockout/knockout/pull/1903 // [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed, // and include any nodes that have been inserted among the previous collection if (continuousNodeArray.length) { // The parent node can be a virtual element; so get the real parent node parentNode = (parentNode.nodeType === 8 && parentNode.parentNode) || parentNode; // Rule [A] while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode) continuousNodeArray.splice(0, 1); // Rule [B] while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode) continuousNodeArray.length--; // Rule [C] if (continuousNodeArray.length > 1) { var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1]; // Replace with the actual new continuous node set continuousNodeArray.length = 0; while (current !== last) { continuousNodeArray.push(current); current = current.nextSibling; } continuousNodeArray.push(last); } } return continuousNodeArray; }, stringTrim: string => string == null ? '' : string.trim ? string.trim() : string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''), stringStartsWith: (string, startsWith) => { string = string || ""; if (startsWith.length > string.length) return false; return string.substring(0, startsWith.length) === startsWith; }, domNodeIsContainedBy: (node, containedByNode) => containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node), domNodeIsAttachedToDocument: node => ko.utils.domNodeIsContainedBy(node, node.ownerDocument.documentElement), catchFunctionErrors: delegate => { return ko['onError'] ? function () { try { return delegate.apply(this, arguments); } catch (e) { ko['onError'] && ko['onError'](e); throw e; } } : delegate; }, setTimeout: (handler, timeout) => setTimeout(ko.utils.catchFunctionErrors(handler), timeout), deferError: error => setTimeout(() => { ko['onError'] && ko['onError'](error); throw error; }, 0), registerEventHandler: (element, eventType, handler) => { var wrappedHandler = ko.utils.catchFunctionErrors(handler); element.addEventListener(eventType, wrappedHandler, false); }, triggerEvent: (element, eventType) => { if (!(element && element.nodeType)) throw new Error("element must be a DOM node when calling triggerEvent"); element.dispatchEvent(new Event(eventType)); }, unwrapObservable: value => ko.isObservable(value) ? value() : value, setTextContent: (element, textContent) => element.textContent = ko.utils.unwrapObservable(textContent) || "" }; ko.exportSymbol('utils', ko.utils); ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly ko.utils.domData = new (function () { let uniqueId = 0, dataStoreKeyExpandoPropertyName = "__ko__" + (Date.now()), dataStore = new WeakMap(); return { get: (node, key) => (dataStore.get(node) || {})[key], set: (node, key, value) => { if (dataStore.has(node)) { dataStore.get(node)[key] = value; } else { let dataForNode = {}; dataForNode[key] = value; dataStore.set(node, dataForNode); } return value; }, getOrSet: function(node, key, value) { return this.get(node, key) || this.set(node, key, value); }, clear: node => dataStore.delete(node), nextKey: () => (uniqueId++) + dataStoreKeyExpandoPropertyName }; })(); ko.utils.domNodeDisposal = new (function () { var domDataKey = ko.utils.domData.nextKey(); var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document function getDisposeCallbacksCollection(node, createIfNotFound) { var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey); if ((allDisposeCallbacks === undefined) && createIfNotFound) { allDisposeCallbacks = []; ko.utils.domData.set(node, domDataKey, allDisposeCallbacks); } return allDisposeCallbacks; } function destroyCallbacksCollection(node) { ko.utils.domData.set(node, domDataKey, undefined); } function cleanSingleNode(node) { // Run all the dispose callbacks var callbacks = getDisposeCallbacksCollection(node, false); if (callbacks) { callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves) for (var i = 0; i < callbacks.length; i++) callbacks[i](node); } // Erase the DOM data ko.utils.domData.clear(node); // Clear any immediate-child comment nodes, as these wouldn't have been found by // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements) if (cleanableNodeTypesWithDescendants[node.nodeType]) { cleanNodesInList(node.childNodes, true/*onlyComments*/); } } function cleanNodesInList(nodeList, onlyComments) { var cleanedNodes = [], lastCleanedNode; for (var i = 0; i < nodeList.length; i++) { if (!onlyComments || nodeList[i].nodeType === 8) { cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]); if (nodeList[i] !== lastCleanedNode) { while (i-- && !cleanedNodes.includes(nodeList[i])); } } } } return { addDisposeCallback : (node, callback) => { if (typeof callback != "function") throw new Error("Callback must be a function"); getDisposeCallbacksCollection(node, true).push(callback); }, removeDisposeCallback : (node, callback) => { var callbacksCollection = getDisposeCallbacksCollection(node, false); if (callbacksCollection) { ko.utils.arrayRemoveItem(callbacksCollection, callback); if (callbacksCollection.length == 0) destroyCallbacksCollection(node); } }, cleanNode : node => { ko.dependencyDetection.ignore(() => { // First clean this node, where applicable if (cleanableNodeTypes[node.nodeType]) { cleanSingleNode(node); // ... then its descendants, where applicable if (cleanableNodeTypesWithDescendants[node.nodeType]) { cleanNodesInList(node.getElementsByTagName("*")); } } }); return node; }, removeNode : node => { ko.cleanNode(node); if (node.parentNode) node.parentNode.removeChild(node); } }; })(); ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal); ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback); ko.tasks = (() => { var taskQueue = [], taskQueueLength = 0, nextHandle = 1, nextIndexToProcess = 0, // Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+ // From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT scheduler = (callback => { var div = document.createElement("div"); new MutationObserver(callback).observe(div, {attributes: true}); return () => div.classList.toggle("foo"); })(scheduledProcess); function processTasks() { if (taskQueueLength) { // Each mark represents the end of a logical group of tasks and the number of these groups is // limited to prevent unchecked recursion. var mark = taskQueueLength, countMarks = 0; // nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue for (var task; nextIndexToProcess < taskQueueLength; ) { if (task = taskQueue[nextIndexToProcess++]) { if (nextIndexToProcess > mark) { if (++countMarks >= 5000) { nextIndexToProcess = taskQueueLength; // skip all tasks remaining in the queue since any of them could be causing the recursion ko.utils.deferError(Error("'Too much recursion' after processing " + countMarks + " task groups.")); break; } mark = taskQueueLength; } try { task(); } catch (ex) { ko.utils.deferError(ex); } } } } } function scheduledProcess() { processTasks(); // Reset the queue nextIndexToProcess = taskQueueLength = taskQueue.length = 0; } var tasks = { schedule: func => { if (!taskQueueLength) { scheduler(scheduledProcess); } taskQueue[taskQueueLength++] = func; return nextHandle++; }, cancel: handle => { var index = handle - (nextHandle - taskQueueLength); if (index >= nextIndexToProcess && index < taskQueueLength) { taskQueue[index] = null; } } }; return tasks; })(); ko.exportSymbol('tasks', ko.tasks); ko.extenders = { 'debounce': (target, timeout) => target.limit(callback => debounce(callback, timeout)), 'rateLimit': (target, options) => { var timeout, method, limitFunction; if (typeof options == 'number') { timeout = options; } else { timeout = options['timeout']; method = options['method']; } limitFunction = typeof method == 'function' ? method : throttle; target.limit(callback => limitFunction(callback, timeout, options)); }, 'notify': (target, notifyWhen) => { target["equalityComparer"] = notifyWhen == "always" ? null : // null equalityComparer means to always notify valuesArePrimitiveAndEqual; } }; var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 }; function valuesArePrimitiveAndEqual(a, b) { return (a === null || primitiveTypes[typeof(a)]) ? (a === b) : false; } function throttle(callback, timeout) { var timeoutInstance; return () => { if (!timeoutInstance) { timeoutInstance = ko.utils.setTimeout(() => { timeoutInstance = 0; callback(); }, timeout); } }; } function debounce(callback, timeout) { var timeoutInstance; return () => { clearTimeout(timeoutInstance); timeoutInstance = ko.utils.setTimeout(callback, timeout); }; } ko.exportSymbol('extenders', ko.extenders); class koSubscription { constructor (target, callback, disposeCallback) { this._target = target; this._callback = callback; this._disposeCallback = disposeCallback; this._isDisposed = false; this._node = null; this._domNodeDisposalCallback = null; ko.exportProperty(this, 'dispose', this.dispose); ko.exportProperty(this, 'disposeWhenNodeIsRemoved', this.disposeWhenNodeIsRemoved); } dispose() { var self = this; if (!self._isDisposed) { if (self._domNodeDisposalCallback) { ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback); } self._isDisposed = true; self._disposeCallback(); self._target = self._callback = self._disposeCallback = self._node = self._domNodeDisposalCallback = null; } } disposeWhenNodeIsRemoved(node) { this._node = node; ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this)); } } ko.subscribable = function () { Object.setPrototypeOf(this, ko_subscribable_fn); ko_subscribable_fn.init(this); } var defaultEvent = "change"; var ko_subscribable_fn = { init: instance => { instance._subscriptions = { "change": [] }; instance._versionNumber = 1; }, subscribe: function (callback, callbackTarget, event) { var self = this; event = event || defaultEvent; var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback; var subscription = new koSubscription(self, boundCallback, () => { ko.utils.arrayRemoveItem(self._subscriptions[event], subscription); if (self.afterSubscriptionRemove) self.afterSubscriptionRemove(event); }); if (self.beforeSubscriptionAdd) self.beforeSubscriptionAdd(event); if (!self._subscriptions[event]) self._subscriptions[event] = []; self._subscriptions[event].push(subscription); return subscription; }, "notifySubscribers": function (valueToNotify, event) { event = event || defaultEvent; if (event === defaultEvent) { this.updateVersion(); } if (this.hasSubscriptionsForEvent(event)) { var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0); try { ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined) var i = 0, subscription; while ((subscription = subs[i++])) { // In case a subscription was disposed during the arrayForEach cycle, check // for isDisposed on each subscription before invoking its callback if (!subscription._isDisposed) subscription._callback(valueToNotify); } } finally { ko.dependencyDetection.end(); // End suppressing dependency detection } } }, getVersion: function () { return this._versionNumber; }, hasChanged: function (versionToCheck) { return this.getVersion() !== versionToCheck; }, updateVersion: function () { ++this._versionNumber; }, limit: function(limitFunction) { var self = this, selfIsObservable = ko.isObservable(self), ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate, beforeChange = 'beforeChange'; if (!self._origNotifySubscribers) { self._origNotifySubscribers = self["notifySubscribers"]; // Moved out of "limit" to avoid the extra closure self["notifySubscribers"] = function(value, event) { if (!event || event === defaultEvent) { this._limitChange(value); } else if (event === 'beforeChange') { this._limitBeforeChange(value); } else { this._origNotifySubscribers(value, event); } } } var finish = limitFunction(() => { self._notificationIsPending = false; // If an observable provided a reference to itself, access it to get the latest value. // This allows computed observables to delay calculating their value until needed. if (selfIsObservable && pendingValue === self) { pendingValue = self._evalIfChanged ? self._evalIfChanged() : self(); } var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue)); didUpdate = notifyNextChange = ignoreBeforeChange = false; if (shouldNotify) { self._origNotifySubscribers(previousValue = pendingValue); } }); self._limitChange = (value, isDirty) => { if (!isDirty || !self._notificationIsPending) { didUpdate = !isDirty; } self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0); self._notificationIsPending = ignoreBeforeChange = true; pendingValue = value; finish(); }; self._limitBeforeChange = value => { if (!ignoreBeforeChange) { previousValue = value; self._origNotifySubscribers(value, beforeChange); } }; self._recordUpdate = () => { didUpdate = true; }; self._notifyNextChangeIfValueIsDifferent = () => { if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) { notifyNextChange = true; } }; }, hasSubscriptionsForEvent: function(event) { return this._subscriptions[event] && this._subscriptions[event].length; }, isDifferent: function(oldValue, newValue) { return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue); }, toString: () => '[object Object]', extend: function(requestedExtenders) { var target = this; if (requestedExtenders) { ko.utils.objectForEach(requestedExtenders, (key, value) => { var extenderHandler = ko.extenders[key]; if (typeof extenderHandler == 'function') { target = extenderHandler(target, value) || target; } }); } return target; } }; ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init); ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe); ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend); // For browsers that support proto assignment, we overwrite the prototype of each // observable instance. Since observables are functions, we need Function.prototype // to still be in the prototype chain. Object.setPrototypeOf(ko_subscribable_fn, Function.prototype); ko.subscribable['fn'] = ko_subscribable_fn; ko.isSubscribable = instance => instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function"; ko.dependencyDetection = (() => { var outerFrames = [], currentFrame, lastId = 0, begin = options => { outerFrames.push(currentFrame); currentFrame = options; }, end = () => currentFrame = outerFrames.pop(); return { begin: begin, end: end, registerDependency: subscribable => { if (currentFrame) { if (!ko.isSubscribable(subscribable)) throw new Error("Only subscribable things can act as dependencies"); currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = ++lastId)); } }, ignore: (callback, callbackTarget, callbackArgs) => { try { begin(); return callback.apply(callbackTarget, callbackArgs || []); } finally { end(); } }, getDependenciesCount: () => { return currentFrame && currentFrame.computed.getDependenciesCount(); }, isInitial: () => { return currentFrame && currentFrame.isInitial; }, computed: () => { return currentFrame && currentFrame.computed; } }; })(); const observableLatestValue = Symbol('_latestValue'), length = 'length'; ko.observable = initialValue => { function observable() { if (arguments.length > 0) { // Write // Ignore writes if the value hasn't changed if (observable.isDifferent(observable[observableLatestValue], arguments[0])) { observable.valueWillMutate(); observable[observableLatestValue] = arguments[0]; observable.valueHasMutated(); } return this; // Permits chained assignments } else { // Read ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation return observable[observableLatestValue]; } } observable[observableLatestValue] = initialValue; Object.defineProperty(observable, length, { get: () => null == observable[observableLatestValue] ? undefined : observable[observableLatestValue][length] }); // Inherit from 'subscribable' ko.subscribable['fn'].init(observable); // Inherit from 'observable' Object.setPrototypeOf(observable, observableFn); return observable; } // Define prototype for observables var observableFn = { 'toJSON': function() { let value = this[observableLatestValue]; return value && value.toJSON ? value.toJSON() : value; }, 'equalityComparer': valuesArePrimitiveAndEqual, peek: function() { return this[observableLatestValue]; }, valueHasMutated: function () { this['notifySubscribers'](this[observableLatestValue], 'spectate'); this['notifySubscribers'](this[observableLatestValue]); }, valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); } }; // Note that for browsers that don't support proto assignment, the // inheritance chain is created manually in the ko.observable constructor Object.setPrototypeOf(observableFn, ko.subscribable['fn']); var protoProperty = ko.observable.protoProperty = '__ko_proto__'; observableFn[protoProperty] = ko.observable; ko.isObservable = instance => { var proto = typeof instance == 'function' && instance[protoProperty]; if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) { throw Error("Invalid object that looks like an observable; possibly from another Knockout instance"); } return !!proto; }; ko.isWriteableObservable = instance => { return (typeof instance == 'function' && ( (instance[protoProperty] === observableFn[protoProperty]) || // Observable (instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction))); // Writable computed observable }; ko.exportSymbol('observable', ko.observable); ko.exportSymbol('isObservable', ko.isObservable); ko.exportSymbol('observable.fn', observableFn); ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated); ko.observableArray = initialValues => { initialValues = initialValues || []; if (typeof initialValues != 'object' || !('length' in initialValues)) throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined."); var result = ko.observable(initialValues); Object.setPrototypeOf(result, ko.observableArray['fn']); return result.extend({'trackArrayChanges':true}); }; ko.observableArray['fn'] = { 'remove': function (valueOrPredicate) { var underlyingArray = this.peek(); var removedValues = []; var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; }; var i = underlyingArray.length; while (i--) { var value = underlyingArray[i]; if (predicate(value)) { if (removedValues.length === 0) { this.valueWillMutate(); } if (underlyingArray[i] !== value) { throw Error("Array modified during remove; cannot remove item"); } removedValues.push(value); underlyingArray.splice(i, 1); } } if (removedValues.length) { this.valueHasMutated(); } return removedValues; }, 'removeAll': function (arrayOfValues) { // If you passed zero args, we remove everything if (arrayOfValues === undefined) { var underlyingArray = this.peek(); var allValues = underlyingArray.slice(0); this.valueWillMutate(); underlyingArray.splice(0, underlyingArray.length); this.valueHasMutated(); return allValues; } // If you passed an arg, we interpret it as an array of entries to remove if (!arrayOfValues) return []; return this['remove'](value => arrayOfValues.includes(value)); } }; // Note that for browsers that don't support proto assignment, the // inheritance chain is created manually in the ko.observableArray constructor Object.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']); // Populate ko.observableArray.fn with native arrays functions Object.getOwnPropertyNames(Array.prototype).forEach(methodName => { // skip property length if (typeof Array.prototype[methodName] === 'function' && 'constructor' != methodName) { if (["copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift"].includes(methodName)) { // Mutator methods // https://developer.mozilla.org/tr/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#mutator_methods // Important: Do not add any additional functions here that may reasonably be used to *read* data from the array // because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale ko.observableArray['fn'][methodName] = function (...args) { // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of // (for consistency with mutating regular observables) var underlyingArray = this.peek(); this.valueWillMutate(); this.cacheDiffForKnownOperation(underlyingArray, methodName, args); var methodCallResult = underlyingArray[methodName](...args); this.valueHasMutated(); // The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead. return methodCallResult === underlyingArray ? this : methodCallResult; }; } else { // Accessor and Iteration methods ko.observableArray['fn'][methodName] = function (...args) { return this()[methodName](...args); }; } } }); ko.isObservableArray = instance => { return ko.isObservable(instance) && typeof instance["remove"] == "function" && typeof instance["push"] == "function"; }; ko.exportSymbol('observableArray', ko.observableArray); ko.exportSymbol('isObservableArray', ko.isObservableArray); var arrayChangeEventName = 'arrayChange'; ko.extenders['trackArrayChanges'] = (target, options) => { // Use the provided options--each call to trackArrayChanges overwrites the previously set options target.compareArrayOptions = {}; if (options && typeof options == "object") { ko.utils.extend(target.compareArrayOptions, options); } target.compareArrayOptions['sparse'] = true; // Only modify the target observable once if (target.cacheDiffForKnownOperation) { return; } var trackingChanges = false, cachedDiff = null, changeSubscription, spectateSubscription, pendingChanges = 0, previousContents, underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd, underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove; // Watch "subscribe" calls, and for array change events, ensure change tracking is enabled target.beforeSubscriptionAdd = event => { if (underlyingBeforeSubscriptionAddFunction) { underlyingBeforeSubscriptionAddFunction.call(target, event); } if (event === arrayChangeEventName) { trackChanges(); } }; // Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed target.afterSubscriptionRemove = event => { if (underlyingAfterSubscriptionRemoveFunction) { underlyingAfterSubscriptionRemoveFunction.call(target, event); } if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) { if (changeSubscription) { changeSubscription.dispose(); } if (spectateSubscription) { spectateSubscription.dispose(); } spectateSubscription = changeSubscription = null; trackingChanges = false; previousContents = undefined; } }; function trackChanges() { if (trackingChanges) { // Whenever there's a new subscription and there are pending notifications, make sure all previous // subscriptions are notified of the change so that all subscriptions are in sync. notifyChanges(); return; } trackingChanges = true; // Track how many times the array actually changed value spectateSubscription = target.subscribe(() => ++pendingChanges, null, "spectate"); // Each time the array changes value, capture a clone so that on the next // change it's possible to produce a diff previousContents = [].concat(target.peek() || []); cachedDiff = null; changeSubscription = target.subscribe(notifyChanges); function notifyChanges() { if (pendingChanges) { // Make a copy of the current contents and ensure it's an array var currentContents = [].concat(target.peek() || []), changes; // Compute the diff and issue notifications, but only if someone is listening if (target.hasSubscriptionsForEvent(arrayChangeEventName)) { changes = getChanges(previousContents, currentContents); } // Eliminate references to the old, removed items, so they can be GCed previousContents = currentContents; cachedDiff = null; pendingChanges = 0; if (changes && changes.length) { target['notifySubscribers'](changes, arrayChangeEventName); } } } } function getChanges(previousContents, currentContents) { // We try to re-use cached diffs. // The scenarios where pendingChanges > 1 are when using rate limiting or deferred updates, // which without this check would not be compatible with arrayChange notifications. Normally, // notifications are issued immediately so we wouldn't be queueing up more than one. if (!cachedDiff || pendingChanges > 1) { cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions); } return cachedDiff; } target.cacheDiffForKnownOperation = (rawArray, operationName, args) => { // Only run if we're currently tracking changes for this observable array // and there aren't any pending deferred notifications. if (!trackingChanges || pendingChanges) { return; } var diff = [], arrayLength = rawArray.length, argsLength = args.length, offset = 0; function pushDiff(status, value, index) { return diff[diff.length] = { 'status': status, 'value': value, 'index': index }; } switch (operationName) { case 'push': offset = arrayLength; case 'unshift': for (var index = 0; index < argsLength; index++) { pushDiff('added', args[index], offset + index); } break; case 'pop': offset = arrayLength - 1; case 'shift': if (arrayLength) { pushDiff('deleted', rawArray[offset], offset); } break; case 'splice': // Negative start index means 'from end of array'. After that we clamp to [0...arrayLength]. // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength), endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength), endAddIndex = startIndex + argsLength - 2, endIndex = Math.max(endDeleteIndex, endAddIndex), additions = [], deletions = []; for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) { if (index < endDeleteIndex) deletions.push(pushDiff('deleted', rawArray[index], index)); if (index < endAddIndex) additions.push(pushDiff('added', args[argsIndex], index)); } ko.utils.findMovesInArrayComparison(deletions, additions); break; default: return; } cachedDiff = diff; }; }; var computedState = Symbol('_state'); ko.computed = function (evaluatorFunctionOrOptions, options) { if (typeof evaluatorFunctionOrOptions === "object") { // Single-parameter syntax - everything is on this "options" param options = evaluatorFunctionOrOptions; } else { // Multi-parameter syntax - construct the options according to the params passed options = options || {}; if (evaluatorFunctionOrOptions) { options["read"] = evaluatorFunctionOrOptions; } } if (typeof options["read"] != "function") throw Error("Pass a function that returns the value of the ko.computed"); var writeFunction = options["write"]; var state = { latestValue: undefined, isStale: true, isDirty: true, isBeingEvaluated: false, suppressDisposalUntilDisposeWhenReturnsFalse: false, isDisposed: false, pure: false, isSleeping: false, readFunction: options["read"], disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null, disposeWhen: options["disposeWhen"] || options.disposeWhen, domNodeDisposalCallback: null, dependencyTracking: {}, dependenciesCount: 0, evaluationTimeoutInstance: null }; function computedObservable() { if (arguments.length > 0) { if (typeof writeFunction === "function") { // Writing a value writeFunction(...arguments); } else { 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."); } return this; // Permits chained assignments } else { // Reading the value if (!state.isDisposed) { ko.dependencyDetection.registerDependency(computedObservable); } if (state.isDirty || (state.isSleeping && computedObservable.haveDependenciesChanged())) { computedObservable.evaluateImmediate(); } return state.latestValue; } } computedObservable[computedState] = state; computedObservable.hasWriteFunction = typeof writeFunction === "function"; // Inherit from 'subscribable' ko.subscribable['fn'].init(computedObservable); // Inherit from 'computed' Object.setPrototypeOf(computedObservable, computedFn); if (options['pure']) { state.pure = true; state.isSleeping = true; // Starts off sleeping; will awake on the first subscription ko.utils.extend(computedObservable, pureComputedOverrides); } else if (options['deferEvaluation']) { ko.utils.extend(computedObservable, deferEvaluationOverrides); } if (DEBUG) { // #1731 - Aid debugging by exposing the computed's options computedObservable["_options"] = options; } if (state.disposeWhenNodeIsRemoved) { // Since this computed is associated with a DOM node, and we don't want to dispose the computed // until the DOM node is *removed* from the document (as opposed to never having been in the document), // we'll prevent disposal until "disposeWhen" first returns false. state.suppressDisposalUntilDisposeWhenReturnsFalse = true; // disposeWhenNodeIsRemoved: true can be used to opt into the "only dispose after first false result" // behaviour even if there's no specific node to watch. In that case, clear the option so we don't try // to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't // be documented or used by application code, as it's likely to change in a future version of KO. if (!state.disposeWhenNodeIsRemoved.nodeType) { state.disposeWhenNodeIsRemoved = null; } } // Evaluate, unless sleeping or deferEvaluation is true if (!state.isSleeping && !options['deferEvaluation']) { computedObservable.evaluateImmediate(); } // Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is // removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose). if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) { ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () { computedObservable.dispose(); }); } return computedObservable; }; // Utility function that disposes a given dependencyTracking entry function computedDisposeDependencyCallback(id, entryToDispose) { if (entryToDispose !== null && entryToDispose.dispose) { entryToDispose.dispose(); } } // This function gets called each time a dependency is detected while evaluating a computed. // It's factored out as a shared function to avoid creating unnecessary function instances during evaluation. function computedBeginDependencyDetectionCallback(subscribable, id) { var computedObservable = this.computedObservable, state = computedObservable[computedState]; if (!state.isDisposed) { if (this.disposalCount && this.disposalCandidates[id]) { // Don't want to dispose this subscription, as it's still being used computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]); this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway --this.disposalCount; } else if (!state.dependencyTracking[id]) { // Brand new subscription - add it computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable)); } // If the observable we've accessed has a pending notification, ensure we get notified of the actual final value (bypass equality checks) if (subscribable._notificationIsPending) { subscribable._notifyNextChangeIfValueIsDifferent(); } } } var computedFn = { "equalityComparer": valuesArePrimitiveAndEqual, getDependenciesCount: function () { return this[computedState].dependenciesCount; }, getDependencies: function () { var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = []; ko.utils.objectForEach(dependencyTracking, (id, dependency) => dependentObservables[dependency._order] = dependency._target ); return dependentObservables; }, hasAncestorDependency: function (obs) { if (!this[computedState].dependenciesCount) { return false; } var dependencies = this.getDependencies(); if (dependencies.includes(obs)) { return true; } return !!dependencies.find(dep => dep.hasAncestorDependency && dep.hasAncestorDependency(obs) ); }, addDependencyTracking: function (id, target, trackingObj) { if (this[computedState].pure && target === this) { throw Error("A 'pure' computed must not be called recursively"); } this[computedState].dependencyTracking[id] = trackingObj; trackingObj._order = this[computedState].dependenciesCount++; trackingObj._version = target.getVersion(); }, haveDependenciesChanged: function () { var id, dependency, dependencyTracking = this[computedState].dependencyTracking; for (id in dependencyTracking) { if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) { dependency = dependencyTracking[id]; if ((this._evalDelayed && dependency._target._notificationIsPending) || dependency._target.hasChanged(dependency._version)) { return true; } } } }, markDirty: function () { // Process "dirty" events if we can handle delayed notifications if (this._evalDelayed && !this[computedState].isBeingEvaluated) { this._evalDelayed(false /*isChange*/); } }, isActive: function () { var state = this[computedState]; return state.isDirty || state.dependenciesCount > 0; }, respondToChange: function () { // Ignore "change" events if we've already scheduled a delayed notification if (!this._notificationIsPending) { this.evaluatePossiblyAsync(); } else if (this[computedState].isDirty) { this[computedState].isStale = true; } }, subscribeToDependency: function (target) { return target.subscribe(this.evaluatePossiblyAsync, this); }, evaluatePossiblyAsync: function () { var computedObservable = this, throttleEvaluationTimeout = computedObservable['throttleEvaluation']; if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) { clearTimeout(this[computedState].evaluationTimeoutInstance); this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(() => computedObservable.evaluateImmediate(true /*notifyChange*/) , throttleEvaluationTimeout); } else if (computedObservable._evalDelayed) { computedObservable._evalDelayed(true /*isChange*/); } else { computedObservable.evaluateImmediate(true /*notifyChange*/); } }, evaluateImmediate: function (notifyChange) { var computedObservable = this, state = computedObservable[computedState], disposeWhen = state.disposeWhen, changed = false; if (state.isBeingEvaluated) { // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation. // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387 return; } // Do not evaluate (and possibly capture new dependencies) if disposed if (state.isDisposed) { return; } if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) { // See comment above about suppressDisposalUntilDisposeWhenReturnsFalse if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) { computedObservable.dispose(); return; } } else { // It just did return false, so we can stop suppressing now state.suppressDisposalUntilDisposeWhenReturnsFalse = false; } state.isBeingEvaluated = true; try { changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange); } finally { state.isBeingEvaluated = false; } return changed; }, evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) { // This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else. // Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate, // which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least). var computedObservable = this, state = computedObservable[computedState], changed = false; // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal). // Then, during evaluation, we cross off any that are in fact still being used. var isInitial = state.pure ? undefined : !state.dependenciesCount, // If we're evaluating when there are no previous dependencies, it must be the first time dependencyDetectionContext = { computedObservable: computedObservable, disposalCandidates: state.dependencyTracking, disposalCount: state.dependenciesCount }; ko.dependencyDetection.begin({ callbackTarget: dependencyDetectionContext, callback: computedBeginDependencyDetectionCallback, computed: computedObservable, isInitial: isInitial }); state.dependencyTracking = {}; state.dependenciesCount = 0; var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext); if (!state.dependenciesCount) { computedObservable.dispose(); changed = true; // When evaluation causes a disposal, make sure all dependent computeds get notified so they'll see the new state } else { changed = computedObservable.isDifferent(state.latestValue, newValue); } if (changed) { if (!state.isSleeping) { computedObservable["notifySubscribers"](state.latestValue, "beforeChange"); } else { computedObservable.updateVersion(); } state.latestValue = newValue; if (DEBUG) computedObservable._latestValue = newValue; computedObservable["notifySubscribers"](state.latestValue, "spectate"); if (!state.isSleeping && notifyChange) { computedObservable["notifySubscribers"](state.latestValue); } if (computedObservable._recordUpdate) { computedObservable._recordUpdate(); } } if (isInitial) { computedObservable["notifySubscribers"](state.latestValue, "awake"); } return changed; }, evaluateImmediate_CallReadThenEndDependencyDetection: (state, dependencyDetectionContext) => { // This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic. // You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection // can be independent of try/finally blocks, which contributes to saving about 40% off the CPU // overhead of computed evaluation (on V8 at least). try { return state.readFunction(); } finally { ko.dependencyDetection.end(); // For each subscription no longer being used, remove it from the active subscriptions list and dispose it if (dependencyDetectionContext.disposalCount && !state.isSleeping) { ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback); } state.isStale = state.isDirty = false; } }, peek: function (evaluate) { // By default, peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set. // Pass in true to evaluate if needed. var state = this[computedState]; if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) { this.evaluateImmediate(); } return state.latestValue; }, limit: function (limitFunction) { // Override the limit function with one that delays evaluation as well ko.subscribable['fn'].limit.call(this, limitFunction); this._evalIfChanged = function () { if (!this[computedState].isSleeping) { if (this[computedState].isStale) { this.evaluateImmediate(); } else { this[computedState].isDirty = false; } } return this[computedState].latestValue; }; this._evalDelayed = function (isChange) { this._limitBeforeChange(this[computedState].latestValue); // Mark as dirty this[computedState].isDirty = true; if (isChange) { this[computedState].isStale = true; } // Pass the observable to the "limit" code, which will evaluate it when // it's time to do the notification. this._limitChange(this, !isChange /* isDirty */); }; }, dispose: function () { var state = this[computedState]; if (!state.isSleeping && state.dependencyTracking) { ko.utils.objectForEach(state.dependencyTracking, (id, dependency) => dependency.dispose && dependency.dispose() ); } if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) { ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback); } state.dependencyTracking = undefined; state.dependenciesCount = 0; state.isDisposed = true; state.isStale = false; state.isDirty = false; state.isSleeping = false; state.disposeWhenNodeIsRemoved = undefined; state.disposeWhen = undefined; state.readFunction = undefined; } }; var pureComputedOverrides = { beforeSubscriptionAdd: function (event) { // If asleep, wake up the computed by subscribing to any dependencies. var computedObservable = this, state = computedObservable[computedState]; if (!state.isDisposed && state.isSleeping && event == 'change') { state.isSleeping = false; if (state.isStale || computedObservable.haveDependenciesChanged()) { state.dependencyTracking = null; state.dependenciesCount = 0; if (computedObservable.evaluateImmediate()) { computedObservable.updateVersion(); } } else { // First put the dependencies in order var dependenciesOrder = []; ko.utils.objectForEach(state.dependencyTracking, (id, dependency) => dependenciesOrder[dependency._order] = id ); // Next, subscribe to each one dependenciesOrder.forEach((id, order) => { var dependency = state.dependencyTracking[id], subscription = computedObservable.subscribeToDependency(dependency._target); subscription._order = order; subscription._version = dependency._version; state.dependencyTracking[id] = subscription; }); // Waking dependencies may have triggered effects if (computedObservable.haveDependenciesChanged()) { if (computedObservable.evaluateImmediate()) { computedObservable.updateVersion(); } } } if (!state.isDisposed) { // test since evaluating could trigger disposal computedObservable["notifySubscribers"](state.latestValue, "awake"); } } }, afterSubscriptionRemove: function (event) { var state = this[computedState]; if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) { ko.utils.objectForEach(state.dependencyTracking, (id, dependency) => { if (dependency.dispose) { state.dependencyTracking[id] = { _target: dependency._target, _order: dependency._order, _version: dependency._version }; dependency.dispose(); } }); state.isSleeping = true; this["notifySubscribers"](undefined, "asleep"); } }, getVersion: function () { // Because a pure computed is not automatically updated while it is sleeping, we can't // simply return the version number. Instead, we check if any of the dependencies have // changed and conditionally re-evaluate the computed observable. var state = this[computedState]; if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) { this.evaluateImmediate(); } return ko.subscribable['fn'].getVersion.call(this); } }; var deferEvaluationOverrides = { beforeSubscriptionAdd: function (event) { // This will force a computed with deferEvaluation to evaluate when the first subscription is registered. if (event == 'change' || event == 'beforeChange') { this.peek(); } } }; // Note that for browsers that don't support proto assignment, the // inheritance chain is created manually in the ko.computed constructor Object.setPrototypeOf(computedFn, ko.subscribable['fn']); // Set the proto values for ko.computed var protoProp = ko.observable.protoProperty; // == "__ko_proto__" computedFn[protoProp] = ko.computed; ko.exportSymbol('computed', ko.computed); ko.exportSymbol('computed.fn', computedFn); ko.exportProperty(computedFn, 'dispose', computedFn.dispose); ko.pureComputed = (evaluatorFunctionOrOptions) => { if (typeof evaluatorFunctionOrOptions === 'function') { return ko.computed(evaluatorFunctionOrOptions, {'pure':true}); } evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions); // make a copy of the parameter object evaluatorFunctionOrOptions['pure'] = true; return ko.computed(evaluatorFunctionOrOptions); }; (() => { var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__'; // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns. ko.selectExtensions = { readValue : element => { switch (element.nodeName) { case 'OPTION': if (element[hasDomDataExpandoProperty] === true) return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey); return element.value; case 'SELECT': return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined; default: return element.value; } }, writeValue: (element, value, allowUnset) => { switch (element.nodeName) { case 'OPTION': if (typeof value === "string") { ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined); delete element[hasDomDataExpandoProperty]; element.value = value; } else { // Store arbitrary object using DomData ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value); element[hasDomDataExpandoProperty] = true; // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value. element.value = typeof value === "number" ? value : ""; } break; case 'SELECT': // A blank string or null value will select the caption var selection = -1, noValue = ("" === value || null == value); for (var i = 0, n = element.options.length, optionValue; i < n; ++i) { optionValue = ko.selectExtensions.readValue(element.options[i]); // Include special check to handle selecting a caption with a blank string value if (optionValue == value || (optionValue === "" && noValue)) { selection = i; break; } } if (allowUnset || selection >= 0 || (noValue && element.size > 1)) { element.selectedIndex = selection; } break; default: element.value = (value == null) ? "" : value; break; } } }; })(); ko.expressionRewriting = (() => { var javaScriptReservedWords = ["true", "false", "null", "undefined"]; // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c). // This also will not properly handle nested brackets (e.g., obj1[obj2['prop']]; see #911). var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i; function getWriteableValue(expression) { if (javaScriptReservedWords.includes(expression)) return false; var match = expression.match(javaScriptAssignmentTarget); return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression; } // The following regular expressions will be used to split an object-literal string into tokens var specials = ',"\'`{}()/:[\\]', // These characters have special meaning to the parser and must not appear in the middle of a token, except as part of a string. // Create the actual regular expression by or-ing the following regex strings. The order is important. bindingToken = RegExp([ // These match strings, either with double quotes, single quotes, or backticks '"(?:\\\\.|[^"])*"', "'(?:\\\\.|[^'])*'", "`(?:\\\\.|[^`])*`", // Match C style comments "/\\*(?:[^*]|\\*+[^*/])*\\*+/", // Match C++ style comments "//.*\n", // Match a regular expression (text enclosed by slashes), but will also match sets of divisions // as a regular expression (this is handled by the parsing loop below). '/(?:\\\\.|[^/])+/w*', // Match text (at least two characters) that does not contain any of the above special characters, // although some of the special characters are allowed to start it (all but the colon and comma). // The text can contain spaces, but leading or trailing spaces are skipped. '[^\\s:,/][^' + specials + ']*[^\\s' + specials + ']', // Match any non-space character not matched already. This will match colons and commas, since they're // not matched by "everyThingElse", but will also match any other single character that wasn't already // matched (for example: in "a: 1, b: 2", each of the non-space characters will be matched by oneNotSpace). '[^\\s]' ].join('|'), 'g'), // Match end of previous token to determine whether a slash is a division or regex. divisionLookBehind = /[\])"'A-Za-z0-9_$]+$/, keywordRegexLookBehind = {'in':1,'return':1,'typeof':1}; function parseObjectLiteral(objectLiteralString) { // Trim leading and trailing spaces from the string var str = ko.utils.stringTrim(objectLiteralString); // Trim braces '{' surrounding the whole object literal if (str.charCodeAt(0) === 123) str = str.slice(1, -1); // Add a newline to correctly match a C++ style comment at the end of the string and // add a comma so that we don't need a separate code block to deal with the last item str += "\n,"; // Split into tokens var result = [], toks = str.match(bindingToken), key, values = [], depth = 0; if (toks.length > 1) { for (var i = 0, tok; tok = toks[i]; ++i) { var c = tok.charCodeAt(0); // A comma signals the end of a key/value pair if depth is zero if (c === 44) { // "," if (depth <= 0) { result.push((key && values.length) ? {key: key, value: values.join('')} : {'unknown': key || values.join('')}); key = depth = 0; values = []; continue; } // Simply skip the colon that separates the name and value } else if (c === 58) { // ":" if (!depth && !key && values.length === 1) { key = values.pop(); continue; } // Comments: skip them } else if (c === 47 && tok.length > 1 && (tok.charCodeAt(1) === 47 || tok.charCodeAt(1) === 42)) { // "//" or "/*" continue; // A set of slashes is initially matched as a regular expression, but could be division } else if (c === 47 && i && tok.length > 1) { // "/" // Look at the end of the previous token to determine if the slash is actually division var match = toks[i-1].match(divisionLookBehind); if (match && !keywordRegexLookBehind[match[0]]) { // The slash is actually a division punctuator; re-parse the remainder of the string (not including the slash) str = str.substr(str.indexOf(tok) + 1); toks = str.match(bindingToken); i = -1; // Continue with just the slash tok = '/'; } // Increment depth for parentheses, braces, and brackets so that interior commas are ignored } else if (c === 40 || c === 123 || c === 91) { // '(', '{', '[' ++depth; } else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']' --depth; // The key will be the first token; if it's a string, trim the quotes } else if (!key && !values.length && (c === 34 || c === 39)) { // '"', "'" tok = tok.slice(1, -1); } values.push(tok); } if (depth > 0) { throw Error("Unbalanced parentheses, braces, or brackets"); } } return result; } // Two-way bindings include a write function that allow the handler to update the value even if it's not an observable. var twoWayBindings = {}; function preProcessBindings(bindingsStringOrKeyValueArray, bindingOptions) { bindingOptions = bindingOptions || {}; function processKeyValue(key, val) { var writableVal; function callPreprocessHook(obj) { return (obj && obj['preprocess']) ? (val = obj['preprocess'](val, key, processKeyValue)) : true; } if (!bindingParams) { if (!callPreprocessHook(ko['getBindingHandler'](key))) return; if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) { // For two-way bindings, provide a write method in case the value // isn't a writable observable. var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key; propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + writableVal + "=_z}"); } } // Values are wrapped in a function so that each value can be accessed independently if (makeValueAccessors) { val = 'function(){return ' + val + ' }'; } resultStrings.push("'" + key + "':" + val); } var resultStrings = [], propertyAccessorResultStrings = [], makeValueAccessors = bindingOptions['valueAccessors'], bindingParams = bindingOptions['bindingParams'], keyValueArray = typeof bindingsStringOrKeyValueArray === "string" ? parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray; keyValueArray.forEach(keyValue => processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value) ); if (propertyAccessorResultStrings.length) processKeyValue('_ko_property_writers', "{" + propertyAccessorResultStrings.join(",") + " }"); return resultStrings.join(","); } return { bindingRewriteValidators: [], twoWayBindings: twoWayBindings, parseObjectLiteral: parseObjectLiteral, preProcessBindings: preProcessBindings, keyValueArrayContainsKey: (keyValueArray, key) => -1 < keyValueArray.findIndex(v => v['key'] == key), // Internal, private KO utility for updating model properties from within bindings // property: If the property being updated is (or might be) an observable, pass it here // If it turns out to be a writable observable, it will be written to directly // allBindings: An object with a get method to retrieve bindings in the current execution context. // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus' // value: The value to be written // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if // it is !== existing value on that writable observable writeValueToProperty: (property, allBindings, key, value, checkIfDifferent) => { if (!property || !ko.isObservable(property)) { var propWriters = allBindings.get('_ko_property_writers'); if (propWriters && propWriters[key]) propWriters[key](value); } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) { property(value); } } }; })(); (() => { // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes // may be used to represent hierarchy (in addition to the DOM's natural hierarchy). // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state // of that virtual hierarchy // // The point of all this is to support containerless templates (e.g., blah) // without having to scatter special cases all over the binding and templating code. var startCommentRegex = /^\s*ko(?:\s+([\s\S]+))?\s*$/; var endCommentRegex = /^\s*\/ko\s*$/; function isStartComment(node) { return (node.nodeType == 8) && startCommentRegex.test(node.nodeValue); } function isEndComment(node) { return (node.nodeType == 8) && endCommentRegex.test(node.nodeValue); } function isUnmatchedEndComment(node) { return isEndComment(node) && !(ko.utils.domData.get(node, matchedEndCommentDataKey)); } var matchedEndCommentDataKey = "__ko_matchedEndComment__" function getVirtualChildren(startComment, allowUnbalanced) { var currentNode = startComment; var depth = 1; var children = []; while (currentNode = currentNode.nextSibling) { if (isEndComment(currentNode)) { ko.utils.domData.set(currentNode, matchedEndCommentDataKey, true); depth--; if (depth === 0) return children; } children.push(currentNode); if (isStartComment(currentNode)) depth++; } if (!allowUnbalanced) throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue); return null; } function getMatchingEndComment(startComment, allowUnbalanced) { var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced); if (allVirtualChildren) { if (allVirtualChildren.length > 0) return allVirtualChildren[allVirtualChildren.length - 1].nextSibling; return startComment.nextSibling; } return null; // Must have no matching end comment, and allowUnbalanced is true } ko.virtualElements = { allowedBindings: {}, childNodes: node => isStartComment(node) ? getVirtualChildren(node) : node.childNodes, emptyNode: node => { if (isStartComment(node)) { var virtualChildren = getVirtualChildren(node); for (var i = 0, j = virtualChildren.length; i < j; i++) ko.removeNode(virtualChildren[i]); } else ko.utils.emptyDomNode(node); }, setDomNodeChildren: (node, childNodes) => { if (isStartComment(node)) { ko.virtualElements.emptyNode(node); var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children for (var i = 0, j = childNodes.length; i < j; i++) endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode); } else ko.utils.setDomNodeChildren(node, childNodes); }, prepend: (containerNode, nodeToPrepend) => { var insertBeforeNode; if (isStartComment(containerNode)) { // Start comments must always have a parent and at least one following sibling (the end comment) insertBeforeNode = containerNode.nextSibling; containerNode = containerNode.parentNode; } else { insertBeforeNode = containerNode.firstChild; } containerNode.insertBefore(nodeToPrepend, insertBeforeNode); }, insertAfter: (containerNode, nodeToInsert, insertAfterNode) => { if (insertAfterNode) { // Children of start comments must always have a parent and at least one following sibling (the end comment) var insertBeforeNode = insertAfterNode.nextSibling; if (isStartComment(containerNode)) { containerNode = containerNode.parentNode; } containerNode.insertBefore(nodeToInsert, insertBeforeNode); } else { ko.virtualElements.prepend(containerNode, nodeToInsert); } }, firstChild: node => { if (isStartComment(node)) { return (!node.nextSibling || isEndComment(node.nextSibling)) ? null : node.nextSibling; } if (node.firstChild && isEndComment(node.firstChild)) { throw new Error("Found invalid end comment, as the first child of " + node); } return node.firstChild; }, nextSibling: node => { if (isStartComment(node)) { node = getMatchingEndComment(node); } if (node.nextSibling && isEndComment(node.nextSibling)) { if (isUnmatchedEndComment(node.nextSibling)) { throw Error("Found end comment without a matching opening comment, as child of " + node); } return null; } return node.nextSibling; }, hasBindingValue: isStartComment, virtualNodeBindingValue: node => { var regexMatch = node.nodeValue.match(startCommentRegex); return regexMatch ? regexMatch[1] : null; } }; })(); (() => { const defaultBindingAttributeName = "data-bind", bindingCache = {}, createBindingsStringEvaluatorViaCache = (bindingsString, cache, options) => { var cacheKey = bindingsString + (options && options['valueAccessors'] || ''); return cache[cacheKey] || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options)); }, createBindingsStringEvaluator = (bindingsString, options) => { // Build the source for a function that evaluates "expression" // For each scope variable, add an extra level of "with" nesting // Example result: with(sc1) { with(sc0) { return (expression) } } var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options), functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}"; return new Function("$context", "$element", functionBody); }, // The following function is only used internally by this default provider. // It's not part of the interface definition for a general binding provider. getBindingsString = node => { switch (node.nodeType) { case 1: return node.getAttribute(defaultBindingAttributeName); // Element case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node } return null; }, // The following function is only used internally by this default provider. // It's not part of the interface definition for a general binding provider. parseBindingsString = (bindingsString, bindingContext, node, options) => { try { var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, bindingCache, options); return bindingFunction(bindingContext, node); } catch (ex) { ex.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + ex.message; throw ex; } }; ko.bindingProvider = new class { nodeHasBindings(node) { switch (node.nodeType) { case 1: // Element return node.getAttribute(defaultBindingAttributeName) != null; case 8: // Comment node return ko.virtualElements.hasBindingValue(node); default: return false; } } getBindingAccessors(node, bindingContext) { var bindingsString = getBindingsString(node, bindingContext); return bindingsString ? parseBindingsString(bindingsString, bindingContext, node, { 'valueAccessors': true }) : null; } }; })(); (() => { // Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294 var contextSubscribable = Symbol('_subscribable'); var contextAncestorBindingInfo = Symbol('_ancestorBindingInfo'); var contextDataDependency = Symbol('_dataDependency'); ko.bindingHandlers = {}; // Use an overridable method for retrieving binding handlers so that plugins may support dynamically created handlers ko['getBindingHandler'] = bindingKey => ko.bindingHandlers[bindingKey]; var inheritParentVm = {}; // The ko.bindingContext constructor is only called directly to create the root context. For child // contexts, use bindingContext.createChildContext or bindingContext.extend. ko.bindingContext = function(dataItemOrAccessor, parentContext, dataItemAlias, extendCallback, options) { // The binding context object includes static properties for the current, parent, and root view models. // If a view model is actually stored in an observable, the corresponding binding context object, and // any child contexts, must be updated when the view model is changed. function updateContext() { // Most of the time, the context will directly get a view model object, but if a function is given, // we call the function to retrieve the view model. If the function accesses any observables or returns // an observable, the dependency is tracked, and those observables can later cause the binding // context to be updated. var dataItemOrObservable = isFunc ? realDataItemOrAccessor() : realDataItemOrAccessor, dataItem = ko.utils.unwrapObservable(dataItemOrObservable); if (parentContext) { // Copy $root and any custom properties from the parent context ko.utils.extend(self, parentContext); // Copy Symbol properties if (contextAncestorBindingInfo in parentContext) { self[contextAncestorBindingInfo] = parentContext[contextAncestorBindingInfo]; } } else { self['$parents'] = []; self['$root'] = dataItem; // Export 'ko' in the binding context so it will be available in bindings and templates // even if 'ko' isn't exported as a global, such as when using an AMD loader. // See https://github.com/SteveSanderson/knockout/issues/490 self['ko'] = ko; } self[contextSubscribable] = subscribable; if (shouldInheritData) { dataItem = self['$data']; } else { self['$rawData'] = dataItemOrObservable; self['$data'] = dataItem; } if (dataItemAlias) self[dataItemAlias] = dataItem; // The extendCallback function is provided when creating a child context or extending a context. // It handles the specific actions needed to finish setting up the binding context. Actions in this // function could also add dependencies to this binding context. if (extendCallback) extendCallback(self, parentContext, dataItem); // When a "parent" context is given and we don't already have a dependency on its context, register a dependency on it. // Thus whenever the parent context is updated, this context will also be updated. if (parentContext && parentContext[contextSubscribable] && !ko.dependencyDetection.computed().hasAncestorDependency(parentContext[contextSubscribable])) { parentContext[contextSubscribable](); } if (dataDependency) { self[contextDataDependency] = dataDependency; } return self['$data']; } var self = this, shouldInheritData = dataItemOrAccessor === inheritParentVm, realDataItemOrAccessor = shouldInheritData ? undefined : dataItemOrAccessor, isFunc = typeof(realDataItemOrAccessor) == "function" && !ko.isObservable(realDataItemOrAccessor), subscribable, dataDependency = options && options['dataDependency']; if (options && options['exportDependencies']) { // The "exportDependencies" option means that the calling code will track any dependencies and re-create // the binding context when they change. updateContext(); } else { subscribable = ko.pureComputed(updateContext); subscribable.peek(); // At this point, the binding context has been initialized, and the "subscribable" computed observable is // subscribed to any observables that were accessed in the process. If there is nothing to track, the // computed will be inactive, and we can safely throw it away. If it's active, the computed is stored in // the context object. if (subscribable.isActive()) { // Always notify because even if the model ($data) hasn't changed, other context properties might have changed subscribable['equalityComparer'] = null; } else { self[contextSubscribable] = undefined; } } } // Extend the binding context hierarchy with a new view model object. If the parent context is watching // any observables, the new child context will automatically get a dependency on the parent context. // But this does not mean that the $data value of the child context will also get updated. If the child // view model also depends on the parent view model, you must provide a function that returns the correct // view model on each update. ko.bindingContext.prototype['createChildContext'] = function (dataItemOrAccessor, dataItemAlias, extendCallback, options) { if (!options && dataItemAlias && typeof dataItemAlias == "object") { options = dataItemAlias; dataItemAlias = options['as']; extendCallback = options['extend']; } if (dataItemAlias && options && options['noChildContext']) { var isFunc = typeof(dataItemOrAccessor) == "function" && !ko.isObservable(dataItemOrAccessor); return new ko.bindingContext(inheritParentVm, this, null, self => { if (extendCallback) extendCallback(self); self[dataItemAlias] = isFunc ? dataItemOrAccessor() : dataItemOrAccessor; }, options); } return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, (self, parentContext) => { // Extend the context hierarchy by setting the appropriate pointers self['$parentContext'] = parentContext; self['$parent'] = parentContext['$data']; self['$parents'] = (parentContext['$parents'] || []).slice(0); self['$parents'].unshift(self['$parent']); if (extendCallback) extendCallback(self); }, options); }; // Extend the binding context with new custom properties. This doesn't change the context hierarchy. // Similarly to "child" contexts, provide a function here to make sure that the correct values are set // when an observable view model is updated. ko.bindingContext.prototype['extend'] = function(properties, options) { return new ko.bindingContext(inheritParentVm, this, null, self => ko.utils.extend(self, typeof(properties) == "function" ? properties(self) : properties) , options); }; var boundElementDomDataKey = ko.utils.domData.nextKey(); function asyncContextDispose(node) { var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey), asyncContext = bindingInfo && bindingInfo.asyncContext; if (asyncContext) { bindingInfo.asyncContext = null; asyncContext.notifyAncestor(); } } function AsyncCompleteContext(node, bindingInfo, ancestorBindingInfo) { this.node = node; this.bindingInfo = bindingInfo; this.asyncDescendants = []; this.childrenComplete = false; if (!bindingInfo.asyncContext) { ko.utils.domNodeDisposal.addDisposeCallback(node, asyncContextDispose); } if (ancestorBindingInfo && ancestorBindingInfo.asyncContext) { ancestorBindingInfo.asyncContext.asyncDescendants.push(node); this.ancestorBindingInfo = ancestorBindingInfo; } } AsyncCompleteContext.prototype.notifyAncestor = function () { if (this.ancestorBindingInfo && this.ancestorBindingInfo.asyncContext) { this.ancestorBindingInfo.asyncContext.descendantComplete(this.node); } }; AsyncCompleteContext.prototype.descendantComplete = function (node) { ko.utils.arrayRemoveItem(this.asyncDescendants, node); if (!this.asyncDescendants.length && this.childrenComplete) { this.completeChildren(); } }; AsyncCompleteContext.prototype.completeChildren = function () { this.childrenComplete = true; if (this.bindingInfo.asyncContext && !this.asyncDescendants.length) { this.bindingInfo.asyncContext = null; ko.utils.domNodeDisposal.removeDisposeCallback(this.node, asyncContextDispose); ko.bindingEvent.notify(this.node, ko.bindingEvent.descendantsComplete); this.notifyAncestor(); } }; ko.bindingEvent = { childrenComplete: "childrenComplete", descendantsComplete : "descendantsComplete", subscribe: (node, event, callback, context, options) => { var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {}); if (!bindingInfo.eventSubscribable) { bindingInfo.eventSubscribable = new ko.subscribable; } if (options && options['notifyImmediately'] && bindingInfo.notifiedEvents[event]) { ko.dependencyDetection.ignore(callback, context, [node]); } return bindingInfo.eventSubscribable.subscribe(callback, context, event); }, notify: (node, event) => { var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey); if (bindingInfo) { bindingInfo.notifiedEvents[event] = true; if (bindingInfo.eventSubscribable) { bindingInfo.eventSubscribable['notifySubscribers'](node, event); } if (event == ko.bindingEvent.childrenComplete) { if (bindingInfo.asyncContext) { bindingInfo.asyncContext.completeChildren(); } else if (bindingInfo.asyncContext === undefined && bindingInfo.eventSubscribable && bindingInfo.eventSubscribable.hasSubscriptionsForEvent(ko.bindingEvent.descendantsComplete)) { // It's currently an error to register a descendantsComplete handler for a node that was never registered as completing asynchronously. // That's because without the asyncContext, we don't have a way to know that all descendants have completed. throw new Error("descendantsComplete event not supported for bindings on this node"); } } } }, startPossiblyAsyncContentBinding: (node, bindingContext) => { var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {}); if (!bindingInfo.asyncContext) { bindingInfo.asyncContext = new AsyncCompleteContext(node, bindingInfo, bindingContext[contextAncestorBindingInfo]); } // If the provided context was already extended with this node's binding info, just return the extended context if (bindingContext[contextAncestorBindingInfo] == bindingInfo) { return bindingContext; } return bindingContext['extend'](ctx => { ctx[contextAncestorBindingInfo] = bindingInfo; }); } }; // Given a function that returns bindings, create and return a new object that contains // binding value-accessors functions. Each accessor function calls the original function // so that it always gets the latest value and all dependencies are captured. This is used // by ko.applyBindingsToNode and getBindingsAndMakeAccessors. function makeAccessorsFromFunction(callback) { return ko.utils.objectMap(ko.dependencyDetection.ignore(callback), (value, key) => () => callback()[key] ); } // Given a bindings function or object, create and return a new object that contains // binding value-accessors functions. This is used by ko.applyBindingsToNode. function makeBindingAccessors(bindings, context, node) { return (typeof bindings === 'function') ? makeAccessorsFromFunction(bindings.bind(null, context, node)) : ko.utils.objectMap(bindings, value => () => value); } function validateThatBindingIsAllowedForVirtualElements(bindingName) { var validator = ko.virtualElements.allowedBindings[bindingName]; if (!validator) throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements") } function applyBindingsToDescendantsInternal(bindingContext, elementOrVirtualElement) { var nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement); if (nextInQueue) { var currentChild; while (currentChild = nextInQueue) { // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position nextInQueue = ko.virtualElements.nextSibling(currentChild); applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild); } } ko.bindingEvent.notify(elementOrVirtualElement, ko.bindingEvent.childrenComplete); } function applyBindingsToNodeAndDescendantsInternal(bindingContext, nodeVerified) { var bindingContextForDescendants = bindingContext; var isElement = (nodeVerified.nodeType === 1); // Perf optimisation: Apply bindings only if... // (1) We need to store the binding info for the node (all element nodes) // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template) var shouldApplyBindings = isElement || ko.bindingProvider.nodeHasBindings(nodeVerified); if (shouldApplyBindings) bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext)['bindingContextForDescendants']; // Don't want bindings that operate on text nodes to mutate