PageRenderTime 113ms CodeModel.GetById 20ms app.highlight 73ms RepoModel.GetById 2ms app.codeStats 1ms

/Scripts/knockout-2.2.0.debug.js

https://bitbucket.org/parivedasolutions/attributeroutingtest
JavaScript | 3577 lines | 3033 code | 296 blank | 248 comment | 498 complexity | 53ace77b7c95fc1ec9887939788244b0 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// Knockout JavaScript library v2.2.0
   2// (c) Steven Sanderson - http://knockoutjs.com/
   3// License: MIT (http://www.opensource.org/licenses/mit-license.php)
   4
   5(function(){
   6var DEBUG=true;
   7(function(window,document,navigator,jQuery,undefined){
   8!function(factory) {
   9    // Support three module loading scenarios
  10    if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
  11        // [1] CommonJS/Node.js
  12        var target = module['exports'] || exports; // module.exports is for Node.js
  13        factory(target);
  14    } else if (typeof define === 'function' && define['amd']) {
  15        // [2] AMD anonymous module
  16        define(['exports'], factory);
  17    } else {
  18        // [3] No module loader (plain <script> tag) - put directly in global namespace
  19        factory(window['ko'] = {});
  20    }
  21}(function(koExports){
  22// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
  23// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
  24var ko = typeof koExports !== 'undefined' ? koExports : {};
  25// Google Closure Compiler helpers (used only to make the minified file smaller)
  26ko.exportSymbol = function(koPath, object) {
  27	var tokens = koPath.split(".");
  28
  29	// In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
  30	// At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
  31	var target = ko;
  32
  33	for (var i = 0; i < tokens.length - 1; i++)
  34		target = target[tokens[i]];
  35	target[tokens[tokens.length - 1]] = object;
  36};
  37ko.exportProperty = function(owner, publicName, object) {
  38  owner[publicName] = object;
  39};
  40ko.version = "2.2.0";
  41
  42ko.exportSymbol('version', ko.version);
  43ko.utils = new (function () {
  44    var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
  45
  46    // 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)
  47    var knownEvents = {}, knownEventTypesByEventName = {};
  48    var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
  49    knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
  50    knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
  51    for (var eventType in knownEvents) {
  52        var knownEventsForType = knownEvents[eventType];
  53        if (knownEventsForType.length) {
  54            for (var i = 0, j = knownEventsForType.length; i < j; i++)
  55                knownEventTypesByEventName[knownEventsForType[i]] = eventType;
  56        }
  57    }
  58    var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
  59
  60    // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
  61    // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
  62    // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
  63    // If there is a future need to detect specific versions of IE10+, we will amend this.
  64    var ieVersion = (function() {
  65        var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
  66
  67        // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
  68        while (
  69            div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
  70            iElems[0]
  71        );
  72        return version > 4 ? version : undefined;
  73    }());
  74    var isIe6 = ieVersion === 6,
  75        isIe7 = ieVersion === 7;
  76
  77    function isClickOnCheckableElement(element, eventType) {
  78        if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
  79        if (eventType.toLowerCase() != "click") return false;
  80        var inputType = element.type;
  81        return (inputType == "checkbox") || (inputType == "radio");
  82    }
  83
  84    return {
  85        fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
  86
  87        arrayForEach: function (array, action) {
  88            for (var i = 0, j = array.length; i < j; i++)
  89                action(array[i]);
  90        },
  91
  92        arrayIndexOf: function (array, item) {
  93            if (typeof Array.prototype.indexOf == "function")
  94                return Array.prototype.indexOf.call(array, item);
  95            for (var i = 0, j = array.length; i < j; i++)
  96                if (array[i] === item)
  97                    return i;
  98            return -1;
  99        },
 100
 101        arrayFirst: function (array, predicate, predicateOwner) {
 102            for (var i = 0, j = array.length; i < j; i++)
 103                if (predicate.call(predicateOwner, array[i]))
 104                    return array[i];
 105            return null;
 106        },
 107
 108        arrayRemoveItem: function (array, itemToRemove) {
 109            var index = ko.utils.arrayIndexOf(array, itemToRemove);
 110            if (index >= 0)
 111                array.splice(index, 1);
 112        },
 113
 114        arrayGetDistinctValues: function (array) {
 115            array = array || [];
 116            var result = [];
 117            for (var i = 0, j = array.length; i < j; i++) {
 118                if (ko.utils.arrayIndexOf(result, array[i]) < 0)
 119                    result.push(array[i]);
 120            }
 121            return result;
 122        },
 123
 124        arrayMap: function (array, mapping) {
 125            array = array || [];
 126            var result = [];
 127            for (var i = 0, j = array.length; i < j; i++)
 128                result.push(mapping(array[i]));
 129            return result;
 130        },
 131
 132        arrayFilter: function (array, predicate) {
 133            array = array || [];
 134            var result = [];
 135            for (var i = 0, j = array.length; i < j; i++)
 136                if (predicate(array[i]))
 137                    result.push(array[i]);
 138            return result;
 139        },
 140
 141        arrayPushAll: function (array, valuesToPush) {
 142            if (valuesToPush instanceof Array)
 143                array.push.apply(array, valuesToPush);
 144            else
 145                for (var i = 0, j = valuesToPush.length; i < j; i++)
 146                    array.push(valuesToPush[i]);
 147            return array;
 148        },
 149
 150        extend: function (target, source) {
 151            if (source) {
 152                for(var prop in source) {
 153                    if(source.hasOwnProperty(prop)) {
 154                        target[prop] = source[prop];
 155                    }
 156                }
 157            }
 158            return target;
 159        },
 160
 161        emptyDomNode: function (domNode) {
 162            while (domNode.firstChild) {
 163                ko.removeNode(domNode.firstChild);
 164            }
 165        },
 166
 167        moveCleanedNodesToContainerElement: function(nodes) {
 168            // Ensure it's a real array, as we're about to reparent the nodes and
 169            // we don't want the underlying collection to change while we're doing that.
 170            var nodesArray = ko.utils.makeArray(nodes);
 171
 172            var container = document.createElement('div');
 173            for (var i = 0, j = nodesArray.length; i < j; i++) {
 174                container.appendChild(ko.cleanNode(nodesArray[i]));
 175            }
 176            return container;
 177        },
 178
 179        cloneNodes: function (nodesArray, shouldCleanNodes) {
 180            for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
 181                var clonedNode = nodesArray[i].cloneNode(true);
 182                newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
 183            }
 184            return newNodesArray;
 185        },
 186
 187        setDomNodeChildren: function (domNode, childNodes) {
 188            ko.utils.emptyDomNode(domNode);
 189            if (childNodes) {
 190                for (var i = 0, j = childNodes.length; i < j; i++)
 191                    domNode.appendChild(childNodes[i]);
 192            }
 193        },
 194
 195        replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
 196            var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
 197            if (nodesToReplaceArray.length > 0) {
 198                var insertionPoint = nodesToReplaceArray[0];
 199                var parent = insertionPoint.parentNode;
 200                for (var i = 0, j = newNodesArray.length; i < j; i++)
 201                    parent.insertBefore(newNodesArray[i], insertionPoint);
 202                for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
 203                    ko.removeNode(nodesToReplaceArray[i]);
 204                }
 205            }
 206        },
 207
 208        setOptionNodeSelectionState: function (optionNode, isSelected) {
 209            // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
 210            if (ieVersion < 7)
 211                optionNode.setAttribute("selected", isSelected);
 212            else
 213                optionNode.selected = isSelected;
 214        },
 215
 216        stringTrim: function (string) {
 217            return (string || "").replace(stringTrimRegex, "");
 218        },
 219
 220        stringTokenize: function (string, delimiter) {
 221            var result = [];
 222            var tokens = (string || "").split(delimiter);
 223            for (var i = 0, j = tokens.length; i < j; i++) {
 224                var trimmed = ko.utils.stringTrim(tokens[i]);
 225                if (trimmed !== "")
 226                    result.push(trimmed);
 227            }
 228            return result;
 229        },
 230
 231        stringStartsWith: function (string, startsWith) {
 232            string = string || "";
 233            if (startsWith.length > string.length)
 234                return false;
 235            return string.substring(0, startsWith.length) === startsWith;
 236        },
 237
 238        domNodeIsContainedBy: function (node, containedByNode) {
 239            if (containedByNode.compareDocumentPosition)
 240                return (containedByNode.compareDocumentPosition(node) & 16) == 16;
 241            while (node != null) {
 242                if (node == containedByNode)
 243                    return true;
 244                node = node.parentNode;
 245            }
 246            return false;
 247        },
 248
 249        domNodeIsAttachedToDocument: function (node) {
 250            return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
 251        },
 252
 253        tagNameLower: function(element) {
 254            // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
 255            // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
 256            // we don't need to do the .toLowerCase() as it will always be lower case anyway.
 257            return element && element.tagName && element.tagName.toLowerCase();
 258        },
 259
 260        registerEventHandler: function (element, eventType, handler) {
 261            var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
 262            if (!mustUseAttachEvent && typeof jQuery != "undefined") {
 263                if (isClickOnCheckableElement(element, eventType)) {
 264                    // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
 265                    // it toggles the element checked state *after* the click event handlers run, whereas native
 266                    // click events toggle the checked state *before* the event handler.
 267                    // Fix this by intecepting the handler and applying the correct checkedness before it runs.
 268                    var originalHandler = handler;
 269                    handler = function(event, eventData) {
 270                        var jQuerySuppliedCheckedState = this.checked;
 271                        if (eventData)
 272                            this.checked = eventData.checkedStateBeforeEvent !== true;
 273                        originalHandler.call(this, event);
 274                        this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
 275                    };
 276                }
 277                jQuery(element)['bind'](eventType, handler);
 278            } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
 279                element.addEventListener(eventType, handler, false);
 280            else if (typeof element.attachEvent != "undefined")
 281                element.attachEvent("on" + eventType, function (event) {
 282                    handler.call(element, event);
 283                });
 284            else
 285                throw new Error("Browser doesn't support addEventListener or attachEvent");
 286        },
 287
 288        triggerEvent: function (element, eventType) {
 289            if (!(element && element.nodeType))
 290                throw new Error("element must be a DOM node when calling triggerEvent");
 291
 292            if (typeof jQuery != "undefined") {
 293                var eventData = [];
 294                if (isClickOnCheckableElement(element, eventType)) {
 295                    // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
 296                    eventData.push({ checkedStateBeforeEvent: element.checked });
 297                }
 298                jQuery(element)['trigger'](eventType, eventData);
 299            } else if (typeof document.createEvent == "function") {
 300                if (typeof element.dispatchEvent == "function") {
 301                    var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
 302                    var event = document.createEvent(eventCategory);
 303                    event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
 304                    element.dispatchEvent(event);
 305                }
 306                else
 307                    throw new Error("The supplied element doesn't support dispatchEvent");
 308            } else if (typeof element.fireEvent != "undefined") {
 309                // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
 310                // so to make it consistent, we'll do it manually here
 311                if (isClickOnCheckableElement(element, eventType))
 312                    element.checked = element.checked !== true;
 313                element.fireEvent("on" + eventType);
 314            }
 315            else
 316                throw new Error("Browser doesn't support triggering events");
 317        },
 318
 319        unwrapObservable: function (value) {
 320            return ko.isObservable(value) ? value() : value;
 321        },
 322
 323        peekObservable: function (value) {
 324            return ko.isObservable(value) ? value.peek() : value;
 325        },
 326
 327        toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
 328            if (classNames) {
 329                var cssClassNameRegex = /[\w-]+/g,
 330                    currentClassNames = node.className.match(cssClassNameRegex) || [];
 331                ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
 332                    var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
 333                    if (indexOfClass >= 0) {
 334                        if (!shouldHaveClass)
 335                            currentClassNames.splice(indexOfClass, 1);
 336                    } else {
 337                        if (shouldHaveClass)
 338                            currentClassNames.push(className);
 339                    }
 340                });
 341                node.className = currentClassNames.join(" ");
 342            }
 343        },
 344
 345        setTextContent: function(element, textContent) {
 346            var value = ko.utils.unwrapObservable(textContent);
 347            if ((value === null) || (value === undefined))
 348                value = "";
 349
 350            if (element.nodeType === 3) {
 351                element.data = value;
 352            } else {
 353                // We need there to be exactly one child: a text node.
 354                // If there are no children, more than one, or if it's not a text node,
 355                // we'll clear everything and create a single text node.
 356                var innerTextNode = ko.virtualElements.firstChild(element);
 357                if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
 358                    ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
 359                } else {
 360                    innerTextNode.data = value;
 361                }
 362
 363                ko.utils.forceRefresh(element);
 364            }
 365        },
 366
 367        setElementName: function(element, name) {
 368            element.name = name;
 369
 370            // Workaround IE 6/7 issue
 371            // - https://github.com/SteveSanderson/knockout/issues/197
 372            // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
 373            if (ieVersion <= 7) {
 374                try {
 375                    element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
 376                }
 377                catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
 378            }
 379        },
 380
 381        forceRefresh: function(node) {
 382            // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
 383            if (ieVersion >= 9) {
 384                // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
 385                var elem = node.nodeType == 1 ? node : node.parentNode;
 386                if (elem.style)
 387                    elem.style.zoom = elem.style.zoom;
 388            }
 389        },
 390
 391        ensureSelectElementIsRenderedCorrectly: function(selectElement) {
 392            // 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.
 393            // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
 394            if (ieVersion >= 9) {
 395                var originalWidth = selectElement.style.width;
 396                selectElement.style.width = 0;
 397                selectElement.style.width = originalWidth;
 398            }
 399        },
 400
 401        range: function (min, max) {
 402            min = ko.utils.unwrapObservable(min);
 403            max = ko.utils.unwrapObservable(max);
 404            var result = [];
 405            for (var i = min; i <= max; i++)
 406                result.push(i);
 407            return result;
 408        },
 409
 410        makeArray: function(arrayLikeObject) {
 411            var result = [];
 412            for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
 413                result.push(arrayLikeObject[i]);
 414            };
 415            return result;
 416        },
 417
 418        isIe6 : isIe6,
 419        isIe7 : isIe7,
 420        ieVersion : ieVersion,
 421
 422        getFormFields: function(form, fieldName) {
 423            var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
 424            var isMatchingField = (typeof fieldName == 'string')
 425                ? function(field) { return field.name === fieldName }
 426                : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
 427            var matches = [];
 428            for (var i = fields.length - 1; i >= 0; i--) {
 429                if (isMatchingField(fields[i]))
 430                    matches.push(fields[i]);
 431            };
 432            return matches;
 433        },
 434
 435        parseJson: function (jsonString) {
 436            if (typeof jsonString == "string") {
 437                jsonString = ko.utils.stringTrim(jsonString);
 438                if (jsonString) {
 439                    if (window.JSON && window.JSON.parse) // Use native parsing where available
 440                        return window.JSON.parse(jsonString);
 441                    return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
 442                }
 443            }
 444            return null;
 445        },
 446
 447        stringifyJson: function (data, replacer, space) {   // replacer and space are optional
 448            if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
 449                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");
 450            return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
 451        },
 452
 453        postJson: function (urlOrForm, data, options) {
 454            options = options || {};
 455            var params = options['params'] || {};
 456            var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
 457            var url = urlOrForm;
 458
 459            // If we were given a form, use its 'action' URL and pick out any requested field values
 460            if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
 461                var originalForm = urlOrForm;
 462                url = originalForm.action;
 463                for (var i = includeFields.length - 1; i >= 0; i--) {
 464                    var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
 465                    for (var j = fields.length - 1; j >= 0; j--)
 466                        params[fields[j].name] = fields[j].value;
 467                }
 468            }
 469
 470            data = ko.utils.unwrapObservable(data);
 471            var form = document.createElement("form");
 472            form.style.display = "none";
 473            form.action = url;
 474            form.method = "post";
 475            for (var key in data) {
 476                var input = document.createElement("input");
 477                input.name = key;
 478                input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
 479                form.appendChild(input);
 480            }
 481            for (var key in params) {
 482                var input = document.createElement("input");
 483                input.name = key;
 484                input.value = params[key];
 485                form.appendChild(input);
 486            }
 487            document.body.appendChild(form);
 488            options['submitter'] ? options['submitter'](form) : form.submit();
 489            setTimeout(function () { form.parentNode.removeChild(form); }, 0);
 490        }
 491    }
 492})();
 493
 494ko.exportSymbol('utils', ko.utils);
 495ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
 496ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
 497ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
 498ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
 499ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
 500ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
 501ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
 502ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
 503ko.exportSymbol('utils.extend', ko.utils.extend);
 504ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
 505ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
 506ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
 507ko.exportSymbol('utils.postJson', ko.utils.postJson);
 508ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
 509ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
 510ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
 511ko.exportSymbol('utils.range', ko.utils.range);
 512ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
 513ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
 514ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
 515
 516if (!Function.prototype['bind']) {
 517    // 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)
 518    // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
 519    Function.prototype['bind'] = function (object) {
 520        var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
 521        return function () {
 522            return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
 523        };
 524    };
 525}
 526
 527ko.utils.domData = new (function () {
 528    var uniqueId = 0;
 529    var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
 530    var dataStore = {};
 531    return {
 532        get: function (node, key) {
 533            var allDataForNode = ko.utils.domData.getAll(node, false);
 534            return allDataForNode === undefined ? undefined : allDataForNode[key];
 535        },
 536        set: function (node, key, value) {
 537            if (value === undefined) {
 538                // Make sure we don't actually create a new domData key if we are actually deleting a value
 539                if (ko.utils.domData.getAll(node, false) === undefined)
 540                    return;
 541            }
 542            var allDataForNode = ko.utils.domData.getAll(node, true);
 543            allDataForNode[key] = value;
 544        },
 545        getAll: function (node, createIfNotFound) {
 546            var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
 547            var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
 548            if (!hasExistingDataStore) {
 549                if (!createIfNotFound)
 550                    return undefined;
 551                dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
 552                dataStore[dataStoreKey] = {};
 553            }
 554            return dataStore[dataStoreKey];
 555        },
 556        clear: function (node) {
 557            var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
 558            if (dataStoreKey) {
 559                delete dataStore[dataStoreKey];
 560                node[dataStoreKeyExpandoPropertyName] = null;
 561                return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
 562            }
 563            return false;
 564        }
 565    }
 566})();
 567
 568ko.exportSymbol('utils.domData', ko.utils.domData);
 569ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
 570
 571ko.utils.domNodeDisposal = new (function () {
 572    var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
 573    var cleanableNodeTypes = { 1: true, 8: true, 9: true };       // Element, Comment, Document
 574    var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
 575
 576    function getDisposeCallbacksCollection(node, createIfNotFound) {
 577        var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
 578        if ((allDisposeCallbacks === undefined) && createIfNotFound) {
 579            allDisposeCallbacks = [];
 580            ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
 581        }
 582        return allDisposeCallbacks;
 583    }
 584    function destroyCallbacksCollection(node) {
 585        ko.utils.domData.set(node, domDataKey, undefined);
 586    }
 587
 588    function cleanSingleNode(node) {
 589        // Run all the dispose callbacks
 590        var callbacks = getDisposeCallbacksCollection(node, false);
 591        if (callbacks) {
 592            callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
 593            for (var i = 0; i < callbacks.length; i++)
 594                callbacks[i](node);
 595        }
 596
 597        // Also erase the DOM data
 598        ko.utils.domData.clear(node);
 599
 600        // Special support for jQuery here because it's so commonly used.
 601        // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
 602        // so notify it to tear down any resources associated with the node & descendants here.
 603        if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
 604            jQuery['cleanData']([node]);
 605
 606        // Also clear any immediate-child comment nodes, as these wouldn't have been found by
 607        // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
 608        if (cleanableNodeTypesWithDescendants[node.nodeType])
 609            cleanImmediateCommentTypeChildren(node);
 610    }
 611
 612    function cleanImmediateCommentTypeChildren(nodeWithChildren) {
 613        var child, nextChild = nodeWithChildren.firstChild;
 614        while (child = nextChild) {
 615            nextChild = child.nextSibling;
 616            if (child.nodeType === 8)
 617                cleanSingleNode(child);
 618        }
 619    }
 620
 621    return {
 622        addDisposeCallback : function(node, callback) {
 623            if (typeof callback != "function")
 624                throw new Error("Callback must be a function");
 625            getDisposeCallbacksCollection(node, true).push(callback);
 626        },
 627
 628        removeDisposeCallback : function(node, callback) {
 629            var callbacksCollection = getDisposeCallbacksCollection(node, false);
 630            if (callbacksCollection) {
 631                ko.utils.arrayRemoveItem(callbacksCollection, callback);
 632                if (callbacksCollection.length == 0)
 633                    destroyCallbacksCollection(node);
 634            }
 635        },
 636
 637        cleanNode : function(node) {
 638            // First clean this node, where applicable
 639            if (cleanableNodeTypes[node.nodeType]) {
 640                cleanSingleNode(node);
 641
 642                // ... then its descendants, where applicable
 643                if (cleanableNodeTypesWithDescendants[node.nodeType]) {
 644                    // Clone the descendants list in case it changes during iteration
 645                    var descendants = [];
 646                    ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
 647                    for (var i = 0, j = descendants.length; i < j; i++)
 648                        cleanSingleNode(descendants[i]);
 649                }
 650            }
 651            return node;
 652        },
 653
 654        removeNode : function(node) {
 655            ko.cleanNode(node);
 656            if (node.parentNode)
 657                node.parentNode.removeChild(node);
 658        }
 659    }
 660})();
 661ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
 662ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
 663ko.exportSymbol('cleanNode', ko.cleanNode);
 664ko.exportSymbol('removeNode', ko.removeNode);
 665ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
 666ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
 667ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
 668(function () {
 669    var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
 670
 671    function simpleHtmlParse(html) {
 672        // Based on jQuery's "clean" function, but only accounting for table-related elements.
 673        // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
 674
 675        // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
 676        // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
 677        // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
 678        // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
 679
 680        // Trim whitespace, otherwise indexOf won't work as expected
 681        var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
 682
 683        // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
 684        var wrap = tags.match(/^<(thead|tbody|tfoot)/)              && [1, "<table>", "</table>"] ||
 685                   !tags.indexOf("<tr")                             && [2, "<table><tbody>", "</tbody></table>"] ||
 686                   (!tags.indexOf("<td") || !tags.indexOf("<th"))   && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
 687                   /* anything else */                                 [0, "", ""];
 688
 689        // Go to html and back, then peel off extra wrappers
 690        // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
 691        var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
 692        if (typeof window['innerShiv'] == "function") {
 693            div.appendChild(window['innerShiv'](markup));
 694        } else {
 695            div.innerHTML = markup;
 696        }
 697
 698        // Move to the right depth
 699        while (wrap[0]--)
 700            div = div.lastChild;
 701
 702        return ko.utils.makeArray(div.lastChild.childNodes);
 703    }
 704
 705    function jQueryHtmlParse(html) {
 706        var elems = jQuery['clean']([html]);
 707
 708        // 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.
 709        // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
 710        // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
 711        if (elems && elems[0]) {
 712            // Find the top-most parent element that's a direct child of a document fragment
 713            var elem = elems[0];
 714            while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
 715                elem = elem.parentNode;
 716            // ... then detach it
 717            if (elem.parentNode)
 718                elem.parentNode.removeChild(elem);
 719        }
 720
 721        return elems;
 722    }
 723
 724    ko.utils.parseHtmlFragment = function(html) {
 725        return typeof jQuery != 'undefined' ? jQueryHtmlParse(html)   // As below, benefit from jQuery's optimisations where possible
 726                                            : simpleHtmlParse(html);  // ... otherwise, this simple logic will do in most common cases.
 727    };
 728
 729    ko.utils.setHtml = function(node, html) {
 730        ko.utils.emptyDomNode(node);
 731
 732        // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
 733        html = ko.utils.unwrapObservable(html);
 734
 735        if ((html !== null) && (html !== undefined)) {
 736            if (typeof html != 'string')
 737                html = html.toString();
 738
 739            // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
 740            // for example <tr> elements which are not normally allowed to exist on their own.
 741            // If you've referenced jQuery we'll use that rather than duplicating its code.
 742            if (typeof jQuery != 'undefined') {
 743                jQuery(node)['html'](html);
 744            } else {
 745                // ... otherwise, use KO's own parsing logic.
 746                var parsedNodes = ko.utils.parseHtmlFragment(html);
 747                for (var i = 0; i < parsedNodes.length; i++)
 748                    node.appendChild(parsedNodes[i]);
 749            }
 750        }
 751    };
 752})();
 753
 754ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
 755ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
 756
 757ko.memoization = (function () {
 758    var memos = {};
 759
 760    function randomMax8HexChars() {
 761        return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
 762    }
 763    function generateRandomId() {
 764        return randomMax8HexChars() + randomMax8HexChars();
 765    }
 766    function findMemoNodes(rootNode, appendToArray) {
 767        if (!rootNode)
 768            return;
 769        if (rootNode.nodeType == 8) {
 770            var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
 771            if (memoId != null)
 772                appendToArray.push({ domNode: rootNode, memoId: memoId });
 773        } else if (rootNode.nodeType == 1) {
 774            for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
 775                findMemoNodes(childNodes[i], appendToArray);
 776        }
 777    }
 778
 779    return {
 780        memoize: function (callback) {
 781            if (typeof callback != "function")
 782                throw new Error("You can only pass a function to ko.memoization.memoize()");
 783            var memoId = generateRandomId();
 784            memos[memoId] = callback;
 785            return "<!--[ko_memo:" + memoId + "]-->";
 786        },
 787
 788        unmemoize: function (memoId, callbackParams) {
 789            var callback = memos[memoId];
 790            if (callback === undefined)
 791                throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
 792            try {
 793                callback.apply(null, callbackParams || []);
 794                return true;
 795            }
 796            finally { delete memos[memoId]; }
 797        },
 798
 799        unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
 800            var memos = [];
 801            findMemoNodes(domNode, memos);
 802            for (var i = 0, j = memos.length; i < j; i++) {
 803                var node = memos[i].domNode;
 804                var combinedParams = [node];
 805                if (extraCallbackParamsArray)
 806                    ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
 807                ko.memoization.unmemoize(memos[i].memoId, combinedParams);
 808                node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
 809                if (node.parentNode)
 810                    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)
 811            }
 812        },
 813
 814        parseMemoText: function (memoText) {
 815            var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
 816            return match ? match[1] : null;
 817        }
 818    };
 819})();
 820
 821ko.exportSymbol('memoization', ko.memoization);
 822ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
 823ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
 824ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
 825ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
 826ko.extenders = {
 827    'throttle': function(target, timeout) {
 828        // Throttling means two things:
 829
 830        // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
 831        //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
 832        target['throttleEvaluation'] = timeout;
 833
 834        // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
 835        //     so the target cannot change value synchronously or faster than a certain rate
 836        var writeTimeoutInstance = null;
 837        return ko.dependentObservable({
 838            'read': target,
 839            'write': function(value) {
 840                clearTimeout(writeTimeoutInstance);
 841                writeTimeoutInstance = setTimeout(function() {
 842                    target(value);
 843                }, timeout);
 844            }
 845        });
 846    },
 847
 848    'notify': function(target, notifyWhen) {
 849        target["equalityComparer"] = notifyWhen == "always"
 850            ? function() { return false } // Treat all values as not equal
 851            : ko.observable["fn"]["equalityComparer"];
 852        return target;
 853    }
 854};
 855
 856function applyExtenders(requestedExtenders) {
 857    var target = this;
 858    if (requestedExtenders) {
 859        for (var key in requestedExtenders) {
 860            var extenderHandler = ko.extenders[key];
 861            if (typeof extenderHandler == 'function') {
 862                target = extenderHandler(target, requestedExtenders[key]);
 863            }
 864        }
 865    }
 866    return target;
 867}
 868
 869ko.exportSymbol('extenders', ko.extenders);
 870
 871ko.subscription = function (target, callback, disposeCallback) {
 872    this.target = target;
 873    this.callback = callback;
 874    this.disposeCallback = disposeCallback;
 875    ko.exportProperty(this, 'dispose', this.dispose);
 876};
 877ko.subscription.prototype.dispose = function () {
 878    this.isDisposed = true;
 879    this.disposeCallback();
 880};
 881
 882ko.subscribable = function () {
 883    this._subscriptions = {};
 884
 885    ko.utils.extend(this, ko.subscribable['fn']);
 886    ko.exportProperty(this, 'subscribe', this.subscribe);
 887    ko.exportProperty(this, 'extend', this.extend);
 888    ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
 889}
 890
 891var defaultEvent = "change";
 892
 893ko.subscribable['fn'] = {
 894    subscribe: function (callback, callbackTarget, event) {
 895        event = event || defaultEvent;
 896        var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
 897
 898        var subscription = new ko.subscription(this, boundCallback, function () {
 899            ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
 900        }.bind(this));
 901
 902        if (!this._subscriptions[event])
 903            this._subscriptions[event] = [];
 904        this._subscriptions[event].push(subscription);
 905        return subscription;
 906    },
 907
 908    "notifySubscribers": function (valueToNotify, event) {
 909        event = event || defaultEvent;
 910        if (this._subscriptions[event]) {
 911            ko.dependencyDetection.ignore(function() {
 912                ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
 913                    // In case a subscription was disposed during the arrayForEach cycle, check
 914                    // for isDisposed on each subscription before invoking its callback
 915                    if (subscription && (subscription.isDisposed !== true))
 916                        subscription.callback(valueToNotify);
 917                });
 918            }, this);
 919        }
 920    },
 921
 922    getSubscriptionsCount: function () {
 923        var total = 0;
 924        for (var eventName in this._subscriptions) {
 925            if (this._subscriptions.hasOwnProperty(eventName))
 926                total += this._subscriptions[eventName].length;
 927        }
 928        return total;
 929    },
 930
 931    extend: applyExtenders
 932};
 933
 934
 935ko.isSubscribable = function (instance) {
 936    return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
 937};
 938
 939ko.exportSymbol('subscribable', ko.subscribable);
 940ko.exportSymbol('isSubscribable', ko.isSubscribable);
 941
 942ko.dependencyDetection = (function () {
 943    var _frames = [];
 944
 945    return {
 946        begin: function (callback) {
 947            _frames.push({ callback: callback, distinctDependencies:[] });
 948        },
 949
 950        end: function () {
 951            _frames.pop();
 952        },
 953
 954        registerDependency: function (subscribable) {
 955            if (!ko.isSubscribable(subscribable))
 956                throw new Error("Only subscribable things can act as dependencies");
 957            if (_frames.length > 0) {
 958                var topFrame = _frames[_frames.length - 1];
 959                if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
 960                    return;
 961                topFrame.distinctDependencies.push(subscribable);
 962                topFrame.callback(subscribable);
 963            }
 964        },
 965
 966        ignore: function(callback, callbackTarget, callbackArgs) {
 967            try {
 968                _frames.push(null);
 969                return callback.apply(callbackTarget, callbackArgs || []);
 970            } finally {
 971                _frames.pop();
 972            }
 973        }
 974    };
 975})();
 976var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
 977
 978ko.observable = function (initialValue) {
 979    var _latestValue = initialValue;
 980
 981    function observable() {
 982        if (arguments.length > 0) {
 983            // Write
 984
 985            // Ignore writes if the value hasn't changed
 986            if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
 987                observable.valueWillMutate();
 988                _latestValue = arguments[0];
 989                if (DEBUG) observable._latestValue = _latestValue;
 990                observable.valueHasMutated();
 991            }
 992            return this; // Permits chained assignments
 993        }
 994        else {
 995            // Read
 996            ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
 997            return _latestValue;
 998        }
 999    }
