/*! * 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) */ (function(){ 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 = (() => { const arrayCall = (name, arr, p1, p2) => Array.prototype[name].call(arr, p1, p2), objectForEach = (obj, action) => obj && Object.entries(obj).forEach(prop => action(prop[0], prop[1])), extend = (target, source) => { source && Object.entries(source).forEach(prop => target[prop[0]] = prop[1]); return target; }, setPrototypeOf = (obj, proto) => { obj.__proto__ = proto; return obj; }, // For details on the pattern for changing node classes // see: https://github.com/knockout/knockout/issues/1597 toggleDomNodeCssClass = (node, classNames, shouldHaveClass) => { if (classNames) { var addOrRemoveFn = shouldHaveClass ? 'add' : 'remove'; classNames.split(/\s+/).forEach(className => node.classList[addOrRemoveFn](className) ); } }; var canSetPrototype = ({ __proto__: [] } instanceof Array); return { arrayForEach: (array, action, actionOwner) => arrayCall('forEach', array, action, actionOwner), arrayIndexOf: (array, item) => arrayCall('indexOf', array, item), arrayFirst: (array, predicate, predicateOwner) => arrayCall('find', array, (e, i, a) => predicate.call(predicateOwner, e, i, a)), arrayRemoveItem: (array, itemToRemove) => { var index = ko.utils.arrayIndexOf(array, itemToRemove); if (index > 0) { array.splice(index, 1); } else if (index === 0) { array.shift(); } }, arrayPushAll: (array, valuesToPush) => { if (valuesToPush instanceof Array) array.push.apply(array, valuesToPush); else for (var i = 0, j = valuesToPush.length; i < j; i++) array.push(valuesToPush[i]); return array; }, canSetPrototype: canSetPrototype, extend: extend, setPrototypeOf: setPrototypeOf, setPrototypeOfOrExtend: canSetPrototype ? setPrototypeOf : extend, objectForEach: objectForEach, 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 = ko.utils.makeArray(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) => arrayCall('map', nodesArray, shouldCleanNodes ? node => ko.cleanNode(node.cloneNode(true)) : node => node.cloneNode(true) ), setDomNodeChildren: (domNode, childNodes) => { ko.utils.emptyDomNode(domNode); childNodes && domNode.append.apply(domNode, 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), // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case. // Possible future optimization: If we know it's an element from an XHTML document (not HTML), // we don't need to do the .toLowerCase() as it will always be lower case anyway. tagNameLower: element => element && element.tagName && element.tagName.toLowerCase(), 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, peekObservable: value => ko.isObservable(value) ? value.peek() : value, toggleDomNodeCssClass: toggleDomNodeCssClass, setTextContent: (element, textContent) => element.textContent = ko.utils.unwrapObservable(textContent) || "", makeArray: arrayLikeObject => Array['from'](arrayLikeObject) } })(); ko.exportSymbol('utils', ko.utils); ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly ko.utils.domData = new (function () { var uniqueId = 0; var dataStoreKeyExpandoPropertyName = "__ko__" + (Date.now()); var // We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using // it cross-window, so instead we just store the data directly on the node. // See https://github.com/knockout/knockout/issues/2141 getDataForNode = (node, createIfNotFound) => { var dataForNode = node[dataStoreKeyExpandoPropertyName]; if (!dataForNode && createIfNotFound) { dataForNode = node[dataStoreKeyExpandoPropertyName] = {}; } return dataForNode; }, clear = node => { if (node[dataStoreKeyExpandoPropertyName]) { delete node[dataStoreKeyExpandoPropertyName]; return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended } return false; }; return { get: (node, key) => { var dataForNode = getDataForNode(node, false); return dataForNode && dataForNode[key]; }, set: (node, key, value) => { // Make sure we don't actually create a new domData key if we are actually deleting a value var dataForNode = getDataForNode(node, value !== undefined /* createIfNotFound */); dataForNode && (dataForNode[key] = value); }, getOrSet: (node, key, value) => { var dataForNode = getDataForNode(node, true /* createIfNotFound */); return dataForNode[key] || (dataForNode[key] = value); }, clear: clear, 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-- && ko.utils.arrayIndexOf(cleanedNodes, nodeList[i]) == -1); } } } } 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); (() => { var none = [0, "", ""], table = [1, "", "
"], tbody = [2, "", "
"], tr = [3, "", "
"], select = [1, ""], lookup = { 'thead': table, 'tbody': table, 'tfoot': table, 'tr': tbody, 'td': tr, 'th': tr, 'option': select, 'optgroup': select }; function getWrap(tags) { var m = tags.match(/^(?:\s*?)*?<([a-z]+)[\s>]/); return (m && lookup[m[1]]) || none; } function simpleHtmlParse(html, documentContext) { documentContext || (documentContext = document); var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window; // Based on jQuery's "clean" function, but only accounting for table-related elements. // Trim whitespace, otherwise indexOf won't work as expected var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div"), wrap = getWrap(tags), depth = wrap[0]; // Go to html and back, then peel off extra wrappers // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness. var markup = "ignored
" + wrap[1] + html + wrap[2] + "
"; if (typeof windowContext['innerShiv'] == "function") { // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv // somehow shims the native APIs so it just works anyway) div.append(windowContext['innerShiv'](markup)); } else { div.innerHTML = markup; } // Move to the right depth while (depth--) div = div.lastChild; return ko.utils.makeArray(div.lastChild.childNodes); } ko.utils.parseHtmlFragment = (html, documentContext) => simpleHtmlParse(html, documentContext); // ... otherwise, this simple logic will do in most common cases. ko.utils.parseHtmlForTemplateNodes = (html, documentContext) => { var nodes = ko.utils.parseHtmlFragment(html, documentContext); return (nodes.length && nodes[0].parentElement) || ko.utils.moveCleanedNodesToContainerElement(nodes); }; ko.utils.setHtml = (node, html) => { ko.utils.emptyDomNode(node); // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it html = ko.utils.unwrapObservable(html); if ((html !== null) && (html !== undefined)) { if (typeof html != 'string') html = html.toString(); // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments, // for example elements which are not normally allowed to exist on their own. // If you've referenced jQuery we'll use that rather than duplicating its code. // ... otherwise, use KO's own parsing logic. var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument); for (var i = 0; i < parsedNodes.length; i++) node.appendChild(parsedNodes[i]); } }; })(); 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 = { 'throttle': (target, timeout) => { // Throttling means two things: // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate target['throttleEvaluation'] = timeout; // (2) For writable targets (observables, or writable dependent observables), we throttle *writes* // so the target cannot change value synchronously or faster than a certain rate var writeTimeoutInstance = null; return ko.computed({ 'read': target, 'write': value => { clearTimeout(writeTimeoutInstance); writeTimeoutInstance = ko.utils.setTimeout(() => target(value), 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 : method == 'notifyWhenChangesStop' ? debounce : 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) { var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes); return oldValueIsPrimitive ? (a === b) : false; } function throttle(callback, timeout) { var timeoutInstance; return () => { if (!timeoutInstance) { timeoutInstance = ko.utils.setTimeout(() => { timeoutInstance = undefined; callback(); }, timeout); } }; } function debounce(callback, timeout) { var timeoutInstance; return () => { clearTimeout(timeoutInstance); timeoutInstance = ko.utils.setTimeout(callback, timeout); }; } function applyExtenders(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.exportSymbol('extenders', ko.extenders); ko.subscription = function (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); }; ko.subscription.prototype.dispose = function () { 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; } }; ko.subscription.prototype.disposeWhenNodeIsRemoved = function (node) { this._node = node; ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this)); }; ko.subscribable = function () { ko.utils.setPrototypeOfOrExtend(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 ko.subscription(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) for (var i = 0, subscription; subscription = subs[i]; ++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; }, getSubscriptionsCount: function (event) { if (event) { return this._subscriptions[event] && this._subscriptions[event].length || 0; } var total = 0; ko.utils.objectForEach(this._subscriptions, (eventName, subscriptions) => total += subscriptions.length ); return total; }, isDifferent: function(oldValue, newValue) { return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue); }, toString: () => '[object Object]', extend: applyExtenders }; 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); ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount); // 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. if (ko.utils.canSetPrototype) { ko.utils.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.computedContext = ko.dependencyDetection = (() => { var outerFrames = [], currentFrame, lastId = 0; function begin(options) { outerFrames.push(currentFrame); currentFrame = options; } function 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: () => { if (currentFrame) return currentFrame.computed.getDependenciesCount(); }, getDependencies: () => { if (currentFrame) return currentFrame.computed.getDependencies(); }, isInitial: () => { if (currentFrame) return currentFrame.isInitial; }, computed: () => { if (currentFrame) return currentFrame.computed; } }; })(); var observableLatestValue = Symbol('_latestValue'); 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; // Inherit from 'subscribable' if (!ko.utils.canSetPrototype) { // 'subscribable' won't be on the prototype chain unless we put it there directly ko.utils.extend(observable, ko.subscribable['fn']); } ko.subscribable['fn'].init(observable); // Inherit from 'observable' ko.utils.setPrototypeOfOrExtend(observable, observableFn); return observable; } // Define prototype for observables var observableFn = { 'toJSON': function() { let value = this[observableLatestValue]; return 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 if (ko.utils.canSetPrototype) { ko.utils.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); ko.utils.setPrototypeOfOrExtend(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; }; for (var i = 0; i < underlyingArray.length; 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); i--; } } 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'](function (value) { return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0; }); }, 'indexOf': function (item) { return ko.utils.arrayIndexOf(this(), item); } }; // Note that for browsers that don't support proto assignment, the // inheritance chain is created manually in the ko.observableArray constructor if (ko.utils.canSetPrototype) { ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']); } // Populate ko.observableArray.fn with read/write functions from native arrays // 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 ["pop", "push", "reverse", "shift", "sort", "splice", "unshift"].forEach(methodName => { ko.observableArray['fn'][methodName] = function () { // 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, arguments); var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments); 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; }; }); // Populate ko.observableArray.fn with read-only functions from native arrays ["slice"].forEach(methodName => { ko.observableArray['fn'][methodName] = function () { var underlyingArray = this(); return underlyingArray[methodName].apply(underlyingArray, arguments); }; }); 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, evaluatorFunctionTarget, 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"], evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"], 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.apply(state.evaluatorFunctionTarget, 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' if (!ko.utils.canSetPrototype) { // 'subscribable' won't be on the prototype chain unless we put it there directly ko.utils.extend(computedObservable, ko.subscribable['fn']); } ko.subscribable['fn'].init(computedObservable); // Inherit from 'computed' ko.utils.setPrototypeOfOrExtend(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 (ko.utils.arrayIndexOf(dependencies, obs) !== -1) { return true; } return !!ko.utils.arrayFirst(dependencies, 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 { var readFunction = state.readFunction; return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : 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; if (!this.hasWriteFunction) { state.evaluatorFunctionTarget = 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 ko.utils.arrayForEach(dependenciesOrder, (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 if (ko.utils.canSetPrototype) { ko.utils.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, evaluatorFunctionTarget) => { if (typeof evaluatorFunctionOrOptions === 'function') { return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, {'pure':true}); } evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions); // make a copy of the parameter object evaluatorFunctionOrOptions['pure'] = true; return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget); }; (() => { 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 (ko.utils.tagNameLower(element)) { 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 (ko.utils.tagNameLower(element)) { 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': if (value === "" || value === null) // A blank string or null value will select the caption value = undefined; var selection = -1; 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 === "" && value === undefined)) { selection = i; break; } } if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) { element.selectedIndex = selection; } break; default: if ((value === null) || (value === undefined)) value = ""; element.value = 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 (ko.utils.arrayIndexOf(javaScriptReservedWords, expression) >= 0) 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; ko.utils.arrayForEach(keyValueArray, 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) => { for (var i = 0; i < keyValueArray.length; i++) if (keyValueArray[i]['key'] == key) return true; return false; }, // 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)) ko.utils.emptyDomNode(node); else { var virtualChildren = ko.virtualElements.childNodes(node); for (var i = 0, j = virtualChildren.length; i < j; i++) ko.removeNode(virtualChildren[i]); } }, setDomNodeChildren: (node, childNodes) => { if (!isStartComment(node)) ko.utils.setDomNodeChildren(node, childNodes); else { 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); } }, 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) { ko.virtualElements.prepend(containerNode, nodeToInsert); } else { // 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); } }, firstChild: node => { if (!isStartComment(node)) { if (node.firstChild && isEndComment(node.firstChild)) { throw new Error("Found invalid end comment, as the first child of " + node); } return node.firstChild; } return (!node.nextSibling || isEndComment(node.nextSibling)) ? null : node.nextSibling; }, 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; } }; })(); (function() { var defaultBindingAttributeName = "data-bind"; ko.bindingProvider = function() { this.bindingCache = {}; }; ko.utils.extend(ko.bindingProvider.prototype, { 'nodeHasBindings': node => { switch (node.nodeType) { case 1: // Element return node.getAttribute(defaultBindingAttributeName) != null || ko.components['getComponentNameForNode'](node); case 8: // Comment node return ko.virtualElements.hasBindingValue(node); default: return false; } }, 'getBindings': function(node, bindingContext) { var bindingsString = this['getBindingsString'](node, bindingContext), parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null; return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ false); }, 'getBindingAccessors': function(node, bindingContext) { var bindingsString = this['getBindingsString'](node, bindingContext), parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node, { 'valueAccessors': true }) : null; return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ true); }, // 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, bindingContext) => { 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': function(bindingsString, bindingContext, node, options) { try { var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache, options); return bindingFunction(bindingContext, node); } catch (ex) { ex.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + ex.message; throw ex; } } }); ko.bindingProvider['instance'] = new ko.bindingProvider(); function createBindingsStringEvaluatorViaCache(bindingsString, cache, options) { var cacheKey = bindingsString + (options && options['valueAccessors'] || ''); return cache[cacheKey] || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options)); } function 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); } })(); (() => { // 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 = {}; // The following element types will not be recursed into during binding. var bindingDoesNotRecurseIntoElementTypes = { // Don't want bindings that operate on text nodes to mutate