ko/bck2brwsr/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 24 Jun 2013 17:50:44 +0200
branchclassloader
changeset 1225 73c0973e8e0a
parent 1189 ko-bck2brwsr/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js@9f8b07dcbe79
permissions -rw-r--r--
Moving all knockout related code under the 'ko' directory
     1 /*
     2  * HTML via Java(tm) Language Bindings
     3  * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details. apidesign.org
    13  * designates this particular file as subject to the
    14  * "Classpath" exception as provided by apidesign.org
    15  * in the License file that accompanied this code.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program. Look for COPYING file in the top folder.
    19  * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    20  */
    21 // Knockout JavaScript library v2.2.1
    22 // (c) Steven Sanderson - http://knockoutjs.com/
    23 // License: MIT (http://www.opensource.org/licenses/mit-license.php)
    24 
    25 (function(){
    26 var DEBUG=true;
    27 (function(window,document,navigator,jQuery,undefined){
    28 !function(factory) {
    29     // Support three module loading scenarios
    30     if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
    31         // [1] CommonJS/Node.js
    32         var target = module['exports'] || exports; // module.exports is for Node.js
    33         factory(target);
    34     } else if (typeof define === 'function' && define['amd']) {
    35         // [2] AMD anonymous module
    36         define(['exports'], factory);
    37     } else {
    38         // [3] No module loader (plain <script> tag) - put directly in global namespace
    39         factory(window['ko'] = {});
    40     }
    41 }(function(koExports){
    42 // Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
    43 // In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
    44 var ko = typeof koExports !== 'undefined' ? koExports : {};
    45 // Google Closure Compiler helpers (used only to make the minified file smaller)
    46 ko.exportSymbol = function(koPath, object) {
    47 	var tokens = koPath.split(".");
    48 
    49 	// In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
    50 	// At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
    51 	var target = ko;
    52 
    53 	for (var i = 0; i < tokens.length - 1; i++)
    54 		target = target[tokens[i]];
    55 	target[tokens[tokens.length - 1]] = object;
    56 };
    57 ko.exportProperty = function(owner, publicName, object) {
    58   owner[publicName] = object;
    59 };
    60 ko.version = "2.2.1";
    61 
    62 ko.exportSymbol('version', ko.version);
    63 ko.utils = new (function () {
    64     var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
    65 
    66     // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
    67     var knownEvents = {}, knownEventTypesByEventName = {};
    68     var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
    69     knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
    70     knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
    71     for (var eventType in knownEvents) {
    72         var knownEventsForType = knownEvents[eventType];
    73         if (knownEventsForType.length) {
    74             for (var i = 0, j = knownEventsForType.length; i < j; i++)
    75                 knownEventTypesByEventName[knownEventsForType[i]] = eventType;
    76         }
    77     }
    78     var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
    79 
    80     // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
    81     // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
    82     // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
    83     // If there is a future need to detect specific versions of IE10+, we will amend this.
    84     var ieVersion = (function() {
    85         var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
    86 
    87         // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
    88         while (
    89             div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
    90             iElems[0]
    91         );
    92         return version > 4 ? version : undefined;
    93     }());
    94     var isIe6 = ieVersion === 6,
    95         isIe7 = ieVersion === 7;
    96 
    97     function isClickOnCheckableElement(element, eventType) {
    98         if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
    99         if (eventType.toLowerCase() != "click") return false;
   100         var inputType = element.type;
   101         return (inputType == "checkbox") || (inputType == "radio");
   102     }
   103 
   104     return {
   105         fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
   106 
   107         arrayForEach: function (array, action) {
   108             for (var i = 0, j = array.length; i < j; i++)
   109                 action(array[i]);
   110         },
   111 
   112         arrayIndexOf: function (array, item) {
   113             if (typeof Array.prototype.indexOf == "function")
   114                 return Array.prototype.indexOf.call(array, item);
   115             for (var i = 0, j = array.length; i < j; i++)
   116                 if (array[i] === item)
   117                     return i;
   118             return -1;
   119         },
   120 
   121         arrayFirst: function (array, predicate, predicateOwner) {
   122             for (var i = 0, j = array.length; i < j; i++)
   123                 if (predicate.call(predicateOwner, array[i]))
   124                     return array[i];
   125             return null;
   126         },
   127 
   128         arrayRemoveItem: function (array, itemToRemove) {
   129             var index = ko.utils.arrayIndexOf(array, itemToRemove);
   130             if (index >= 0)
   131                 array.splice(index, 1);
   132         },
   133 
   134         arrayGetDistinctValues: function (array) {
   135             array = array || [];
   136             var result = [];
   137             for (var i = 0, j = array.length; i < j; i++) {
   138                 if (ko.utils.arrayIndexOf(result, array[i]) < 0)
   139                     result.push(array[i]);
   140             }
   141             return result;
   142         },
   143 
   144         arrayMap: function (array, mapping) {
   145             array = array || [];
   146             var result = [];
   147             for (var i = 0, j = array.length; i < j; i++)
   148                 result.push(mapping(array[i]));
   149             return result;
   150         },
   151 
   152         arrayFilter: function (array, predicate) {
   153             array = array || [];
   154             var result = [];
   155             for (var i = 0, j = array.length; i < j; i++)
   156                 if (predicate(array[i]))
   157                     result.push(array[i]);
   158             return result;
   159         },
   160 
   161         arrayPushAll: function (array, valuesToPush) {
   162             if (valuesToPush instanceof Array)
   163                 array.push.apply(array, valuesToPush);
   164             else
   165                 for (var i = 0, j = valuesToPush.length; i < j; i++)
   166                     array.push(valuesToPush[i]);
   167             return array;
   168         },
   169 
   170         extend: function (target, source) {
   171             if (source) {
   172                 for(var prop in source) {
   173                     if(source.hasOwnProperty(prop)) {
   174                         target[prop] = source[prop];
   175                     }
   176                 }
   177             }
   178             return target;
   179         },
   180 
   181         emptyDomNode: function (domNode) {
   182             while (domNode.firstChild) {
   183                 ko.removeNode(domNode.firstChild);
   184             }
   185         },
   186 
   187         moveCleanedNodesToContainerElement: function(nodes) {
   188             // Ensure it's a real array, as we're about to reparent the nodes and
   189             // we don't want the underlying collection to change while we're doing that.
   190             var nodesArray = ko.utils.makeArray(nodes);
   191 
   192             var container = document.createElement('div');
   193             for (var i = 0, j = nodesArray.length; i < j; i++) {
   194                 container.appendChild(ko.cleanNode(nodesArray[i]));
   195             }
   196             return container;
   197         },
   198 
   199         cloneNodes: function (nodesArray, shouldCleanNodes) {
   200             for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
   201                 var clonedNode = nodesArray[i].cloneNode(true);
   202                 newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
   203             }
   204             return newNodesArray;
   205         },
   206 
   207         setDomNodeChildren: function (domNode, childNodes) {
   208             ko.utils.emptyDomNode(domNode);
   209             if (childNodes) {
   210                 for (var i = 0, j = childNodes.length; i < j; i++)
   211                     domNode.appendChild(childNodes[i]);
   212             }
   213         },
   214 
   215         replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
   216             var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
   217             if (nodesToReplaceArray.length > 0) {
   218                 var insertionPoint = nodesToReplaceArray[0];
   219                 var parent = insertionPoint.parentNode;
   220                 for (var i = 0, j = newNodesArray.length; i < j; i++)
   221                     parent.insertBefore(newNodesArray[i], insertionPoint);
   222                 for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
   223                     ko.removeNode(nodesToReplaceArray[i]);
   224                 }
   225             }
   226         },
   227 
   228         setOptionNodeSelectionState: function (optionNode, isSelected) {
   229             // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
   230             if (ieVersion < 7)
   231                 optionNode.setAttribute("selected", isSelected);
   232             else
   233                 optionNode.selected = isSelected;
   234         },
   235 
   236         stringTrim: function (string) {
   237             return (string || "").replace(stringTrimRegex, "");
   238         },
   239 
   240         stringTokenize: function (string, delimiter) {
   241             var result = [];
   242             var tokens = (string || "").split(delimiter);
   243             for (var i = 0, j = tokens.length; i < j; i++) {
   244                 var trimmed = ko.utils.stringTrim(tokens[i]);
   245                 if (trimmed !== "")
   246                     result.push(trimmed);
   247             }
   248             return result;
   249         },
   250 
   251         stringStartsWith: function (string, startsWith) {
   252             string = string || "";
   253             if (startsWith.length > string.length)
   254                 return false;
   255             return string.substring(0, startsWith.length) === startsWith;
   256         },
   257 
   258         domNodeIsContainedBy: function (node, containedByNode) {
   259             if (containedByNode.compareDocumentPosition)
   260                 return (containedByNode.compareDocumentPosition(node) & 16) == 16;
   261             while (node != null) {
   262                 if (node == containedByNode)
   263                     return true;
   264                 node = node.parentNode;
   265             }
   266             return false;
   267         },
   268 
   269         domNodeIsAttachedToDocument: function (node) {
   270             return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
   271         },
   272 
   273         tagNameLower: function(element) {
   274             // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
   275             // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
   276             // we don't need to do the .toLowerCase() as it will always be lower case anyway.
   277             return element && element.tagName && element.tagName.toLowerCase();
   278         },
   279 
   280         registerEventHandler: function (element, eventType, handler) {
   281             var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
   282             if (!mustUseAttachEvent && typeof jQuery != "undefined") {
   283                 if (isClickOnCheckableElement(element, eventType)) {
   284                     // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
   285                     // it toggles the element checked state *after* the click event handlers run, whereas native
   286                     // click events toggle the checked state *before* the event handler.
   287                     // Fix this by intecepting the handler and applying the correct checkedness before it runs.
   288                     var originalHandler = handler;
   289                     handler = function(event, eventData) {
   290                         var jQuerySuppliedCheckedState = this.checked;
   291                         if (eventData)
   292                             this.checked = eventData.checkedStateBeforeEvent !== true;
   293                         originalHandler.call(this, event);
   294                         this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
   295                     };
   296                 }
   297                 jQuery(element)['bind'](eventType, handler);
   298             } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
   299                 element.addEventListener(eventType, handler, false);
   300             else if (typeof element.attachEvent != "undefined")
   301                 element.attachEvent("on" + eventType, function (event) {
   302                     handler.call(element, event);
   303                 });
   304             else
   305                 throw new Error("Browser doesn't support addEventListener or attachEvent");
   306         },
   307 
   308         triggerEvent: function (element, eventType) {
   309             if (!(element && element.nodeType))
   310                 throw new Error("element must be a DOM node when calling triggerEvent");
   311 
   312             if (typeof jQuery != "undefined") {
   313                 var eventData = [];
   314                 if (isClickOnCheckableElement(element, eventType)) {
   315                     // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
   316                     eventData.push({ checkedStateBeforeEvent: element.checked });
   317                 }
   318                 jQuery(element)['trigger'](eventType, eventData);
   319             } else if (typeof document.createEvent == "function") {
   320                 if (typeof element.dispatchEvent == "function") {
   321                     var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
   322                     var event = document.createEvent(eventCategory);
   323                     event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
   324                     element.dispatchEvent(event);
   325                 }
   326                 else
   327                     throw new Error("The supplied element doesn't support dispatchEvent");
   328             } else if (typeof element.fireEvent != "undefined") {
   329                 // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
   330                 // so to make it consistent, we'll do it manually here
   331                 if (isClickOnCheckableElement(element, eventType))
   332                     element.checked = element.checked !== true;
   333                 element.fireEvent("on" + eventType);
   334             }
   335             else
   336                 throw new Error("Browser doesn't support triggering events");
   337         },
   338 
   339         unwrapObservable: function (value) {
   340             return ko.isObservable(value) ? value() : value;
   341         },
   342 
   343         peekObservable: function (value) {
   344             return ko.isObservable(value) ? value.peek() : value;
   345         },
   346 
   347         toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
   348             if (classNames) {
   349                 var cssClassNameRegex = /[\w-]+/g,
   350                     currentClassNames = node.className.match(cssClassNameRegex) || [];
   351                 ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
   352                     var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
   353                     if (indexOfClass >= 0) {
   354                         if (!shouldHaveClass)
   355                             currentClassNames.splice(indexOfClass, 1);
   356                     } else {
   357                         if (shouldHaveClass)
   358                             currentClassNames.push(className);
   359                     }
   360                 });
   361                 node.className = currentClassNames.join(" ");
   362             }
   363         },
   364 
   365         setTextContent: function(element, textContent) {
   366             var value = ko.utils.unwrapObservable(textContent);
   367             if ((value === null) || (value === undefined))
   368                 value = "";
   369 
   370             if (element.nodeType === 3) {
   371                 element.data = value;
   372             } else {
   373                 // We need there to be exactly one child: a text node.
   374                 // If there are no children, more than one, or if it's not a text node,
   375                 // we'll clear everything and create a single text node.
   376                 var innerTextNode = ko.virtualElements.firstChild(element);
   377                 if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
   378                     ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
   379                 } else {
   380                     innerTextNode.data = value;
   381                 }
   382 
   383                 ko.utils.forceRefresh(element);
   384             }
   385         },
   386 
   387         setElementName: function(element, name) {
   388             element.name = name;
   389 
   390             // Workaround IE 6/7 issue
   391             // - https://github.com/SteveSanderson/knockout/issues/197
   392             // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
   393             if (ieVersion <= 7) {
   394                 try {
   395                     element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
   396                 }
   397                 catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
   398             }
   399         },
   400 
   401         forceRefresh: function(node) {
   402             // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
   403             if (ieVersion >= 9) {
   404                 // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
   405                 var elem = node.nodeType == 1 ? node : node.parentNode;
   406                 if (elem.style)
   407                     elem.style.zoom = elem.style.zoom;
   408             }
   409         },
   410 
   411         ensureSelectElementIsRenderedCorrectly: function(selectElement) {
   412             // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
   413             // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
   414             if (ieVersion >= 9) {
   415                 var originalWidth = selectElement.style.width;
   416                 selectElement.style.width = 0;
   417                 selectElement.style.width = originalWidth;
   418             }
   419         },
   420 
   421         range: function (min, max) {
   422             min = ko.utils.unwrapObservable(min);
   423             max = ko.utils.unwrapObservable(max);
   424             var result = [];
   425             for (var i = min; i <= max; i++)
   426                 result.push(i);
   427             return result;
   428         },
   429 
   430         makeArray: function(arrayLikeObject) {
   431             var result = [];
   432             for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
   433                 result.push(arrayLikeObject[i]);
   434             };
   435             return result;
   436         },
   437 
   438         isIe6 : isIe6,
   439         isIe7 : isIe7,
   440         ieVersion : ieVersion,
   441 
   442         getFormFields: function(form, fieldName) {
   443             var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
   444             var isMatchingField = (typeof fieldName == 'string')
   445                 ? function(field) { return field.name === fieldName }
   446                 : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
   447             var matches = [];
   448             for (var i = fields.length - 1; i >= 0; i--) {
   449                 if (isMatchingField(fields[i]))
   450                     matches.push(fields[i]);
   451             };
   452             return matches;
   453         },
   454 
   455         parseJson: function (jsonString) {
   456             if (typeof jsonString == "string") {
   457                 jsonString = ko.utils.stringTrim(jsonString);
   458                 if (jsonString) {
   459                     if (window.JSON && window.JSON.parse) // Use native parsing where available
   460                         return window.JSON.parse(jsonString);
   461                     return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
   462                 }
   463             }
   464             return null;
   465         },
   466 
   467         stringifyJson: function (data, replacer, space) {   // replacer and space are optional
   468             if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
   469                 throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
   470             return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
   471         },
   472 
   473         postJson: function (urlOrForm, data, options) {
   474             options = options || {};
   475             var params = options['params'] || {};
   476             var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
   477             var url = urlOrForm;
   478 
   479             // If we were given a form, use its 'action' URL and pick out any requested field values
   480             if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
   481                 var originalForm = urlOrForm;
   482                 url = originalForm.action;
   483                 for (var i = includeFields.length - 1; i >= 0; i--) {
   484                     var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
   485                     for (var j = fields.length - 1; j >= 0; j--)
   486                         params[fields[j].name] = fields[j].value;
   487                 }
   488             }
   489 
   490             data = ko.utils.unwrapObservable(data);
   491             var form = document.createElement("form");
   492             form.style.display = "none";
   493             form.action = url;
   494             form.method = "post";
   495             for (var key in data) {
   496                 var input = document.createElement("input");
   497                 input.name = key;
   498                 input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
   499                 form.appendChild(input);
   500             }
   501             for (var key in params) {
   502                 var input = document.createElement("input");
   503                 input.name = key;
   504                 input.value = params[key];
   505                 form.appendChild(input);
   506             }
   507             document.body.appendChild(form);
   508             options['submitter'] ? options['submitter'](form) : form.submit();
   509             setTimeout(function () { form.parentNode.removeChild(form); }, 0);
   510         }
   511     }
   512 })();
   513 
   514 ko.exportSymbol('utils', ko.utils);
   515 ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
   516 ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
   517 ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
   518 ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
   519 ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
   520 ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
   521 ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
   522 ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
   523 ko.exportSymbol('utils.extend', ko.utils.extend);
   524 ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
   525 ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
   526 ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
   527 ko.exportSymbol('utils.postJson', ko.utils.postJson);
   528 ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
   529 ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
   530 ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
   531 ko.exportSymbol('utils.range', ko.utils.range);
   532 ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
   533 ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
   534 ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
   535 
   536 if (!Function.prototype['bind']) {
   537     // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
   538     // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
   539     Function.prototype['bind'] = function (object) {
   540         var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
   541         return function () {
   542             return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
   543         };
   544     };
   545 }
   546 
   547 ko.utils.domData = new (function () {
   548     var uniqueId = 0;
   549     var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
   550     var dataStore = {};
   551     return {
   552         get: function (node, key) {
   553             var allDataForNode = ko.utils.domData.getAll(node, false);
   554             return allDataForNode === undefined ? undefined : allDataForNode[key];
   555         },
   556         set: function (node, key, value) {
   557             if (value === undefined) {
   558                 // Make sure we don't actually create a new domData key if we are actually deleting a value
   559                 if (ko.utils.domData.getAll(node, false) === undefined)
   560                     return;
   561             }
   562             var allDataForNode = ko.utils.domData.getAll(node, true);
   563             allDataForNode[key] = value;
   564         },
   565         getAll: function (node, createIfNotFound) {
   566             var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
   567             var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
   568             if (!hasExistingDataStore) {
   569                 if (!createIfNotFound)
   570                     return undefined;
   571                 dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
   572                 dataStore[dataStoreKey] = {};
   573             }
   574             return dataStore[dataStoreKey];
   575         },
   576         clear: function (node) {
   577             var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
   578             if (dataStoreKey) {
   579                 delete dataStore[dataStoreKey];
   580                 node[dataStoreKeyExpandoPropertyName] = null;
   581                 return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
   582             }
   583             return false;
   584         }
   585     }
   586 })();
   587 
   588 ko.exportSymbol('utils.domData', ko.utils.domData);
   589 ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
   590 
   591 ko.utils.domNodeDisposal = new (function () {
   592     var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
   593     var cleanableNodeTypes = { 1: true, 8: true, 9: true };       // Element, Comment, Document
   594     var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
   595 
   596     function getDisposeCallbacksCollection(node, createIfNotFound) {
   597         var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
   598         if ((allDisposeCallbacks === undefined) && createIfNotFound) {
   599             allDisposeCallbacks = [];
   600             ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
   601         }
   602         return allDisposeCallbacks;
   603     }
   604     function destroyCallbacksCollection(node) {
   605         ko.utils.domData.set(node, domDataKey, undefined);
   606     }
   607 
   608     function cleanSingleNode(node) {
   609         // Run all the dispose callbacks
   610         var callbacks = getDisposeCallbacksCollection(node, false);
   611         if (callbacks) {
   612             callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
   613             for (var i = 0; i < callbacks.length; i++)
   614                 callbacks[i](node);
   615         }
   616 
   617         // Also erase the DOM data
   618         ko.utils.domData.clear(node);
   619 
   620         // Special support for jQuery here because it's so commonly used.
   621         // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
   622         // so notify it to tear down any resources associated with the node & descendants here.
   623         if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
   624             jQuery['cleanData']([node]);
   625 
   626         // Also clear any immediate-child comment nodes, as these wouldn't have been found by
   627         // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
   628         if (cleanableNodeTypesWithDescendants[node.nodeType])
   629             cleanImmediateCommentTypeChildren(node);
   630     }
   631 
   632     function cleanImmediateCommentTypeChildren(nodeWithChildren) {
   633         var child, nextChild = nodeWithChildren.firstChild;
   634         while (child = nextChild) {
   635             nextChild = child.nextSibling;
   636             if (child.nodeType === 8)
   637                 cleanSingleNode(child);
   638         }
   639     }
   640 
   641     return {
   642         addDisposeCallback : function(node, callback) {
   643             if (typeof callback != "function")
   644                 throw new Error("Callback must be a function");
   645             getDisposeCallbacksCollection(node, true).push(callback);
   646         },
   647 
   648         removeDisposeCallback : function(node, callback) {
   649             var callbacksCollection = getDisposeCallbacksCollection(node, false);
   650             if (callbacksCollection) {
   651                 ko.utils.arrayRemoveItem(callbacksCollection, callback);
   652                 if (callbacksCollection.length == 0)
   653                     destroyCallbacksCollection(node);
   654             }
   655         },
   656 
   657         cleanNode : function(node) {
   658             // First clean this node, where applicable
   659             if (cleanableNodeTypes[node.nodeType]) {
   660                 cleanSingleNode(node);
   661 
   662                 // ... then its descendants, where applicable
   663                 if (cleanableNodeTypesWithDescendants[node.nodeType]) {
   664                     // Clone the descendants list in case it changes during iteration
   665                     var descendants = [];
   666                     ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
   667                     for (var i = 0, j = descendants.length; i < j; i++)
   668                         cleanSingleNode(descendants[i]);
   669                 }
   670             }
   671             return node;
   672         },
   673 
   674         removeNode : function(node) {
   675             ko.cleanNode(node);
   676             if (node.parentNode)
   677                 node.parentNode.removeChild(node);
   678         }
   679     }
   680 })();
   681 ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
   682 ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
   683 ko.exportSymbol('cleanNode', ko.cleanNode);
   684 ko.exportSymbol('removeNode', ko.removeNode);
   685 ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
   686 ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
   687 ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
   688 (function () {
   689     var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
   690 
   691     function simpleHtmlParse(html) {
   692         // Based on jQuery's "clean" function, but only accounting for table-related elements.
   693         // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
   694 
   695         // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
   696         // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
   697         // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
   698         // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
   699 
   700         // Trim whitespace, otherwise indexOf won't work as expected
   701         var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
   702 
   703         // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
   704         var wrap = tags.match(/^<(thead|tbody|tfoot)/)              && [1, "<table>", "</table>"] ||
   705                    !tags.indexOf("<tr")                             && [2, "<table><tbody>", "</tbody></table>"] ||
   706                    (!tags.indexOf("<td") || !tags.indexOf("<th"))   && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
   707                    /* anything else */                                 [0, "", ""];
   708 
   709         // Go to html and back, then peel off extra wrappers
   710         // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
   711         var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
   712         if (typeof window['innerShiv'] == "function") {
   713             div.appendChild(window['innerShiv'](markup));
   714         } else {
   715             div.innerHTML = markup;
   716         }
   717 
   718         // Move to the right depth
   719         while (wrap[0]--)
   720             div = div.lastChild;
   721 
   722         return ko.utils.makeArray(div.lastChild.childNodes);
   723     }
   724 
   725     function jQueryHtmlParse(html) {
   726         // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
   727         if (jQuery['parseHTML']) {
   728             return jQuery['parseHTML'](html);
   729         } else {
   730             // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
   731             var elems = jQuery['clean']([html]);
   732 
   733             // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
   734             // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
   735             // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
   736             if (elems && elems[0]) {
   737                 // Find the top-most parent element that's a direct child of a document fragment
   738                 var elem = elems[0];
   739                 while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
   740                     elem = elem.parentNode;
   741                 // ... then detach it
   742                 if (elem.parentNode)
   743                     elem.parentNode.removeChild(elem);
   744             }
   745 
   746             return elems;
   747         }
   748     }
   749 
   750     ko.utils.parseHtmlFragment = function(html) {
   751         return typeof jQuery != 'undefined' ? jQueryHtmlParse(html)   // As below, benefit from jQuery's optimisations where possible
   752                                             : simpleHtmlParse(html);  // ... otherwise, this simple logic will do in most common cases.
   753     };
   754 
   755     ko.utils.setHtml = function(node, html) {
   756         ko.utils.emptyDomNode(node);
   757 
   758         // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
   759         html = ko.utils.unwrapObservable(html);
   760 
   761         if ((html !== null) && (html !== undefined)) {
   762             if (typeof html != 'string')
   763                 html = html.toString();
   764 
   765             // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
   766             // for example <tr> elements which are not normally allowed to exist on their own.
   767             // If you've referenced jQuery we'll use that rather than duplicating its code.
   768             if (typeof jQuery != 'undefined') {
   769                 jQuery(node)['html'](html);
   770             } else {
   771                 // ... otherwise, use KO's own parsing logic.
   772                 var parsedNodes = ko.utils.parseHtmlFragment(html);
   773                 for (var i = 0; i < parsedNodes.length; i++)
   774                     node.appendChild(parsedNodes[i]);
   775             }
   776         }
   777     };
   778 })();
   779 
   780 ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
   781 ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
   782 
   783 ko.memoization = (function () {
   784     var memos = {};
   785 
   786     function randomMax8HexChars() {
   787         return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
   788     }
   789     function generateRandomId() {
   790         return randomMax8HexChars() + randomMax8HexChars();
   791     }
   792     function findMemoNodes(rootNode, appendToArray) {
   793         if (!rootNode)
   794             return;
   795         if (rootNode.nodeType == 8) {
   796             var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
   797             if (memoId != null)
   798                 appendToArray.push({ domNode: rootNode, memoId: memoId });
   799         } else if (rootNode.nodeType == 1) {
   800             for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
   801                 findMemoNodes(childNodes[i], appendToArray);
   802         }
   803     }
   804 
   805     return {
   806         memoize: function (callback) {
   807             if (typeof callback != "function")
   808                 throw new Error("You can only pass a function to ko.memoization.memoize()");
   809             var memoId = generateRandomId();
   810             memos[memoId] = callback;
   811             return "<!--[ko_memo:" + memoId + "]-->";
   812         },
   813 
   814         unmemoize: function (memoId, callbackParams) {
   815             var callback = memos[memoId];
   816             if (callback === undefined)
   817                 throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
   818             try {
   819                 callback.apply(null, callbackParams || []);
   820                 return true;
   821             }
   822             finally { delete memos[memoId]; }
   823         },
   824 
   825         unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
   826             var memos = [];
   827             findMemoNodes(domNode, memos);
   828             for (var i = 0, j = memos.length; i < j; i++) {
   829                 var node = memos[i].domNode;
   830                 var combinedParams = [node];
   831                 if (extraCallbackParamsArray)
   832                     ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
   833                 ko.memoization.unmemoize(memos[i].memoId, combinedParams);
   834                 node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
   835                 if (node.parentNode)
   836                     node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
   837             }
   838         },
   839 
   840         parseMemoText: function (memoText) {
   841             var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
   842             return match ? match[1] : null;
   843         }
   844     };
   845 })();
   846 
   847 ko.exportSymbol('memoization', ko.memoization);
   848 ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
   849 ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
   850 ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
   851 ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
   852 ko.extenders = {
   853     'throttle': function(target, timeout) {
   854         // Throttling means two things:
   855 
   856         // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
   857         //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
   858         target['throttleEvaluation'] = timeout;
   859 
   860         // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
   861         //     so the target cannot change value synchronously or faster than a certain rate
   862         var writeTimeoutInstance = null;
   863         return ko.dependentObservable({
   864             'read': target,
   865             'write': function(value) {
   866                 clearTimeout(writeTimeoutInstance);
   867                 writeTimeoutInstance = setTimeout(function() {
   868                     target(value);
   869                 }, timeout);
   870             }
   871         });
   872     },
   873 
   874     'notify': function(target, notifyWhen) {
   875         target["equalityComparer"] = notifyWhen == "always"
   876             ? function() { return false } // Treat all values as not equal
   877             : ko.observable["fn"]["equalityComparer"];
   878         return target;
   879     }
   880 };
   881 
   882 function applyExtenders(requestedExtenders) {
   883     var target = this;
   884     if (requestedExtenders) {
   885         for (var key in requestedExtenders) {
   886             var extenderHandler = ko.extenders[key];
   887             if (typeof extenderHandler == 'function') {
   888                 target = extenderHandler(target, requestedExtenders[key]);
   889             }
   890         }
   891     }
   892     return target;
   893 }
   894 
   895 ko.exportSymbol('extenders', ko.extenders);
   896 
   897 ko.subscription = function (target, callback, disposeCallback) {
   898     this.target = target;
   899     this.callback = callback;
   900     this.disposeCallback = disposeCallback;
   901     ko.exportProperty(this, 'dispose', this.dispose);
   902 };
   903 ko.subscription.prototype.dispose = function () {
   904     this.isDisposed = true;
   905     this.disposeCallback();
   906 };
   907 
   908 ko.subscribable = function () {
   909     this._subscriptions = {};
   910 
   911     ko.utils.extend(this, ko.subscribable['fn']);
   912     ko.exportProperty(this, 'subscribe', this.subscribe);
   913     ko.exportProperty(this, 'extend', this.extend);
   914     ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
   915 }
   916 
   917 var defaultEvent = "change";
   918 
   919 ko.subscribable['fn'] = {
   920     subscribe: function (callback, callbackTarget, event) {
   921         event = event || defaultEvent;
   922         var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
   923 
   924         var subscription = new ko.subscription(this, boundCallback, function () {
   925             ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
   926         }.bind(this));
   927 
   928         if (!this._subscriptions[event])
   929             this._subscriptions[event] = [];
   930         this._subscriptions[event].push(subscription);
   931         return subscription;
   932     },
   933 
   934     "notifySubscribers": function (valueToNotify, event) {
   935         event = event || defaultEvent;
   936         if (this._subscriptions[event]) {
   937             ko.dependencyDetection.ignore(function() {
   938                 ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
   939                     // In case a subscription was disposed during the arrayForEach cycle, check
   940                     // for isDisposed on each subscription before invoking its callback
   941                     if (subscription && (subscription.isDisposed !== true))
   942                         subscription.callback(valueToNotify);
   943                 });
   944             }, this);
   945         }
   946     },
   947 
   948     getSubscriptionsCount: function () {
   949         var total = 0;
   950         for (var eventName in this._subscriptions) {
   951             if (this._subscriptions.hasOwnProperty(eventName))
   952                 total += this._subscriptions[eventName].length;
   953         }
   954         return total;
   955     },
   956 
   957     extend: applyExtenders
   958 };
   959 
   960 
   961 ko.isSubscribable = function (instance) {
   962     return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
   963 };
   964 
   965 ko.exportSymbol('subscribable', ko.subscribable);
   966 ko.exportSymbol('isSubscribable', ko.isSubscribable);
   967 
   968 ko.dependencyDetection = (function () {
   969     var _frames = [];
   970 
   971     return {
   972         begin: function (callback) {
   973             _frames.push({ callback: callback, distinctDependencies:[] });
   974         },
   975 
   976         end: function () {
   977             _frames.pop();
   978         },
   979 
   980         registerDependency: function (subscribable) {
   981             if (!ko.isSubscribable(subscribable))
   982                 throw new Error("Only subscribable things can act as dependencies");
   983             if (_frames.length > 0) {
   984                 var topFrame = _frames[_frames.length - 1];
   985                 if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
   986                     return;
   987                 topFrame.distinctDependencies.push(subscribable);
   988                 topFrame.callback(subscribable);
   989             }
   990         },
   991 
   992         ignore: function(callback, callbackTarget, callbackArgs) {
   993             try {
   994                 _frames.push(null);
   995                 return callback.apply(callbackTarget, callbackArgs || []);
   996             } finally {
   997                 _frames.pop();
   998             }
   999         }
  1000     };
  1001 })();
  1002 var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
  1003 
  1004 ko.observable = function (initialValue) {
  1005     var _latestValue = initialValue;
  1006 
  1007     function observable() {
  1008         if (arguments.length > 0) {
  1009             // Write
  1010 
  1011             // Ignore writes if the value hasn't changed
  1012             if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
  1013                 observable.valueWillMutate();
  1014                 _latestValue = arguments[0];
  1015                 if (DEBUG) observable._latestValue = _latestValue;
  1016                 observable.valueHasMutated();
  1017             }
  1018             return this; // Permits chained assignments
  1019         }
  1020         else {
  1021             // Read
  1022             ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
  1023             return _latestValue;
  1024         }
  1025     }
  1026     if (DEBUG) observable._latestValue = _latestValue;
  1027     ko.subscribable.call(observable);
  1028     observable.peek = function() { return _latestValue };
  1029     observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
  1030     observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
  1031     ko.utils.extend(observable, ko.observable['fn']);
  1032 
  1033     ko.exportProperty(observable, 'peek', observable.peek);
  1034     ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
  1035     ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
  1036 
  1037     return observable;
  1038 }
  1039 
  1040 ko.observable['fn'] = {
  1041     "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
  1042         var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
  1043         return oldValueIsPrimitive ? (a === b) : false;
  1044     }
  1045 };
  1046 
  1047 var protoProperty = ko.observable.protoProperty = "__ko_proto__";
  1048 ko.observable['fn'][protoProperty] = ko.observable;
  1049 
  1050 ko.hasPrototype = function(instance, prototype) {
  1051     if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
  1052     if (instance[protoProperty] === prototype) return true;
  1053     return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
  1054 };
  1055 
  1056 ko.isObservable = function (instance) {
  1057     return ko.hasPrototype(instance, ko.observable);
  1058 }
  1059 ko.isWriteableObservable = function (instance) {
  1060     // Observable
  1061     if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
  1062         return true;
  1063     // Writeable dependent observable
  1064     if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
  1065         return true;
  1066     // Anything else
  1067     return false;
  1068 }
  1069 
  1070 
  1071 ko.exportSymbol('observable', ko.observable);
  1072 ko.exportSymbol('isObservable', ko.isObservable);
  1073 ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
  1074 ko.observableArray = function (initialValues) {
  1075     if (arguments.length == 0) {
  1076         // Zero-parameter constructor initializes to empty array
  1077         initialValues = [];
  1078     }
  1079     if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
  1080         throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
  1081 
  1082     var result = ko.observable(initialValues);
  1083     ko.utils.extend(result, ko.observableArray['fn']);
  1084     return result;
  1085 }
  1086 
  1087 ko.observableArray['fn'] = {
  1088     'remove': function (valueOrPredicate) {
  1089         var underlyingArray = this.peek();
  1090         var removedValues = [];
  1091         var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
  1092         for (var i = 0; i < underlyingArray.length; i++) {
  1093             var value = underlyingArray[i];
  1094             if (predicate(value)) {
  1095                 if (removedValues.length === 0) {
  1096                     this.valueWillMutate();
  1097                 }
  1098                 removedValues.push(value);
  1099                 underlyingArray.splice(i, 1);
  1100                 i--;
  1101             }
  1102         }
  1103         if (removedValues.length) {
  1104             this.valueHasMutated();
  1105         }
  1106         return removedValues;
  1107     },
  1108 
  1109     'removeAll': function (arrayOfValues) {
  1110         // If you passed zero args, we remove everything
  1111         if (arrayOfValues === undefined) {
  1112             var underlyingArray = this.peek();
  1113             var allValues = underlyingArray.slice(0);
  1114             this.valueWillMutate();
  1115             underlyingArray.splice(0, underlyingArray.length);
  1116             this.valueHasMutated();
  1117             return allValues;
  1118         }
  1119         // If you passed an arg, we interpret it as an array of entries to remove
  1120         if (!arrayOfValues)
  1121             return [];
  1122         return this['remove'](function (value) {
  1123             return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
  1124         });
  1125     },
  1126 
  1127     'destroy': function (valueOrPredicate) {
  1128         var underlyingArray = this.peek();
  1129         var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
  1130         this.valueWillMutate();
  1131         for (var i = underlyingArray.length - 1; i >= 0; i--) {
  1132             var value = underlyingArray[i];
  1133             if (predicate(value))
  1134                 underlyingArray[i]["_destroy"] = true;
  1135         }
  1136         this.valueHasMutated();
  1137     },
  1138 
  1139     'destroyAll': function (arrayOfValues) {
  1140         // If you passed zero args, we destroy everything
  1141         if (arrayOfValues === undefined)
  1142             return this['destroy'](function() { return true });
  1143 
  1144         // If you passed an arg, we interpret it as an array of entries to destroy
  1145         if (!arrayOfValues)
  1146             return [];
  1147         return this['destroy'](function (value) {
  1148             return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
  1149         });
  1150     },
  1151 
  1152     'indexOf': function (item) {
  1153         var underlyingArray = this();
  1154         return ko.utils.arrayIndexOf(underlyingArray, item);
  1155     },
  1156 
  1157     'replace': function(oldItem, newItem) {
  1158         var index = this['indexOf'](oldItem);
  1159         if (index >= 0) {
  1160             this.valueWillMutate();
  1161             this.peek()[index] = newItem;
  1162             this.valueHasMutated();
  1163         }
  1164     }
  1165 }
  1166 
  1167 // Populate ko.observableArray.fn with read/write functions from native arrays
  1168 // Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
  1169 // because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
  1170 ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
  1171     ko.observableArray['fn'][methodName] = function () {
  1172         // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
  1173         // (for consistency with mutating regular observables)
  1174         var underlyingArray = this.peek();
  1175         this.valueWillMutate();
  1176         var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
  1177         this.valueHasMutated();
  1178         return methodCallResult;
  1179     };
  1180 });
  1181 
  1182 // Populate ko.observableArray.fn with read-only functions from native arrays
  1183 ko.utils.arrayForEach(["slice"], function (methodName) {
  1184     ko.observableArray['fn'][methodName] = function () {
  1185         var underlyingArray = this();
  1186         return underlyingArray[methodName].apply(underlyingArray, arguments);
  1187     };
  1188 });
  1189 
  1190 ko.exportSymbol('observableArray', ko.observableArray);
  1191 ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
  1192     var _latestValue,
  1193         _hasBeenEvaluated = false,
  1194         _isBeingEvaluated = false,
  1195         readFunction = evaluatorFunctionOrOptions;
  1196 
  1197     if (readFunction && typeof readFunction == "object") {
  1198         // Single-parameter syntax - everything is on this "options" param
  1199         options = readFunction;
  1200         readFunction = options["read"];
  1201     } else {
  1202         // Multi-parameter syntax - construct the options according to the params passed
  1203         options = options || {};
  1204         if (!readFunction)
  1205             readFunction = options["read"];
  1206     }
  1207     if (typeof readFunction != "function")
  1208         throw new Error("Pass a function that returns the value of the ko.computed");
  1209 
  1210     function addSubscriptionToDependency(subscribable) {
  1211         _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
  1212     }
  1213 
  1214     function disposeAllSubscriptionsToDependencies() {
  1215         ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
  1216             subscription.dispose();
  1217         });
  1218         _subscriptionsToDependencies = [];
  1219     }
  1220 
  1221     function evaluatePossiblyAsync() {
  1222         var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
  1223         if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
  1224             clearTimeout(evaluationTimeoutInstance);
  1225             evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
  1226         } else
  1227             evaluateImmediate();
  1228     }
  1229 
  1230     function evaluateImmediate() {
  1231         if (_isBeingEvaluated) {
  1232             // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
  1233             // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
  1234             // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
  1235             // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
  1236             return;
  1237         }
  1238 
  1239         // Don't dispose on first evaluation, because the "disposeWhen" callback might
  1240         // e.g., dispose when the associated DOM element isn't in the doc, and it's not
  1241         // going to be in the doc until *after* the first evaluation
  1242         if (_hasBeenEvaluated && disposeWhen()) {
  1243             dispose();
  1244             return;
  1245         }
  1246 
  1247         _isBeingEvaluated = true;
  1248         try {
  1249             // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
  1250             // Then, during evaluation, we cross off any that are in fact still being used.
  1251             var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
  1252 
  1253             ko.dependencyDetection.begin(function(subscribable) {
  1254                 var inOld;
  1255                 if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
  1256                     disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
  1257                 else
  1258                     addSubscriptionToDependency(subscribable); // Brand new subscription - add it
  1259             });
  1260 
  1261             var newValue = readFunction.call(evaluatorFunctionTarget);
  1262 
  1263             // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
  1264             for (var i = disposalCandidates.length - 1; i >= 0; i--) {
  1265                 if (disposalCandidates[i])
  1266                     _subscriptionsToDependencies.splice(i, 1)[0].dispose();
  1267             }
  1268             _hasBeenEvaluated = true;
  1269 
  1270             dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
  1271             _latestValue = newValue;
  1272             if (DEBUG) dependentObservable._latestValue = _latestValue;
  1273         } finally {
  1274             ko.dependencyDetection.end();
  1275         }
  1276 
  1277         dependentObservable["notifySubscribers"](_latestValue);
  1278         _isBeingEvaluated = false;
  1279         if (!_subscriptionsToDependencies.length)
  1280             dispose();
  1281     }
  1282 
  1283     function dependentObservable() {
  1284         if (arguments.length > 0) {
  1285             if (typeof writeFunction === "function") {
  1286                 // Writing a value
  1287                 writeFunction.apply(evaluatorFunctionTarget, arguments);
  1288             } else {
  1289                 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.");
  1290             }
  1291             return this; // Permits chained assignments
  1292         } else {
  1293             // Reading the value
  1294             if (!_hasBeenEvaluated)
  1295                 evaluateImmediate();
  1296             ko.dependencyDetection.registerDependency(dependentObservable);
  1297             return _latestValue;
  1298         }
  1299     }
  1300 
  1301     function peek() {
  1302         if (!_hasBeenEvaluated)
  1303             evaluateImmediate();
  1304         return _latestValue;
  1305     }
  1306 
  1307     function isActive() {
  1308         return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
  1309     }
  1310 
  1311     // By here, "options" is always non-null
  1312     var writeFunction = options["write"],
  1313         disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
  1314         disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
  1315         dispose = disposeAllSubscriptionsToDependencies,
  1316         _subscriptionsToDependencies = [],
  1317         evaluationTimeoutInstance = null;
  1318 
  1319     if (!evaluatorFunctionTarget)
  1320         evaluatorFunctionTarget = options["owner"];
  1321 
  1322     dependentObservable.peek = peek;
  1323     dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
  1324     dependentObservable.hasWriteFunction = typeof options["write"] === "function";
  1325     dependentObservable.dispose = function () { dispose(); };
  1326     dependentObservable.isActive = isActive;
  1327     dependentObservable.valueHasMutated = function() {
  1328         _hasBeenEvaluated = false;
  1329         evaluateImmediate();
  1330     };
  1331 
  1332     ko.subscribable.call(dependentObservable);
  1333     ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
  1334 
  1335     ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
  1336     ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
  1337     ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
  1338     ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
  1339 
  1340     // Evaluate, unless deferEvaluation is true
  1341     if (options['deferEvaluation'] !== true)
  1342         evaluateImmediate();
  1343 
  1344     // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
  1345     // But skip if isActive is false (there will never be any dependencies to dispose).
  1346     // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
  1347     // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
  1348     if (disposeWhenNodeIsRemoved && isActive()) {
  1349         dispose = function() {
  1350             ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
  1351             disposeAllSubscriptionsToDependencies();
  1352         };
  1353         ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
  1354         var existingDisposeWhenFunction = disposeWhen;
  1355         disposeWhen = function () {
  1356             return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
  1357         }
  1358     }
  1359 
  1360     return dependentObservable;
  1361 };
  1362 
  1363 ko.isComputed = function(instance) {
  1364     return ko.hasPrototype(instance, ko.dependentObservable);
  1365 };
  1366 
  1367 var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
  1368 ko.dependentObservable[protoProp] = ko.observable;
  1369 
  1370 ko.dependentObservable['fn'] = {};
  1371 ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
  1372 
  1373 ko.exportSymbol('dependentObservable', ko.dependentObservable);
  1374 ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
  1375 ko.exportSymbol('isComputed', ko.isComputed);
  1376 
  1377 (function() {
  1378     var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
  1379 
  1380     ko.toJS = function(rootObject) {
  1381         if (arguments.length == 0)
  1382             throw new Error("When calling ko.toJS, pass the object you want to convert.");
  1383 
  1384         // We just unwrap everything at every level in the object graph
  1385         return mapJsObjectGraph(rootObject, function(valueToMap) {
  1386             // Loop because an observable's value might in turn be another observable wrapper
  1387             for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
  1388                 valueToMap = valueToMap();
  1389             return valueToMap;
  1390         });
  1391     };
  1392 
  1393     ko.toJSON = function(rootObject, replacer, space) {     // replacer and space are optional
  1394         var plainJavaScriptObject = ko.toJS(rootObject);
  1395         return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
  1396     };
  1397 
  1398     function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
  1399         visitedObjects = visitedObjects || new objectLookup();
  1400 
  1401         rootObject = mapInputCallback(rootObject);
  1402         var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
  1403         if (!canHaveProperties)
  1404             return rootObject;
  1405 
  1406         var outputProperties = rootObject instanceof Array ? [] : {};
  1407         visitedObjects.save(rootObject, outputProperties);
  1408 
  1409         visitPropertiesOrArrayEntries(rootObject, function(indexer) {
  1410             var propertyValue = mapInputCallback(rootObject[indexer]);
  1411 
  1412             switch (typeof propertyValue) {
  1413                 case "boolean":
  1414                 case "number":
  1415                 case "string":
  1416                 case "function":
  1417                     outputProperties[indexer] = propertyValue;
  1418                     break;
  1419                 case "object":
  1420                 case "undefined":
  1421                     var previouslyMappedValue = visitedObjects.get(propertyValue);
  1422                     outputProperties[indexer] = (previouslyMappedValue !== undefined)
  1423                         ? previouslyMappedValue
  1424                         : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
  1425                     break;
  1426             }
  1427         });
  1428 
  1429         return outputProperties;
  1430     }
  1431 
  1432     function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
  1433         if (rootObject instanceof Array) {
  1434             for (var i = 0; i < rootObject.length; i++)
  1435                 visitorCallback(i);
  1436 
  1437             // For arrays, also respect toJSON property for custom mappings (fixes #278)
  1438             if (typeof rootObject['toJSON'] == 'function')
  1439                 visitorCallback('toJSON');
  1440         } else {
  1441             for (var propertyName in rootObject)
  1442                 visitorCallback(propertyName);
  1443         }
  1444     };
  1445 
  1446     function objectLookup() {
  1447         var keys = [];
  1448         var values = [];
  1449         this.save = function(key, value) {
  1450             var existingIndex = ko.utils.arrayIndexOf(keys, key);
  1451             if (existingIndex >= 0)
  1452                 values[existingIndex] = value;
  1453             else {
  1454                 keys.push(key);
  1455                 values.push(value);
  1456             }
  1457         };
  1458         this.get = function(key) {
  1459             var existingIndex = ko.utils.arrayIndexOf(keys, key);
  1460             return (existingIndex >= 0) ? values[existingIndex] : undefined;
  1461         };
  1462     };
  1463 })();
  1464 
  1465 ko.exportSymbol('toJS', ko.toJS);
  1466 ko.exportSymbol('toJSON', ko.toJSON);
  1467 (function () {
  1468     var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
  1469 
  1470     // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
  1471     // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
  1472     // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
  1473     ko.selectExtensions = {
  1474         readValue : function(element) {
  1475             switch (ko.utils.tagNameLower(element)) {
  1476                 case 'option':
  1477                     if (element[hasDomDataExpandoProperty] === true)
  1478                         return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
  1479                     return ko.utils.ieVersion <= 7
  1480                         ? (element.getAttributeNode('value').specified ? element.value : element.text)
  1481                         : element.value;
  1482                 case 'select':
  1483                     return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
  1484                 default:
  1485                     return element.value;
  1486             }
  1487         },
  1488 
  1489         writeValue: function(element, value) {
  1490             switch (ko.utils.tagNameLower(element)) {
  1491                 case 'option':
  1492                     switch(typeof value) {
  1493                         case "string":
  1494                             ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
  1495                             if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
  1496                                 delete element[hasDomDataExpandoProperty];
  1497                             }
  1498                             element.value = value;
  1499                             break;
  1500                         default:
  1501                             // Store arbitrary object using DomData
  1502                             ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
  1503                             element[hasDomDataExpandoProperty] = true;
  1504 
  1505                             // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
  1506                             element.value = typeof value === "number" ? value : "";
  1507                             break;
  1508                     }
  1509                     break;
  1510                 case 'select':
  1511                     for (var i = element.options.length - 1; i >= 0; i--) {
  1512                         if (ko.selectExtensions.readValue(element.options[i]) == value) {
  1513                             element.selectedIndex = i;
  1514                             break;
  1515                         }
  1516                     }
  1517                     break;
  1518                 default:
  1519                     if ((value === null) || (value === undefined))
  1520                         value = "";
  1521                     element.value = value;
  1522                     break;
  1523             }
  1524         }
  1525     };
  1526 })();
  1527 
  1528 ko.exportSymbol('selectExtensions', ko.selectExtensions);
  1529 ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
  1530 ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
  1531 ko.expressionRewriting = (function () {
  1532     var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
  1533     var javaScriptReservedWords = ["true", "false"];
  1534 
  1535     // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
  1536     // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
  1537     var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
  1538 
  1539     function restoreTokens(string, tokens) {
  1540         var prevValue = null;
  1541         while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
  1542             prevValue = string;
  1543             string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
  1544                 return tokens[tokenIndex];
  1545             });
  1546         }
  1547         return string;
  1548     }
  1549 
  1550     function getWriteableValue(expression) {
  1551         if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
  1552             return false;
  1553         var match = expression.match(javaScriptAssignmentTarget);
  1554         return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
  1555     }
  1556 
  1557     function ensureQuoted(key) {
  1558         var trimmedKey = ko.utils.stringTrim(key);
  1559         switch (trimmedKey.length && trimmedKey.charAt(0)) {
  1560             case "'":
  1561             case '"':
  1562                 return key;
  1563             default:
  1564                 return "'" + trimmedKey + "'";
  1565         }
  1566     }
  1567 
  1568     return {
  1569         bindingRewriteValidators: [],
  1570 
  1571         parseObjectLiteral: function(objectLiteralString) {
  1572             // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
  1573             // that is sufficient just to split an object literal string into a set of top-level key-value pairs
  1574 
  1575             var str = ko.utils.stringTrim(objectLiteralString);
  1576             if (str.length < 3)
  1577                 return [];
  1578             if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
  1579                 str = str.substring(1, str.length - 1);
  1580 
  1581             // Pull out any string literals and regex literals
  1582             var tokens = [];
  1583             var tokenStart = null, tokenEndChar;
  1584             for (var position = 0; position < str.length; position++) {
  1585                 var c = str.charAt(position);
  1586                 if (tokenStart === null) {
  1587                     switch (c) {
  1588                         case '"':
  1589                         case "'":
  1590                         case "/":
  1591                             tokenStart = position;
  1592                             tokenEndChar = c;
  1593                             break;
  1594                     }
  1595                 } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
  1596                     var token = str.substring(tokenStart, position + 1);
  1597                     tokens.push(token);
  1598                     var replacement = "@ko_token_" + (tokens.length - 1) + "@";
  1599                     str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
  1600                     position -= (token.length - replacement.length);
  1601                     tokenStart = null;
  1602                 }
  1603             }
  1604 
  1605             // Next pull out balanced paren, brace, and bracket blocks
  1606             tokenStart = null;
  1607             tokenEndChar = null;
  1608             var tokenDepth = 0, tokenStartChar = null;
  1609             for (var position = 0; position < str.length; position++) {
  1610                 var c = str.charAt(position);
  1611                 if (tokenStart === null) {
  1612                     switch (c) {
  1613                         case "{": tokenStart = position; tokenStartChar = c;
  1614                                   tokenEndChar = "}";
  1615                                   break;
  1616                         case "(": tokenStart = position; tokenStartChar = c;
  1617                                   tokenEndChar = ")";
  1618                                   break;
  1619                         case "[": tokenStart = position; tokenStartChar = c;
  1620                                   tokenEndChar = "]";
  1621                                   break;
  1622                     }
  1623                 }
  1624 
  1625                 if (c === tokenStartChar)
  1626                     tokenDepth++;
  1627                 else if (c === tokenEndChar) {
  1628                     tokenDepth--;
  1629                     if (tokenDepth === 0) {
  1630                         var token = str.substring(tokenStart, position + 1);
  1631                         tokens.push(token);
  1632                         var replacement = "@ko_token_" + (tokens.length - 1) + "@";
  1633                         str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
  1634                         position -= (token.length - replacement.length);
  1635                         tokenStart = null;
  1636                     }
  1637                 }
  1638             }
  1639 
  1640             // Now we can safely split on commas to get the key/value pairs
  1641             var result = [];
  1642             var keyValuePairs = str.split(",");
  1643             for (var i = 0, j = keyValuePairs.length; i < j; i++) {
  1644                 var pair = keyValuePairs[i];
  1645                 var colonPos = pair.indexOf(":");
  1646                 if ((colonPos > 0) && (colonPos < pair.length - 1)) {
  1647                     var key = pair.substring(0, colonPos);
  1648                     var value = pair.substring(colonPos + 1);
  1649                     result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
  1650                 } else {
  1651                     result.push({ 'unknown': restoreTokens(pair, tokens) });
  1652                 }
  1653             }
  1654             return result;
  1655         },
  1656 
  1657         preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
  1658             var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
  1659                 ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
  1660                 : objectLiteralStringOrKeyValueArray;
  1661             var resultStrings = [], propertyAccessorResultStrings = [];
  1662 
  1663             var keyValueEntry;
  1664             for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
  1665                 if (resultStrings.length > 0)
  1666                     resultStrings.push(",");
  1667 
  1668                 if (keyValueEntry['key']) {
  1669                     var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
  1670                     resultStrings.push(quotedKey);
  1671                     resultStrings.push(":");
  1672                     resultStrings.push(val);
  1673 
  1674                     if (val = getWriteableValue(ko.utils.stringTrim(val))) {
  1675                         if (propertyAccessorResultStrings.length > 0)
  1676                             propertyAccessorResultStrings.push(", ");
  1677                         propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
  1678                     }
  1679                 } else if (keyValueEntry['unknown']) {
  1680                     resultStrings.push(keyValueEntry['unknown']);
  1681                 }
  1682             }
  1683 
  1684             var combinedResult = resultStrings.join("");
  1685             if (propertyAccessorResultStrings.length > 0) {
  1686                 var allPropertyAccessors = propertyAccessorResultStrings.join("");
  1687                 combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
  1688             }
  1689 
  1690             return combinedResult;
  1691         },
  1692 
  1693         keyValueArrayContainsKey: function(keyValueArray, key) {
  1694             for (var i = 0; i < keyValueArray.length; i++)
  1695                 if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
  1696                     return true;
  1697             return false;
  1698         },
  1699 
  1700         // Internal, private KO utility for updating model properties from within bindings
  1701         // property:            If the property being updated is (or might be) an observable, pass it here
  1702         //                      If it turns out to be a writable observable, it will be written to directly
  1703         // allBindingsAccessor: All bindings in the current execution context.
  1704         //                      This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
  1705         // key:                 The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
  1706         // value:               The value to be written
  1707         // checkIfDifferent:    If true, and if the property being written is a writable observable, the value will only be written if
  1708         //                      it is !== existing value on that writable observable
  1709         writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
  1710             if (!property || !ko.isWriteableObservable(property)) {
  1711                 var propWriters = allBindingsAccessor()['_ko_property_writers'];
  1712                 if (propWriters && propWriters[key])
  1713                     propWriters[key](value);
  1714             } else if (!checkIfDifferent || property.peek() !== value) {
  1715                 property(value);
  1716             }
  1717         }
  1718     };
  1719 })();
  1720 
  1721 ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
  1722 ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
  1723 ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
  1724 ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
  1725 
  1726 // For backward compatibility, define the following aliases. (Previously, these function names were misleading because
  1727 // they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
  1728 ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
  1729 ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
  1730     // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
  1731     // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
  1732     // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
  1733     // of that virtual hierarchy
  1734     //
  1735     // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
  1736     // without having to scatter special cases all over the binding and templating code.
  1737 
  1738     // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
  1739     // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
  1740     // So, use node.text where available, and node.nodeValue elsewhere
  1741     var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
  1742 
  1743     var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
  1744     var endCommentRegex =   commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
  1745     var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
  1746 
  1747     function isStartComment(node) {
  1748         return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
  1749     }
  1750 
  1751     function isEndComment(node) {
  1752         return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
  1753     }
  1754 
  1755     function getVirtualChildren(startComment, allowUnbalanced) {
  1756         var currentNode = startComment;
  1757         var depth = 1;
  1758         var children = [];
  1759         while (currentNode = currentNode.nextSibling) {
  1760             if (isEndComment(currentNode)) {
  1761                 depth--;
  1762                 if (depth === 0)
  1763                     return children;
  1764             }
  1765 
  1766             children.push(currentNode);
  1767 
  1768             if (isStartComment(currentNode))
  1769                 depth++;
  1770         }
  1771         if (!allowUnbalanced)
  1772             throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
  1773         return null;
  1774     }
  1775 
  1776     function getMatchingEndComment(startComment, allowUnbalanced) {
  1777         var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
  1778         if (allVirtualChildren) {
  1779             if (allVirtualChildren.length > 0)
  1780                 return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
  1781             return startComment.nextSibling;
  1782         } else
  1783             return null; // Must have no matching end comment, and allowUnbalanced is true
  1784     }
  1785 
  1786     function getUnbalancedChildTags(node) {
  1787         // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
  1788         //       from <div>OK</div><!-- /ko --><!-- /ko -->,             returns: <!-- /ko --><!-- /ko -->
  1789         var childNode = node.firstChild, captureRemaining = null;
  1790         if (childNode) {
  1791             do {
  1792                 if (captureRemaining)                   // We already hit an unbalanced node and are now just scooping up all subsequent nodes
  1793                     captureRemaining.push(childNode);
  1794                 else if (isStartComment(childNode)) {
  1795                     var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
  1796                     if (matchingEndComment)             // It's a balanced tag, so skip immediately to the end of this virtual set
  1797                         childNode = matchingEndComment;
  1798                     else
  1799                         captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
  1800                 } else if (isEndComment(childNode)) {
  1801                     captureRemaining = [childNode];     // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
  1802                 }
  1803             } while (childNode = childNode.nextSibling);
  1804         }
  1805         return captureRemaining;
  1806     }
  1807 
  1808     ko.virtualElements = {
  1809         allowedBindings: {},
  1810 
  1811         childNodes: function(node) {
  1812             return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
  1813         },
  1814 
  1815         emptyNode: function(node) {
  1816             if (!isStartComment(node))
  1817                 ko.utils.emptyDomNode(node);
  1818             else {
  1819                 var virtualChildren = ko.virtualElements.childNodes(node);
  1820                 for (var i = 0, j = virtualChildren.length; i < j; i++)
  1821                     ko.removeNode(virtualChildren[i]);
  1822             }
  1823         },
  1824 
  1825         setDomNodeChildren: function(node, childNodes) {
  1826             if (!isStartComment(node))
  1827                 ko.utils.setDomNodeChildren(node, childNodes);
  1828             else {
  1829                 ko.virtualElements.emptyNode(node);
  1830                 var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
  1831                 for (var i = 0, j = childNodes.length; i < j; i++)
  1832                     endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
  1833             }
  1834         },
  1835 
  1836         prepend: function(containerNode, nodeToPrepend) {
  1837             if (!isStartComment(containerNode)) {
  1838                 if (containerNode.firstChild)
  1839                     containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
  1840                 else
  1841                     containerNode.appendChild(nodeToPrepend);
  1842             } else {
  1843                 // Start comments must always have a parent and at least one following sibling (the end comment)
  1844                 containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
  1845             }
  1846         },
  1847 
  1848         insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
  1849             if (!insertAfterNode) {
  1850                 ko.virtualElements.prepend(containerNode, nodeToInsert);
  1851             } else if (!isStartComment(containerNode)) {
  1852                 // Insert after insertion point
  1853                 if (insertAfterNode.nextSibling)
  1854                     containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
  1855                 else
  1856                     containerNode.appendChild(nodeToInsert);
  1857             } else {
  1858                 // Children of start comments must always have a parent and at least one following sibling (the end comment)
  1859                 containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
  1860             }
  1861         },
  1862 
  1863         firstChild: function(node) {
  1864             if (!isStartComment(node))
  1865                 return node.firstChild;
  1866             if (!node.nextSibling || isEndComment(node.nextSibling))
  1867                 return null;
  1868             return node.nextSibling;
  1869         },
  1870 
  1871         nextSibling: function(node) {
  1872             if (isStartComment(node))
  1873                 node = getMatchingEndComment(node);
  1874             if (node.nextSibling && isEndComment(node.nextSibling))
  1875                 return null;
  1876             return node.nextSibling;
  1877         },
  1878 
  1879         virtualNodeBindingValue: function(node) {
  1880             var regexMatch = isStartComment(node);
  1881             return regexMatch ? regexMatch[1] : null;
  1882         },
  1883 
  1884         normaliseVirtualElementDomStructure: function(elementVerified) {
  1885             // Workaround for https://github.com/SteveSanderson/knockout/issues/155
  1886             // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
  1887             // that are direct descendants of <ul> into the preceding <li>)
  1888             if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
  1889                 return;
  1890 
  1891             // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
  1892             // must be intended to appear *after* that child, so move them there.
  1893             var childNode = elementVerified.firstChild;
  1894             if (childNode) {
  1895                 do {
  1896                     if (childNode.nodeType === 1) {
  1897                         var unbalancedTags = getUnbalancedChildTags(childNode);
  1898                         if (unbalancedTags) {
  1899                             // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
  1900                             var nodeToInsertBefore = childNode.nextSibling;
  1901                             for (var i = 0; i < unbalancedTags.length; i++) {
  1902                                 if (nodeToInsertBefore)
  1903                                     elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
  1904                                 else
  1905                                     elementVerified.appendChild(unbalancedTags[i]);
  1906                             }
  1907                         }
  1908                     }
  1909                 } while (childNode = childNode.nextSibling);
  1910             }
  1911         }
  1912     };
  1913 })();
  1914 ko.exportSymbol('virtualElements', ko.virtualElements);
  1915 ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
  1916 ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
  1917 //ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild);     // firstChild is not minified
  1918 ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
  1919 //ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling);   // nextSibling is not minified
  1920 ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
  1921 ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
  1922 (function() {
  1923     var defaultBindingAttributeName = "data-bind";
  1924 
  1925     ko.bindingProvider = function() {
  1926         this.bindingCache = {};
  1927     };
  1928 
  1929     ko.utils.extend(ko.bindingProvider.prototype, {
  1930         'nodeHasBindings': function(node) {
  1931             switch (node.nodeType) {
  1932                 case 1: return node.getAttribute(defaultBindingAttributeName) != null;   // Element
  1933                 case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
  1934                 default: return false;
  1935             }
  1936         },
  1937 
  1938         'getBindings': function(node, bindingContext) {
  1939             var bindingsString = this['getBindingsString'](node, bindingContext);
  1940             return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
  1941         },
  1942 
  1943         // The following function is only used internally by this default provider.
  1944         // It's not part of the interface definition for a general binding provider.
  1945         'getBindingsString': function(node, bindingContext) {
  1946             switch (node.nodeType) {
  1947                 case 1: return node.getAttribute(defaultBindingAttributeName);   // Element
  1948                 case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
  1949                 default: return null;
  1950             }
  1951         },
  1952 
  1953         // The following function is only used internally by this default provider.
  1954         // It's not part of the interface definition for a general binding provider.
  1955         'parseBindingsString': function(bindingsString, bindingContext, node) {
  1956             try {
  1957                 var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
  1958                 return bindingFunction(bindingContext, node);
  1959             } catch (ex) {
  1960                 throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
  1961             }
  1962         }
  1963     });
  1964 
  1965     ko.bindingProvider['instance'] = new ko.bindingProvider();
  1966 
  1967     function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
  1968         var cacheKey = bindingsString;
  1969         return cache[cacheKey]
  1970             || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
  1971     }
  1972 
  1973     function createBindingsStringEvaluator(bindingsString) {
  1974         // Build the source for a function that evaluates "expression"
  1975         // For each scope variable, add an extra level of "with" nesting
  1976         // Example result: with(sc1) { with(sc0) { return (expression) } }
  1977         var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
  1978             functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
  1979         return new Function("$context", "$element", functionBody);
  1980     }
  1981 })();
  1982 
  1983 ko.exportSymbol('bindingProvider', ko.bindingProvider);
  1984 (function () {
  1985     ko.bindingHandlers = {};
  1986 
  1987     ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
  1988         if (parentBindingContext) {
  1989             ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
  1990             this['$parentContext'] = parentBindingContext;
  1991             this['$parent'] = parentBindingContext['$data'];
  1992             this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
  1993             this['$parents'].unshift(this['$parent']);
  1994         } else {
  1995             this['$parents'] = [];
  1996             this['$root'] = dataItem;
  1997             // Export 'ko' in the binding context so it will be available in bindings and templates
  1998             // even if 'ko' isn't exported as a global, such as when using an AMD loader.
  1999             // See https://github.com/SteveSanderson/knockout/issues/490
  2000             this['ko'] = ko;
  2001         }
  2002         this['$data'] = dataItem;
  2003         if (dataItemAlias)
  2004             this[dataItemAlias] = dataItem;
  2005     }
  2006     ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
  2007         return new ko.bindingContext(dataItem, this, dataItemAlias);
  2008     };
  2009     ko.bindingContext.prototype['extend'] = function(properties) {
  2010         var clone = ko.utils.extend(new ko.bindingContext(), this);
  2011         return ko.utils.extend(clone, properties);
  2012     };
  2013 
  2014     function validateThatBindingIsAllowedForVirtualElements(bindingName) {
  2015         var validator = ko.virtualElements.allowedBindings[bindingName];
  2016         if (!validator)
  2017             throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
  2018     }
  2019 
  2020     function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
  2021         var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
  2022         while (currentChild = nextInQueue) {
  2023             // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
  2024             nextInQueue = ko.virtualElements.nextSibling(currentChild);
  2025             applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
  2026         }
  2027     }
  2028 
  2029     function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
  2030         var shouldBindDescendants = true;
  2031 
  2032         // Perf optimisation: Apply bindings only if...
  2033         // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
  2034         //     Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
  2035         // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
  2036         var isElement = (nodeVerified.nodeType === 1);
  2037         if (isElement) // Workaround IE <= 8 HTML parsing weirdness
  2038             ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
  2039 
  2040         var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement)             // Case (1)
  2041                                || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified);       // Case (2)
  2042         if (shouldApplyBindings)
  2043             shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
  2044 
  2045         if (shouldBindDescendants) {
  2046             // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
  2047             //  * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
  2048             //    hence bindingContextsMayDifferFromDomParentElement is false
  2049             //  * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
  2050             //    skip over any number of intermediate virtual elements, any of which might define a custom binding context,
  2051             //    hence bindingContextsMayDifferFromDomParentElement is true
  2052             applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
  2053         }
  2054     }
  2055 
  2056     function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
  2057         // Need to be sure that inits are only run once, and updates never run until all the inits have been run
  2058         var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
  2059 
  2060         // Each time the dependentObservable is evaluated (after data changes),
  2061         // the binding attribute is reparsed so that it can pick out the correct
  2062         // model properties in the context of the changed data.
  2063         // DOM event callbacks need to be able to access this changed data,
  2064         // so we need a single parsedBindings variable (shared by all callbacks
  2065         // associated with this node's bindings) that all the closures can access.
  2066         var parsedBindings;
  2067         function makeValueAccessor(bindingKey) {
  2068             return function () { return parsedBindings[bindingKey] }
  2069         }
  2070         function parsedBindingsAccessor() {
  2071             return parsedBindings;
  2072         }
  2073 
  2074         var bindingHandlerThatControlsDescendantBindings;
  2075         ko.dependentObservable(
  2076             function () {
  2077                 // Ensure we have a nonnull binding context to work with
  2078                 var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
  2079                     ? viewModelOrBindingContext
  2080                     : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
  2081                 var viewModel = bindingContextInstance['$data'];
  2082 
  2083                 // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
  2084                 // we can easily recover it just by scanning up the node's ancestors in the DOM
  2085                 // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
  2086                 if (bindingContextMayDifferFromDomParentElement)
  2087                     ko.storedBindingContextForNode(node, bindingContextInstance);
  2088 
  2089                 // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
  2090                 var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
  2091                 parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
  2092 
  2093                 if (parsedBindings) {
  2094                     // First run all the inits, so bindings can register for notification on changes
  2095                     if (initPhase === 0) {
  2096                         initPhase = 1;
  2097                         for (var bindingKey in parsedBindings) {
  2098                             var binding = ko.bindingHandlers[bindingKey];
  2099                             if (binding && node.nodeType === 8)
  2100                                 validateThatBindingIsAllowedForVirtualElements(bindingKey);
  2101 
  2102                             if (binding && typeof binding["init"] == "function") {
  2103                                 var handlerInitFn = binding["init"];
  2104                                 var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
  2105 
  2106                                 // If this binding handler claims to control descendant bindings, make a note of this
  2107                                 if (initResult && initResult['controlsDescendantBindings']) {
  2108                                     if (bindingHandlerThatControlsDescendantBindings !== undefined)
  2109                                         throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
  2110                                     bindingHandlerThatControlsDescendantBindings = bindingKey;
  2111                                 }
  2112                             }
  2113                         }
  2114                         initPhase = 2;
  2115                     }
  2116 
  2117                     // ... then run all the updates, which might trigger changes even on the first evaluation
  2118                     if (initPhase === 2) {
  2119                         for (var bindingKey in parsedBindings) {
  2120                             var binding = ko.bindingHandlers[bindingKey];
  2121                             if (binding && typeof binding["update"] == "function") {
  2122                                 var handlerUpdateFn = binding["update"];
  2123                                 handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
  2124                             }
  2125                         }
  2126                     }
  2127                 }
  2128             },
  2129             null,
  2130             { disposeWhenNodeIsRemoved : node }
  2131         );
  2132 
  2133         return {
  2134             shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
  2135         };
  2136     };
  2137 
  2138     var storedBindingContextDomDataKey = "__ko_bindingContext__";
  2139     ko.storedBindingContextForNode = function (node, bindingContext) {
  2140         if (arguments.length == 2)
  2141             ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
  2142         else
  2143             return ko.utils.domData.get(node, storedBindingContextDomDataKey);
  2144     }
  2145 
  2146     ko.applyBindingsToNode = function (node, bindings, viewModel) {
  2147         if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
  2148             ko.virtualElements.normaliseVirtualElementDomStructure(node);
  2149         return applyBindingsToNodeInternal(node, bindings, viewModel, true);
  2150     };
  2151 
  2152     ko.applyBindingsToDescendants = function(viewModel, rootNode) {
  2153         if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
  2154             applyBindingsToDescendantsInternal(viewModel, rootNode, true);
  2155     };
  2156 
  2157     ko.applyBindings = function (viewModel, rootNode) {
  2158         if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
  2159             throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
  2160         rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
  2161 
  2162         applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
  2163     };
  2164 
  2165     // Retrieving binding context from arbitrary nodes
  2166     ko.contextFor = function(node) {
  2167         // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
  2168         switch (node.nodeType) {
  2169             case 1:
  2170             case 8:
  2171                 var context = ko.storedBindingContextForNode(node);
  2172                 if (context) return context;
  2173                 if (node.parentNode) return ko.contextFor(node.parentNode);
  2174                 break;
  2175         }
  2176         return undefined;
  2177     };
  2178     ko.dataFor = function(node) {
  2179         var context = ko.contextFor(node);
  2180         return context ? context['$data'] : undefined;
  2181     };
  2182 
  2183     ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
  2184     ko.exportSymbol('applyBindings', ko.applyBindings);
  2185     ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
  2186     ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
  2187     ko.exportSymbol('contextFor', ko.contextFor);
  2188     ko.exportSymbol('dataFor', ko.dataFor);
  2189 })();
  2190 var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
  2191 ko.bindingHandlers['attr'] = {
  2192     'update': function(element, valueAccessor, allBindingsAccessor) {
  2193         var value = ko.utils.unwrapObservable(valueAccessor()) || {};
  2194         for (var attrName in value) {
  2195             if (typeof attrName == "string") {
  2196                 var attrValue = ko.utils.unwrapObservable(value[attrName]);
  2197 
  2198                 // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
  2199                 // when someProp is a "no value"-like value (strictly null, false, or undefined)
  2200                 // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
  2201                 var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
  2202                 if (toRemove)
  2203                     element.removeAttribute(attrName);
  2204 
  2205                 // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
  2206                 // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
  2207                 // but instead of figuring out the mode, we'll just set the attribute through the Javascript
  2208                 // property for IE <= 8.
  2209                 if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
  2210                     attrName = attrHtmlToJavascriptMap[attrName];
  2211                     if (toRemove)
  2212                         element.removeAttribute(attrName);
  2213                     else
  2214                         element[attrName] = attrValue;
  2215                 } else if (!toRemove) {
  2216                     try {
  2217                         element.setAttribute(attrName, attrValue.toString());
  2218                     } catch (err) {
  2219                         // ignore for now
  2220                         if (console) {
  2221                             console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
  2222                         }
  2223                     }
  2224                 }
  2225 
  2226                 // Treat "name" specially - although you can think of it as an attribute, it also needs
  2227                 // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
  2228                 // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
  2229                 // entirely, and there's no strong reason to allow for such casing in HTML.
  2230                 if (attrName === "name") {
  2231                     ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
  2232                 }
  2233             }
  2234         }
  2235     }
  2236 };
  2237 ko.bindingHandlers['checked'] = {
  2238     'init': function (element, valueAccessor, allBindingsAccessor) {
  2239         var updateHandler = function() {
  2240             var valueToWrite;
  2241             if (element.type == "checkbox") {
  2242                 valueToWrite = element.checked;
  2243             } else if ((element.type == "radio") && (element.checked)) {
  2244                 valueToWrite = element.value;
  2245             } else {
  2246                 return; // "checked" binding only responds to checkboxes and selected radio buttons
  2247             }
  2248 
  2249             var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
  2250             if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
  2251                 // For checkboxes bound to an array, we add/remove the checkbox value to that array
  2252                 // This works for both observable and non-observable arrays
  2253                 var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
  2254                 if (element.checked && (existingEntryIndex < 0))
  2255                     modelValue.push(element.value);
  2256                 else if ((!element.checked) && (existingEntryIndex >= 0))
  2257                     modelValue.splice(existingEntryIndex, 1);
  2258             } else {
  2259                 ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
  2260             }
  2261         };
  2262         ko.utils.registerEventHandler(element, "click", updateHandler);
  2263 
  2264         // IE 6 won't allow radio buttons to be selected unless they have a name
  2265         if ((element.type == "radio") && !element.name)
  2266             ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
  2267     },
  2268     'update': function (element, valueAccessor) {
  2269         var value = ko.utils.unwrapObservable(valueAccessor());
  2270 
  2271         if (element.type == "checkbox") {
  2272             if (value instanceof Array) {
  2273                 // When bound to an array, the checkbox being checked represents its value being present in that array
  2274                 element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
  2275             } else {
  2276                 // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
  2277                 element.checked = value;
  2278             }
  2279         } else if (element.type == "radio") {
  2280             element.checked = (element.value == value);
  2281         }
  2282     }
  2283 };
  2284 var classesWrittenByBindingKey = '__ko__cssValue';
  2285 ko.bindingHandlers['css'] = {
  2286     'update': function (element, valueAccessor) {
  2287         var value = ko.utils.unwrapObservable(valueAccessor());
  2288         if (typeof value == "object") {
  2289             for (var className in value) {
  2290                 var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
  2291                 ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
  2292             }
  2293         } else {
  2294             value = String(value || ''); // Make sure we don't try to store or set a non-string value
  2295             ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
  2296             element[classesWrittenByBindingKey] = value;
  2297             ko.utils.toggleDomNodeCssClass(element, value, true);
  2298         }
  2299     }
  2300 };
  2301 ko.bindingHandlers['enable'] = {
  2302     'update': function (element, valueAccessor) {
  2303         var value = ko.utils.unwrapObservable(valueAccessor());
  2304         if (value && element.disabled)
  2305             element.removeAttribute("disabled");
  2306         else if ((!value) && (!element.disabled))
  2307             element.disabled = true;
  2308     }
  2309 };
  2310 
  2311 ko.bindingHandlers['disable'] = {
  2312     'update': function (element, valueAccessor) {
  2313         ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
  2314     }
  2315 };
  2316 // For certain common events (currently just 'click'), allow a simplified data-binding syntax
  2317 // e.g. click:handler instead of the usual full-length event:{click:handler}
  2318 function makeEventHandlerShortcut(eventName) {
  2319     ko.bindingHandlers[eventName] = {
  2320         'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
  2321             var newValueAccessor = function () {
  2322                 var result = {};
  2323                 result[eventName] = valueAccessor();
  2324                 return result;
  2325             };
  2326             return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
  2327         }
  2328     }
  2329 }
  2330 
  2331 ko.bindingHandlers['event'] = {
  2332     'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
  2333         var eventsToHandle = valueAccessor() || {};
  2334         for(var eventNameOutsideClosure in eventsToHandle) {
  2335             (function() {
  2336                 var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
  2337                 if (typeof eventName == "string") {
  2338                     ko.utils.registerEventHandler(element, eventName, function (event) {
  2339                         var handlerReturnValue;
  2340                         var handlerFunction = valueAccessor()[eventName];
  2341                         if (!handlerFunction)
  2342                             return;
  2343                         var allBindings = allBindingsAccessor();
  2344 
  2345                         try {
  2346                             // Take all the event args, and prefix with the viewmodel
  2347                             var argsForHandler = ko.utils.makeArray(arguments);
  2348                             argsForHandler.unshift(viewModel);
  2349                             handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
  2350                         } finally {
  2351                             if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
  2352                                 if (event.preventDefault)
  2353                                     event.preventDefault();
  2354                                 else
  2355                                     event.returnValue = false;
  2356                             }
  2357                         }
  2358 
  2359                         var bubble = allBindings[eventName + 'Bubble'] !== false;
  2360                         if (!bubble) {
  2361                             event.cancelBubble = true;
  2362                             if (event.stopPropagation)
  2363                                 event.stopPropagation();
  2364                         }
  2365                     });
  2366                 }
  2367             })();
  2368         }
  2369     }
  2370 };
  2371 // "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
  2372 // "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
  2373 ko.bindingHandlers['foreach'] = {
  2374     makeTemplateValueAccessor: function(valueAccessor) {
  2375         return function() {
  2376             var modelValue = valueAccessor(),
  2377                 unwrappedValue = ko.utils.peekObservable(modelValue);    // Unwrap without setting a dependency here
  2378 
  2379             // If unwrappedValue is the array, pass in the wrapped value on its own
  2380             // The value will be unwrapped and tracked within the template binding
  2381             // (See https://github.com/SteveSanderson/knockout/issues/523)
  2382             if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
  2383                 return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
  2384 
  2385             // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
  2386             ko.utils.unwrapObservable(modelValue);
  2387             return {
  2388                 'foreach': unwrappedValue['data'],
  2389                 'as': unwrappedValue['as'],
  2390                 'includeDestroyed': unwrappedValue['includeDestroyed'],
  2391                 'afterAdd': unwrappedValue['afterAdd'],
  2392                 'beforeRemove': unwrappedValue['beforeRemove'],
  2393                 'afterRender': unwrappedValue['afterRender'],
  2394                 'beforeMove': unwrappedValue['beforeMove'],
  2395                 'afterMove': unwrappedValue['afterMove'],
  2396                 'templateEngine': ko.nativeTemplateEngine.instance
  2397             };
  2398         };
  2399     },
  2400     'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  2401         return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
  2402     },
  2403     'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  2404         return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
  2405     }
  2406 };
  2407 ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
  2408 ko.virtualElements.allowedBindings['foreach'] = true;
  2409 var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
  2410 ko.bindingHandlers['hasfocus'] = {
  2411     'init': function(element, valueAccessor, allBindingsAccessor) {
  2412         var handleElementFocusChange = function(isFocused) {
  2413             // Where possible, ignore which event was raised and determine focus state using activeElement,
  2414             // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
  2415             // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
  2416             // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
  2417             // from calling 'blur()' on the element when it loses focus.
  2418             // Discussion at https://github.com/SteveSanderson/knockout/pull/352
  2419             element[hasfocusUpdatingProperty] = true;
  2420             var ownerDoc = element.ownerDocument;
  2421             if ("activeElement" in ownerDoc) {
  2422                 isFocused = (ownerDoc.activeElement === element);
  2423             }
  2424             var modelValue = valueAccessor();
  2425             ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
  2426             element[hasfocusUpdatingProperty] = false;
  2427         };
  2428         var handleElementFocusIn = handleElementFocusChange.bind(null, true);
  2429         var handleElementFocusOut = handleElementFocusChange.bind(null, false);
  2430 
  2431         ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
  2432         ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
  2433         ko.utils.registerEventHandler(element, "blur",  handleElementFocusOut);
  2434         ko.utils.registerEventHandler(element, "focusout",  handleElementFocusOut); // For IE
  2435     },
  2436     'update': function(element, valueAccessor) {
  2437         var value = ko.utils.unwrapObservable(valueAccessor());
  2438         if (!element[hasfocusUpdatingProperty]) {
  2439             value ? element.focus() : element.blur();
  2440             ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
  2441         }
  2442     }
  2443 };
  2444 ko.bindingHandlers['html'] = {
  2445     'init': function() {
  2446         // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
  2447         return { 'controlsDescendantBindings': true };
  2448     },
  2449     'update': function (element, valueAccessor) {
  2450         // setHtml will unwrap the value if needed
  2451         ko.utils.setHtml(element, valueAccessor());
  2452     }
  2453 };
  2454 var withIfDomDataKey = '__ko_withIfBindingData';
  2455 // Makes a binding like with or if
  2456 function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
  2457     ko.bindingHandlers[bindingKey] = {
  2458         'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  2459             ko.utils.domData.set(element, withIfDomDataKey, {});
  2460             return { 'controlsDescendantBindings': true };
  2461         },
  2462         'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  2463             var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
  2464                 dataValue = ko.utils.unwrapObservable(valueAccessor()),
  2465                 shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
  2466                 isFirstRender = !withIfData.savedNodes,
  2467                 needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
  2468 
  2469             if (needsRefresh) {
  2470                 if (isFirstRender) {
  2471                     withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
  2472                 }
  2473 
  2474                 if (shouldDisplay) {
  2475                     if (!isFirstRender) {
  2476                         ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
  2477                     }
  2478                     ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
  2479                 } else {
  2480                     ko.virtualElements.emptyNode(element);
  2481                 }
  2482 
  2483                 withIfData.didDisplayOnLastUpdate = shouldDisplay;
  2484             }
  2485         }
  2486     };
  2487     ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
  2488     ko.virtualElements.allowedBindings[bindingKey] = true;
  2489 }
  2490 
  2491 // Construct the actual binding handlers
  2492 makeWithIfBinding('if');
  2493 makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
  2494 makeWithIfBinding('with', true /* isWith */, false /* isNot */,
  2495     function(bindingContext, dataValue) {
  2496         return bindingContext['createChildContext'](dataValue);
  2497     }
  2498 );
  2499 function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
  2500     if (preferModelValue) {
  2501         if (modelValue !== ko.selectExtensions.readValue(element))
  2502             ko.selectExtensions.writeValue(element, modelValue);
  2503     }
  2504 
  2505     // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
  2506     // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
  2507     // change the model value to match the dropdown.
  2508     if (modelValue !== ko.selectExtensions.readValue(element))
  2509         ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
  2510 };
  2511 
  2512 ko.bindingHandlers['options'] = {
  2513     'update': function (element, valueAccessor, allBindingsAccessor) {
  2514         if (ko.utils.tagNameLower(element) !== "select")
  2515             throw new Error("options binding applies only to SELECT elements");
  2516 
  2517         var selectWasPreviouslyEmpty = element.length == 0;
  2518         var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
  2519             return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
  2520         }), function (node) {
  2521             return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
  2522         });
  2523         var previousScrollTop = element.scrollTop;
  2524 
  2525         var value = ko.utils.unwrapObservable(valueAccessor());
  2526         var selectedValue = element.value;
  2527 
  2528         // Remove all existing <option>s.
  2529         // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
  2530         while (element.length > 0) {
  2531             ko.cleanNode(element.options[0]);
  2532             element.remove(0);
  2533         }
  2534 
  2535         if (value) {
  2536             var allBindings = allBindingsAccessor(),
  2537                 includeDestroyed = allBindings['optionsIncludeDestroyed'];
  2538 
  2539             if (typeof value.length != "number")
  2540                 value = [value];
  2541             if (allBindings['optionsCaption']) {
  2542                 var option = document.createElement("option");
  2543                 ko.utils.setHtml(option, allBindings['optionsCaption']);
  2544                 ko.selectExtensions.writeValue(option, undefined);
  2545                 element.appendChild(option);
  2546             }
  2547 
  2548             for (var i = 0, j = value.length; i < j; i++) {
  2549                 // Skip destroyed items
  2550                 var arrayEntry = value[i];
  2551                 if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
  2552                     continue;
  2553 
  2554                 var option = document.createElement("option");
  2555 
  2556                 function applyToObject(object, predicate, defaultValue) {
  2557                     var predicateType = typeof predicate;
  2558                     if (predicateType == "function")    // Given a function; run it against the data value
  2559                         return predicate(object);
  2560                     else if (predicateType == "string") // Given a string; treat it as a property name on the data value
  2561                         return object[predicate];
  2562                     else                                // Given no optionsText arg; use the data value itself
  2563                         return defaultValue;
  2564                 }
  2565 
  2566                 // Apply a value to the option element
  2567                 var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
  2568                 ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
  2569 
  2570                 // Apply some text to the option element
  2571                 var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
  2572                 ko.utils.setTextContent(option, optionText);
  2573 
  2574                 element.appendChild(option);
  2575             }
  2576 
  2577             // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
  2578             // That's why we first added them without selection. Now it's time to set the selection.
  2579             var newOptions = element.getElementsByTagName("option");
  2580             var countSelectionsRetained = 0;
  2581             for (var i = 0, j = newOptions.length; i < j; i++) {
  2582                 if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
  2583                     ko.utils.setOptionNodeSelectionState(newOptions[i], true);
  2584                     countSelectionsRetained++;
  2585                 }
  2586             }
  2587 
  2588             element.scrollTop = previousScrollTop;
  2589 
  2590             if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
  2591                 // Ensure consistency between model value and selected option.
  2592                 // If the dropdown is being populated for the first time here (or was otherwise previously empty),
  2593                 // the dropdown selection state is meaningless, so we preserve the model value.
  2594                 ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
  2595             }
  2596 
  2597             // Workaround for IE9 bug
  2598             ko.utils.ensureSelectElementIsRenderedCorrectly(element);
  2599         }
  2600     }
  2601 };
  2602 ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
  2603 ko.bindingHandlers['selectedOptions'] = {
  2604     'init': function (element, valueAccessor, allBindingsAccessor) {
  2605         ko.utils.registerEventHandler(element, "change", function () {
  2606             var value = valueAccessor(), valueToWrite = [];
  2607             ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
  2608                 if (node.selected)
  2609                     valueToWrite.push(ko.selectExtensions.readValue(node));
  2610             });
  2611             ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
  2612         });
  2613     },
  2614     'update': function (element, valueAccessor) {
  2615         if (ko.utils.tagNameLower(element) != "select")
  2616             throw new Error("values binding applies only to SELECT elements");
  2617 
  2618         var newValue = ko.utils.unwrapObservable(valueAccessor());
  2619         if (newValue && typeof newValue.length == "number") {
  2620             ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
  2621                 var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
  2622                 ko.utils.setOptionNodeSelectionState(node, isSelected);
  2623             });
  2624         }
  2625     }
  2626 };
  2627 ko.bindingHandlers['style'] = {
  2628     'update': function (element, valueAccessor) {
  2629         var value = ko.utils.unwrapObservable(valueAccessor() || {});
  2630         for (var styleName in value) {
  2631             if (typeof styleName == "string") {
  2632                 var styleValue = ko.utils.unwrapObservable(value[styleName]);
  2633                 element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
  2634             }
  2635         }
  2636     }
  2637 };
  2638 ko.bindingHandlers['submit'] = {
  2639     'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
  2640         if (typeof valueAccessor() != "function")
  2641             throw new Error("The value for a submit binding must be a function");
  2642         ko.utils.registerEventHandler(element, "submit", function (event) {
  2643             var handlerReturnValue;
  2644             var value = valueAccessor();
  2645             try { handlerReturnValue = value.call(viewModel, element); }
  2646             finally {
  2647                 if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
  2648                     if (event.preventDefault)
  2649                         event.preventDefault();
  2650                     else
  2651                         event.returnValue = false;
  2652                 }
  2653             }
  2654         });
  2655     }
  2656 };
  2657 ko.bindingHandlers['text'] = {
  2658     'update': function (element, valueAccessor) {
  2659         ko.utils.setTextContent(element, valueAccessor());
  2660     }
  2661 };
  2662 ko.virtualElements.allowedBindings['text'] = true;
  2663 ko.bindingHandlers['uniqueName'] = {
  2664     'init': function (element, valueAccessor) {
  2665         if (valueAccessor()) {
  2666             var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
  2667             ko.utils.setElementName(element, name);
  2668         }
  2669     }
  2670 };
  2671 ko.bindingHandlers['uniqueName'].currentIndex = 0;
  2672 ko.bindingHandlers['value'] = {
  2673     'init': function (element, valueAccessor, allBindingsAccessor) {
  2674         // Always catch "change" event; possibly other events too if asked
  2675         var eventsToCatch = ["change"];
  2676         var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
  2677         var propertyChangedFired = false;
  2678         if (requestedEventsToCatch) {
  2679             if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
  2680                 requestedEventsToCatch = [requestedEventsToCatch];
  2681             ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
  2682             eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
  2683         }
  2684 
  2685         var valueUpdateHandler = function() {
  2686             propertyChangedFired = false;
  2687             var modelValue = valueAccessor();
  2688             var elementValue = ko.selectExtensions.readValue(element);
  2689             ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
  2690         }
  2691 
  2692         // Workaround for https://github.com/SteveSanderson/knockout/issues/122
  2693         // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
  2694         var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
  2695                                        && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
  2696         if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
  2697             ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
  2698             ko.utils.registerEventHandler(element, "blur", function() {
  2699                 if (propertyChangedFired) {
  2700                     valueUpdateHandler();
  2701                 }
  2702             });
  2703         }
  2704 
  2705         ko.utils.arrayForEach(eventsToCatch, function(eventName) {
  2706             // The syntax "after<eventname>" means "run the handler asynchronously after the event"
  2707             // This is useful, for example, to catch "keydown" events after the browser has updated the control
  2708             // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
  2709             var handler = valueUpdateHandler;
  2710             if (ko.utils.stringStartsWith(eventName, "after")) {
  2711                 handler = function() { setTimeout(valueUpdateHandler, 0) };
  2712                 eventName = eventName.substring("after".length);
  2713             }
  2714             ko.utils.registerEventHandler(element, eventName, handler);
  2715         });
  2716     },
  2717     'update': function (element, valueAccessor) {
  2718         var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
  2719         var newValue = ko.utils.unwrapObservable(valueAccessor());
  2720         var elementValue = ko.selectExtensions.readValue(element);
  2721         var valueHasChanged = (newValue != elementValue);
  2722 
  2723         // JavaScript's 0 == "" behavious is unfortunate here as it prevents writing 0 to an empty text box (loose equality suggests the values are the same).
  2724         // We don't want to do a strict equality comparison as that is more confusing for developers in certain cases, so we specifically special case 0 != "" here.
  2725         if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
  2726             valueHasChanged = true;
  2727 
  2728         if (valueHasChanged) {
  2729             var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
  2730             applyValueAction();
  2731 
  2732             // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
  2733             // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
  2734             // to apply the value as well.
  2735             var alsoApplyAsynchronously = valueIsSelectOption;
  2736             if (alsoApplyAsynchronously)
  2737                 setTimeout(applyValueAction, 0);
  2738         }
  2739 
  2740         // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
  2741         // because you're not allowed to have a model value that disagrees with a visible UI selection.
  2742         if (valueIsSelectOption && (element.length > 0))
  2743             ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
  2744     }
  2745 };
  2746 ko.bindingHandlers['visible'] = {
  2747     'update': function (element, valueAccessor) {
  2748         var value = ko.utils.unwrapObservable(valueAccessor());
  2749         var isCurrentlyVisible = !(element.style.display == "none");
  2750         if (value && !isCurrentlyVisible)
  2751             element.style.display = "";
  2752         else if ((!value) && isCurrentlyVisible)
  2753             element.style.display = "none";
  2754     }
  2755 };
  2756 // 'click' is just a shorthand for the usual full-length event:{click:handler}
  2757 makeEventHandlerShortcut('click');
  2758 // If you want to make a custom template engine,
  2759 //
  2760 // [1] Inherit from this class (like ko.nativeTemplateEngine does)
  2761 // [2] Override 'renderTemplateSource', supplying a function with this signature:
  2762 //
  2763 //        function (templateSource, bindingContext, options) {
  2764 //            // - templateSource.text() is the text of the template you should render
  2765 //            // - bindingContext.$data is the data you should pass into the template
  2766 //            //   - you might also want to make bindingContext.$parent, bindingContext.$parents,
  2767 //            //     and bindingContext.$root available in the template too
  2768 //            // - options gives you access to any other properties set on "data-bind: { template: options }"
  2769 //            //
  2770 //            // Return value: an array of DOM nodes
  2771 //        }
  2772 //
  2773 // [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
  2774 //
  2775 //        function (script) {
  2776 //            // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
  2777 //            //               For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
  2778 //        }
  2779 //
  2780 //     This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
  2781 //     If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
  2782 //     and then you don't need to override 'createJavaScriptEvaluatorBlock'.
  2783 
  2784 ko.templateEngine = function () { };
  2785 
  2786 ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
  2787     throw new Error("Override renderTemplateSource");
  2788 };
  2789 
  2790 ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
  2791     throw new Error("Override createJavaScriptEvaluatorBlock");
  2792 };
  2793 
  2794 ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
  2795     // Named template
  2796     if (typeof template == "string") {
  2797         templateDocument = templateDocument || document;
  2798         var elem = templateDocument.getElementById(template);
  2799         if (!elem)
  2800             throw new Error("Cannot find template with ID " + template);
  2801         return new ko.templateSources.domElement(elem);
  2802     } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
  2803         // Anonymous template
  2804         return new ko.templateSources.anonymousTemplate(template);
  2805     } else
  2806         throw new Error("Unknown template type: " + template);
  2807 };
  2808 
  2809 ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
  2810     var templateSource = this['makeTemplateSource'](template, templateDocument);
  2811     return this['renderTemplateSource'](templateSource, bindingContext, options);
  2812 };
  2813 
  2814 ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
  2815     // Skip rewriting if requested
  2816     if (this['allowTemplateRewriting'] === false)
  2817         return true;
  2818     return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
  2819 };
  2820 
  2821 ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
  2822     var templateSource = this['makeTemplateSource'](template, templateDocument);
  2823     var rewritten = rewriterCallback(templateSource['text']());
  2824     templateSource['text'](rewritten);
  2825     templateSource['data']("isRewritten", true);
  2826 };
  2827 
  2828 ko.exportSymbol('templateEngine', ko.templateEngine);
  2829 
  2830 ko.templateRewriting = (function () {
  2831     var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
  2832     var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
  2833 
  2834     function validateDataBindValuesForRewriting(keyValueArray) {
  2835         var allValidators = ko.expressionRewriting.bindingRewriteValidators;
  2836         for (var i = 0; i < keyValueArray.length; i++) {
  2837             var key = keyValueArray[i]['key'];
  2838             if (allValidators.hasOwnProperty(key)) {
  2839                 var validator = allValidators[key];
  2840 
  2841                 if (typeof validator === "function") {
  2842                     var possibleErrorMessage = validator(keyValueArray[i]['value']);
  2843                     if (possibleErrorMessage)
  2844                         throw new Error(possibleErrorMessage);
  2845                 } else if (!validator) {
  2846                     throw new Error("This template engine does not support the '" + key + "' binding within its templates");
  2847                 }
  2848             }
  2849         }
  2850     }
  2851 
  2852     function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
  2853         var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
  2854         validateDataBindValuesForRewriting(dataBindKeyValueArray);
  2855         var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
  2856 
  2857         // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
  2858         // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
  2859         // extra indirection.
  2860         var applyBindingsToNextSiblingScript =
  2861             "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
  2862         return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
  2863     }
  2864 
  2865     return {
  2866         ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
  2867             if (!templateEngine['isTemplateRewritten'](template, templateDocument))
  2868                 templateEngine['rewriteTemplate'](template, function (htmlString) {
  2869                     return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
  2870                 }, templateDocument);
  2871         },
  2872 
  2873         memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
  2874             return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
  2875                 return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
  2876             }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
  2877                 return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
  2878             });
  2879         },
  2880 
  2881         applyMemoizedBindingsToNextSibling: function (bindings) {
  2882             return ko.memoization.memoize(function (domNode, bindingContext) {
  2883                 if (domNode.nextSibling)
  2884                     ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
  2885             });
  2886         }
  2887     }
  2888 })();
  2889 
  2890 
  2891 // Exported only because it has to be referenced by string lookup from within rewritten template
  2892 ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
  2893 (function() {
  2894     // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
  2895     // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
  2896     //
  2897     // Two are provided by default:
  2898     //  1. ko.templateSources.domElement       - reads/writes the text content of an arbitrary DOM element
  2899     //  2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
  2900     //                                           without reading/writing the actual element text content, since it will be overwritten
  2901     //                                           with the rendered template output.
  2902     // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
  2903     // Template sources need to have the following functions:
  2904     //   text() 			- returns the template text from your storage location
  2905     //   text(value)		- writes the supplied template text to your storage location
  2906     //   data(key)			- reads values stored using data(key, value) - see below
  2907     //   data(key, value)	- associates "value" with this template and the key "key". Is used to store information like "isRewritten".
  2908     //
  2909     // Optionally, template sources can also have the following functions:
  2910     //   nodes()            - returns a DOM element containing the nodes of this template, where available
  2911     //   nodes(value)       - writes the given DOM element to your storage location
  2912     // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
  2913     // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
  2914     //
  2915     // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
  2916     // using and overriding "makeTemplateSource" to return an instance of your custom template source.
  2917 
  2918     ko.templateSources = {};
  2919 
  2920     // ---- ko.templateSources.domElement -----
  2921 
  2922     ko.templateSources.domElement = function(element) {
  2923         this.domElement = element;
  2924     }
  2925 
  2926     ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
  2927         var tagNameLower = ko.utils.tagNameLower(this.domElement),
  2928             elemContentsProperty = tagNameLower === "script" ? "text"
  2929                                  : tagNameLower === "textarea" ? "value"
  2930                                  : "innerHTML";
  2931 
  2932         if (arguments.length == 0) {
  2933             return this.domElement[elemContentsProperty];
  2934         } else {
  2935             var valueToWrite = arguments[0];
  2936             if (elemContentsProperty === "innerHTML")
  2937                 ko.utils.setHtml(this.domElement, valueToWrite);
  2938             else
  2939                 this.domElement[elemContentsProperty] = valueToWrite;
  2940         }
  2941     };
  2942 
  2943     ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
  2944         if (arguments.length === 1) {
  2945             return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
  2946         } else {
  2947             ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
  2948         }
  2949     };
  2950 
  2951     // ---- ko.templateSources.anonymousTemplate -----
  2952     // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
  2953     // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
  2954     // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
  2955 
  2956     var anonymousTemplatesDomDataKey = "__ko_anon_template__";
  2957     ko.templateSources.anonymousTemplate = function(element) {
  2958         this.domElement = element;
  2959     }
  2960     ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
  2961     ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
  2962         if (arguments.length == 0) {
  2963             var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
  2964             if (templateData.textData === undefined && templateData.containerData)
  2965                 templateData.textData = templateData.containerData.innerHTML;
  2966             return templateData.textData;
  2967         } else {
  2968             var valueToWrite = arguments[0];
  2969             ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
  2970         }
  2971     };
  2972     ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
  2973         if (arguments.length == 0) {
  2974             var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
  2975             return templateData.containerData;
  2976         } else {
  2977             var valueToWrite = arguments[0];
  2978             ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
  2979         }
  2980     };
  2981 
  2982     ko.exportSymbol('templateSources', ko.templateSources);
  2983     ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
  2984     ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
  2985 })();
  2986 (function () {
  2987     var _templateEngine;
  2988     ko.setTemplateEngine = function (templateEngine) {
  2989         if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
  2990             throw new Error("templateEngine must inherit from ko.templateEngine");
  2991         _templateEngine = templateEngine;
  2992     }
  2993 
  2994     function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
  2995         var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
  2996         while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
  2997             nextInQueue = ko.virtualElements.nextSibling(node);
  2998             if (node.nodeType === 1 || node.nodeType === 8)
  2999                 action(node);
  3000         }
  3001     }
  3002 
  3003     function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
  3004         // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
  3005         // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
  3006         // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
  3007         // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
  3008         // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
  3009 
  3010         if (continuousNodeArray.length) {
  3011             var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
  3012 
  3013             // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
  3014             // whereas a regular applyBindings won't introduce new memoized nodes
  3015             invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
  3016                 ko.applyBindings(bindingContext, node);
  3017             });
  3018             invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
  3019                 ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
  3020             });
  3021         }
  3022     }
  3023 
  3024     function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
  3025         return nodeOrNodeArray.nodeType ? nodeOrNodeArray
  3026                                         : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
  3027                                         : null;
  3028     }
  3029 
  3030     function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
  3031         options = options || {};
  3032         var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
  3033         var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
  3034         var templateEngineToUse = (options['templateEngine'] || _templateEngine);
  3035         ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
  3036         var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
  3037 
  3038         // Loosely check result is an array of DOM nodes
  3039         if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
  3040             throw new Error("Template engine must return an array of DOM nodes");
  3041 
  3042         var haveAddedNodesToParent = false;
  3043         switch (renderMode) {
  3044             case "replaceChildren":
  3045                 ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
  3046                 haveAddedNodesToParent = true;
  3047                 break;
  3048             case "replaceNode":
  3049                 ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
  3050                 haveAddedNodesToParent = true;
  3051                 break;
  3052             case "ignoreTargetNode": break;
  3053             default:
  3054                 throw new Error("Unknown renderMode: " + renderMode);
  3055         }
  3056 
  3057         if (haveAddedNodesToParent) {
  3058             activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
  3059             if (options['afterRender'])
  3060                 ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
  3061         }
  3062 
  3063         return renderedNodesArray;
  3064     }
  3065 
  3066     ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
  3067         options = options || {};
  3068         if ((options['templateEngine'] || _templateEngine) == undefined)
  3069             throw new Error("Set a template engine before calling renderTemplate");
  3070         renderMode = renderMode || "replaceChildren";
  3071 
  3072         if (targetNodeOrNodeArray) {
  3073             var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
  3074 
  3075             var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
  3076             var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
  3077 
  3078             return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
  3079                 function () {
  3080                     // Ensure we've got a proper binding context to work with
  3081                     var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
  3082                         ? dataOrBindingContext
  3083                         : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
  3084 
  3085                     // Support selecting template as a function of the data being rendered
  3086                     var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
  3087 
  3088                     var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
  3089                     if (renderMode == "replaceNode") {
  3090                         targetNodeOrNodeArray = renderedNodesArray;
  3091                         firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
  3092                     }
  3093                 },
  3094                 null,
  3095                 { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
  3096             );
  3097         } else {
  3098             // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node
  3099             return ko.memoization.memoize(function (domNode) {
  3100                 ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
  3101             });
  3102         }
  3103     };
  3104 
  3105     ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
  3106         // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
  3107         // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
  3108         var arrayItemContext;
  3109 
  3110         // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
  3111         var executeTemplateForArrayItem = function (arrayValue, index) {
  3112             // Support selecting template as a function of the data being rendered
  3113             arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
  3114             arrayItemContext['$index'] = index;
  3115             var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
  3116             return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
  3117         }
  3118 
  3119         // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
  3120         var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
  3121             activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
  3122             if (options['afterRender'])
  3123                 options['afterRender'](addedNodesArray, arrayValue);
  3124         };
  3125 
  3126         return ko.dependentObservable(function () {
  3127             var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
  3128             if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
  3129                 unwrappedArray = [unwrappedArray];
  3130 
  3131             // Filter out any entries marked as destroyed
  3132             var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
  3133                 return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
  3134             });
  3135 
  3136             // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
  3137             // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
  3138             ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
  3139 
  3140         }, null, { disposeWhenNodeIsRemoved: targetNode });
  3141     };
  3142 
  3143     var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
  3144     function disposeOldComputedAndStoreNewOne(element, newComputed) {
  3145         var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
  3146         if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
  3147             oldComputed.dispose();
  3148         ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
  3149     }
  3150 
  3151     ko.bindingHandlers['template'] = {
  3152         'init': function(element, valueAccessor) {
  3153             // Support anonymous templates
  3154             var bindingValue = ko.utils.unwrapObservable(valueAccessor());
  3155             if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
  3156                 // It's an anonymous template - store the element contents, then clear the element
  3157                 var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
  3158                     container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
  3159                 new ko.templateSources.anonymousTemplate(element)['nodes'](container);
  3160             }
  3161             return { 'controlsDescendantBindings': true };
  3162         },
  3163         'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  3164             var templateName = ko.utils.unwrapObservable(valueAccessor()),
  3165                 options = {},
  3166                 shouldDisplay = true,
  3167                 dataValue,
  3168                 templateComputed = null;
  3169 
  3170             if (typeof templateName != "string") {
  3171                 options = templateName;
  3172                 templateName = options['name'];
  3173 
  3174                 // Support "if"/"ifnot" conditions
  3175                 if ('if' in options)
  3176                     shouldDisplay = ko.utils.unwrapObservable(options['if']);
  3177                 if (shouldDisplay && 'ifnot' in options)
  3178                     shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
  3179 
  3180                 dataValue = ko.utils.unwrapObservable(options['data']);
  3181             }
  3182 
  3183             if ('foreach' in options) {
  3184                 // Render once for each data point (treating data set as empty if shouldDisplay==false)
  3185                 var dataArray = (shouldDisplay && options['foreach']) || [];
  3186                 templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
  3187             } else if (!shouldDisplay) {
  3188                 ko.virtualElements.emptyNode(element);
  3189             } else {
  3190                 // Render once for this single data point (or use the viewModel if no data was provided)
  3191                 var innerBindingContext = ('data' in options) ?
  3192                     bindingContext['createChildContext'](dataValue, options['as']) :  // Given an explitit 'data' value, we create a child binding context for it
  3193                     bindingContext;                                                        // Given no explicit 'data' value, we retain the same binding context
  3194                 templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
  3195             }
  3196 
  3197             // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
  3198             disposeOldComputedAndStoreNewOne(element, templateComputed);
  3199         }
  3200     };
  3201 
  3202     // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
  3203     ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
  3204         var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
  3205 
  3206         if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
  3207             return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)
  3208 
  3209         if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
  3210             return null; // Named templates can be rewritten, so return "no error"
  3211         return "This template engine does not support anonymous templates nested within its templates";
  3212     };
  3213 
  3214     ko.virtualElements.allowedBindings['template'] = true;
  3215 })();
  3216 
  3217 ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
  3218 ko.exportSymbol('renderTemplate', ko.renderTemplate);
  3219 
  3220 ko.utils.compareArrays = (function () {
  3221     var statusNotInOld = 'added', statusNotInNew = 'deleted';
  3222 
  3223     // Simple calculation based on Levenshtein distance.
  3224     function compareArrays(oldArray, newArray, dontLimitMoves) {
  3225         oldArray = oldArray || [];
  3226         newArray = newArray || [];
  3227 
  3228         if (oldArray.length <= newArray.length)
  3229             return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
  3230         else
  3231             return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
  3232     }
  3233 
  3234     function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
  3235         var myMin = Math.min,
  3236             myMax = Math.max,
  3237             editDistanceMatrix = [],
  3238             smlIndex, smlIndexMax = smlArray.length,
  3239             bigIndex, bigIndexMax = bigArray.length,
  3240             compareRange = (bigIndexMax - smlIndexMax) || 1,
  3241             maxDistance = smlIndexMax + bigIndexMax + 1,
  3242             thisRow, lastRow,
  3243             bigIndexMaxForRow, bigIndexMinForRow;
  3244 
  3245         for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
  3246             lastRow = thisRow;
  3247             editDistanceMatrix.push(thisRow = []);
  3248             bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
  3249             bigIndexMinForRow = myMax(0, smlIndex - 1);
  3250             for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
  3251                 if (!bigIndex)
  3252                     thisRow[bigIndex] = smlIndex + 1;
  3253                 else if (!smlIndex)  // Top row - transform empty array into new array via additions
  3254                     thisRow[bigIndex] = bigIndex + 1;
  3255                 else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
  3256                     thisRow[bigIndex] = lastRow[bigIndex - 1];                  // copy value (no edit)
  3257                 else {
  3258                     var northDistance = lastRow[bigIndex] || maxDistance;       // not in big (deletion)
  3259                     var westDistance = thisRow[bigIndex - 1] || maxDistance;    // not in small (addition)
  3260                     thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
  3261                 }
  3262             }
  3263         }
  3264 
  3265         var editScript = [], meMinusOne, notInSml = [], notInBig = [];
  3266         for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
  3267             meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
  3268             if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
  3269                 notInSml.push(editScript[editScript.length] = {     // added
  3270                     'status': statusNotInSml,
  3271                     'value': bigArray[--bigIndex],
  3272                     'index': bigIndex });
  3273             } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
  3274                 notInBig.push(editScript[editScript.length] = {     // deleted
  3275                     'status': statusNotInBig,
  3276                     'value': smlArray[--smlIndex],
  3277                     'index': smlIndex });
  3278             } else {
  3279                 editScript.push({
  3280                     'status': "retained",
  3281                     'value': bigArray[--bigIndex] });
  3282                 --smlIndex;
  3283             }
  3284         }
  3285 
  3286         if (notInSml.length && notInBig.length) {
  3287             // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
  3288             // smlIndexMax keeps the time complexity of this algorithm linear.
  3289             var limitFailedCompares = smlIndexMax * 10, failedCompares,
  3290                 a, d, notInSmlItem, notInBigItem;
  3291             // Go through the items that have been added and deleted and try to find matches between them.
  3292             for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
  3293                 for (d = 0; notInBigItem = notInBig[d]; d++) {
  3294                     if (notInSmlItem['value'] === notInBigItem['value']) {
  3295                         notInSmlItem['moved'] = notInBigItem['index'];
  3296                         notInBigItem['moved'] = notInSmlItem['index'];
  3297                         notInBig.splice(d,1);       // This item is marked as moved; so remove it from notInBig list
  3298                         failedCompares = d = 0;     // Reset failed compares count because we're checking for consecutive failures
  3299                         break;
  3300                     }
  3301                 }
  3302                 failedCompares += d;
  3303             }
  3304         }
  3305         return editScript.reverse();
  3306     }
  3307 
  3308     return compareArrays;
  3309 })();
  3310 
  3311 ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
  3312 
  3313 (function () {
  3314     // Objective:
  3315     // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
  3316     //   map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
  3317     // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
  3318     //   so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
  3319     //   previously mapped - retain those nodes, and just insert/delete other ones
  3320 
  3321     // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
  3322     // You can use this, for example, to activate bindings on those nodes.
  3323 
  3324     function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
  3325         // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
  3326         // them against what is in the DOM right now. It may be that some of the nodes have already been removed from the document,
  3327         // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
  3328         // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
  3329         // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
  3330         //
  3331         // Rules:
  3332         //   [A] Any leading nodes that aren't in the document any more should be ignored
  3333         //       These most likely correspond to memoization nodes that were already removed during binding
  3334         //       See https://github.com/SteveSanderson/knockout/pull/440
  3335         //   [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
  3336         //       have already been removed, and include any nodes that have been inserted among the previous collection
  3337 
  3338         // Rule [A]
  3339         while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
  3340             contiguousNodeArray.splice(0, 1);
  3341 
  3342         // Rule [B]
  3343         if (contiguousNodeArray.length > 1) {
  3344             // Build up the actual new contiguous node set
  3345             var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
  3346             while (current !== last) {
  3347                 current = current.nextSibling;
  3348                 if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
  3349                     return;
  3350                 newContiguousSet.push(current);
  3351             }
  3352 
  3353             // ... then mutate the input array to match this.
  3354             // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
  3355             Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
  3356         }
  3357         return contiguousNodeArray;
  3358     }
  3359 
  3360     function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
  3361         // Map this array value inside a dependentObservable so we re-map when any dependency changes
  3362         var mappedNodes = [];
  3363         var dependentObservable = ko.dependentObservable(function() {
  3364             var newMappedNodes = mapping(valueToMap, index) || [];
  3365 
  3366             // On subsequent evaluations, just replace the previously-inserted DOM nodes
  3367             if (mappedNodes.length > 0) {
  3368                 ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
  3369                 if (callbackAfterAddingNodes)
  3370                     ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
  3371             }
  3372 
  3373             // Replace the contents of the mappedNodes array, thereby updating the record
  3374             // of which nodes would be deleted if valueToMap was itself later removed
  3375             mappedNodes.splice(0, mappedNodes.length);
  3376             ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
  3377         }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
  3378         return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
  3379     }
  3380 
  3381     var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
  3382 
  3383     ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
  3384         // Compare the provided array against the previous one
  3385         array = array || [];
  3386         options = options || {};
  3387         var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
  3388         var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
  3389         var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
  3390         var editScript = ko.utils.compareArrays(lastArray, array);
  3391 
  3392         // Build the new mapping result
  3393         var newMappingResult = [];
  3394         var lastMappingResultIndex = 0;
  3395         var newMappingResultIndex = 0;
  3396 
  3397         var nodesToDelete = [];
  3398         var itemsToProcess = [];
  3399         var itemsForBeforeRemoveCallbacks = [];
  3400         var itemsForMoveCallbacks = [];
  3401         var itemsForAfterAddCallbacks = [];
  3402         var mapData;
  3403 
  3404         function itemMovedOrRetained(editScriptIndex, oldPosition) {
  3405             mapData = lastMappingResult[oldPosition];
  3406             if (newMappingResultIndex !== oldPosition)
  3407                 itemsForMoveCallbacks[editScriptIndex] = mapData;
  3408             // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
  3409             mapData.indexObservable(newMappingResultIndex++);
  3410             fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
  3411             newMappingResult.push(mapData);
  3412             itemsToProcess.push(mapData);
  3413         }
  3414 
  3415         function callCallback(callback, items) {
  3416             if (callback) {
  3417                 for (var i = 0, n = items.length; i < n; i++) {
  3418                     if (items[i]) {
  3419                         ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
  3420                             callback(node, i, items[i].arrayEntry);
  3421                         });
  3422                     }
  3423                 }
  3424             }
  3425         }
  3426 
  3427         for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
  3428             movedIndex = editScriptItem['moved'];
  3429             switch (editScriptItem['status']) {
  3430                 case "deleted":
  3431                     if (movedIndex === undefined) {
  3432                         mapData = lastMappingResult[lastMappingResultIndex];
  3433 
  3434                         // Stop tracking changes to the mapping for these nodes
  3435                         if (mapData.dependentObservable)
  3436                             mapData.dependentObservable.dispose();
  3437 
  3438                         // Queue these nodes for later removal
  3439                         nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
  3440                         if (options['beforeRemove']) {
  3441                             itemsForBeforeRemoveCallbacks[i] = mapData;
  3442                             itemsToProcess.push(mapData);
  3443                         }
  3444                     }
  3445                     lastMappingResultIndex++;
  3446                     break;
  3447 
  3448                 case "retained":
  3449                     itemMovedOrRetained(i, lastMappingResultIndex++);
  3450                     break;
  3451 
  3452                 case "added":
  3453                     if (movedIndex !== undefined) {
  3454                         itemMovedOrRetained(i, movedIndex);
  3455                     } else {
  3456                         mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
  3457                         newMappingResult.push(mapData);
  3458                         itemsToProcess.push(mapData);
  3459                         if (!isFirstExecution)
  3460                             itemsForAfterAddCallbacks[i] = mapData;
  3461                     }
  3462                     break;
  3463             }
  3464         }
  3465 
  3466         // Call beforeMove first before any changes have been made to the DOM
  3467         callCallback(options['beforeMove'], itemsForMoveCallbacks);
  3468 
  3469         // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
  3470         ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
  3471 
  3472         // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
  3473         for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
  3474             // Get nodes for newly added items
  3475             if (!mapData.mappedNodes)
  3476                 ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
  3477 
  3478             // Put nodes in the right place if they aren't there already
  3479             for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
  3480                 if (node !== nextNode)
  3481                     ko.virtualElements.insertAfter(domNode, node, lastNode);
  3482             }
  3483 
  3484             // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
  3485             if (!mapData.initialized && callbackAfterAddingNodes) {
  3486                 callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
  3487                 mapData.initialized = true;
  3488             }
  3489         }
  3490 
  3491         // If there's a beforeRemove callback, call it after reordering.
  3492         // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
  3493         // some sort of animation, which is why we first reorder the nodes that will be removed. If the
  3494         // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
  3495         // Perhaps we'll make that change in the future if this scenario becomes more common.
  3496         callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
  3497 
  3498         // Finally call afterMove and afterAdd callbacks
  3499         callCallback(options['afterMove'], itemsForMoveCallbacks);
  3500         callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
  3501 
  3502         // Store a copy of the array items we just considered so we can difference it next time
  3503         ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
  3504     }
  3505 })();
  3506 
  3507 ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
  3508 ko.nativeTemplateEngine = function () {
  3509     this['allowTemplateRewriting'] = false;
  3510 }
  3511 
  3512 ko.nativeTemplateEngine.prototype = new ko.templateEngine();
  3513 ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
  3514     var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
  3515         templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
  3516         templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
  3517 
  3518     if (templateNodes) {
  3519         return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
  3520     } else {
  3521         var templateText = templateSource['text']();
  3522         return ko.utils.parseHtmlFragment(templateText);
  3523     }
  3524 };
  3525 
  3526 ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
  3527 ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
  3528 
  3529 ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
  3530 (function() {
  3531     ko.jqueryTmplTemplateEngine = function () {
  3532         // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
  3533         // doesn't expose a version number, so we have to infer it.
  3534         // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
  3535         // which KO internally refers to as version "2", so older versions are no longer detected.
  3536         var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
  3537             if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
  3538                 return 0;
  3539             // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
  3540             try {
  3541                 if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
  3542                     // Since 1.0.0pre, custom tags should append markup to an array called "__"
  3543                     return 2; // Final version of jquery.tmpl
  3544                 }
  3545             } catch(ex) { /* Apparently not the version we were looking for */ }
  3546 
  3547             return 1; // Any older version that we don't support
  3548         })();
  3549 
  3550         function ensureHasReferencedJQueryTemplates() {
  3551             if (jQueryTmplVersion < 2)
  3552                 throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
  3553         }
  3554 
  3555         function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
  3556             return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
  3557         }
  3558 
  3559         this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
  3560             options = options || {};
  3561             ensureHasReferencedJQueryTemplates();
  3562 
  3563             // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
  3564             var precompiled = templateSource['data']('precompiled');
  3565             if (!precompiled) {
  3566                 var templateText = templateSource['text']() || "";
  3567                 // Wrap in "with($whatever.koBindingContext) { ... }"
  3568                 templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
  3569 
  3570                 precompiled = jQuery['template'](null, templateText);
  3571                 templateSource['data']('precompiled', precompiled);
  3572             }
  3573 
  3574             var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
  3575             var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
  3576 
  3577             var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
  3578             resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
  3579 
  3580             jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
  3581             return resultNodes;
  3582         };
  3583 
  3584         this['createJavaScriptEvaluatorBlock'] = function(script) {
  3585             return "{{ko_code ((function() { return " + script + " })()) }}";
  3586         };
  3587 
  3588         this['addTemplate'] = function(templateName, templateMarkup) {
  3589             document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
  3590         };
  3591 
  3592         if (jQueryTmplVersion > 0) {
  3593             jQuery['tmpl']['tag']['ko_code'] = {
  3594                 open: "__.push($1 || '');"
  3595             };
  3596             jQuery['tmpl']['tag']['ko_with'] = {
  3597                 open: "with($1) {",
  3598                 close: "} "
  3599             };
  3600         }
  3601     };
  3602 
  3603     ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
  3604 
  3605     // Use this one by default *only if jquery.tmpl is referenced*
  3606     var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
  3607     if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
  3608         ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
  3609 
  3610     ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
  3611 })();
  3612 });
  3613 })(window,document,navigator,window["jQuery"]);
  3614 })();