1000    if (DEBUG) observable._latestValue = _latestValue;
1001    ko.subscribable.call(observable);
1002    observable.peek = function() { return _latestValue };
1003    observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
1004    observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
1005    ko.utils.extend(observable, ko.observable['fn']);
1006
1007    ko.exportProperty(observable, 'peek', observable.peek);
1008    ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
1009    ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
1010
1011    return observable;
1012}
1013
1014ko.observable['fn'] = {
1015    "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
1016        var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
1017        return oldValueIsPrimitive ? (a === b) : false;
1018    }
1019};
1020
1021var protoProperty = ko.observable.protoProperty = "__ko_proto__";
1022ko.observable['fn'][protoProperty] = ko.observable;
1023
1024ko.hasPrototype = function(instance, prototype) {
1025    if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
1026    if (instance[protoProperty] === prototype) return true;
1027    return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
1028};
1029
1030ko.isObservable = function (instance) {
1031    return ko.hasPrototype(instance, ko.observable);
1032}
1033ko.isWriteableObservable = function (instance) {
1034    // Observable
1035    if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
1036        return true;
1037    // Writeable dependent observable
1038    if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
1039        return true;
1040    // Anything else
1041    return false;
1042}
1043
1044
1045ko.exportSymbol('observable', ko.observable);
1046ko.exportSymbol('isObservable', ko.isObservable);
1047ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
1048ko.observableArray = function (initialValues) {
1049    if (arguments.length == 0) {
1050        // Zero-parameter constructor initializes to empty array
1051        initialValues = [];
1052    }
1053    if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
1054        throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
1055
1056    var result = ko.observable(initialValues);
1057    ko.utils.extend(result, ko.observableArray['fn']);
1058    return result;
1059}
1060
1061ko.observableArray['fn'] = {
1062    'remove': function (valueOrPredicate) {
1063        var underlyingArray = this.peek();
1064        var removedValues = [];
1065        var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
1066        for (var i = 0; i < underlyingArray.length; i++) {
1067            var value = underlyingArray[i];
1068            if (predicate(value)) {
1069                if (removedValues.length === 0) {
1070                    this.valueWillMutate();
1071                }
1072                removedValues.push(value);
1073                underlyingArray.splice(i, 1);
1074                i--;
1075            }
1076        }
1077        if (removedValues.length) {
1078            this.valueHasMutated();
1079        }
1080        return removedValues;
1081    },
1082
1083    'removeAll': function (arrayOfValues) {
1084        // If you passed zero args, we remove everything
1085        if (arrayOfValues === undefined) {
1086            var underlyingArray = this.peek();
1087            var allValues = underlyingArray.slice(0);
1088            this.valueWillMutate();
1089            underlyingArray.splice(0, underlyingArray.length);
1090            this.valueHasMutated();
1091            return allValues;
1092        }
1093        // If you passed an arg, we interpret it as an array of entries to remove
1094        if (!arrayOfValues)
1095            return [];
1096        return this['remove'](function (value) {
1097            return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
1098        });
1099    },
1100
1101    'destroy': function (valueOrPredicate) {
1102        var underlyingArray = this.peek();
1103        var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
1104        this.valueWillMutate();
1105        for (var i = underlyingArray.length - 1; i >= 0; i--) {
1106            var value = underlyingArray[i];
1107            if (predicate(value))
1108                underlyingArray[i]["_destroy"] = true;
1109        }
1110        this.valueHasMutated();
1111    },
1112
1113    'destroyAll': function (arrayOfValues) {
1114        // If you passed zero args, we destroy everything
1115        if (arrayOfValues === undefined)
1116            return this['destroy'](function() { return true });
1117
1118        // If you passed an arg, we interpret it as an array of entries to destroy
1119        if (!arrayOfValues)
1120            return [];
1121        return this['destroy'](function (value) {
1122            return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
1123        });
1124    },
1125
1126    'indexOf': function (item) {
1127        var underlyingArray = this();
1128        return ko.utils.arrayIndexOf(underlyingArray, item);
1129   

Large files files are truncated, but you can click here to view the full file