PageSourceSearch

https://www.noscommunes.ca/js/nowinthehouse.min.js

js noscommunes.ca collected 2026-09-24 09:15:57 UTC 448,896 bytes, 7,901 lines download raw bytes

1
2
3var resources = {
4    ok: {
5        default: 'OK'
6    },
7    silhouette: {
8        m: "/Content/Parliamentarians/images/OfficialMpPhotos/silhouette_m.jpg",
9        f: "/Content/Parliamentarians/images/OfficialMpPhotos/silhouette.jpg"
10    },
11    current_member_speaking: {
12        en: 'Current Member Speaking',
13        fr: 'Député ayant présentement la parole'
14    },
15    current_member_speakingFe: {
16        en: 'Current Member Speaking',
17        fr: 'Députée ayant présentement la parole'
18    },
19    current_member_speakingOther: {
20        en: 'Current Member Speaking',
21        fr: 'Député·e ayant présentement la parole'
22    },
23    previous_member_speaking: {
24        en: 'Previous Members Speaking',
25        fr: 'Député(e)s ayant pris la parole',
26    },
27    view_live: {
28        en: 'Watch on ParlVU',
29        fr: 'Regardez sur ParlVU',
30    },
31    house_is_sitting: {
32        en: 'The House is currently sitting.',
33        fr: 'Une séance de la Chambre est en cours.'
34    },
35    house_is_adjourned:{
36        en: 'Adjourned',
37        fr: 'Ajournement'
38    },
39    extended_break_message: {
40        en: 'The House is adjourned.',
41        fr: 'La Chambre est ajournée.'
42    },
43    parliament_dissolved_message: {
44        en: 'Parliament is dissolved.',
45        fr: 'Le Parlement est dissout.'
46    },
47    session_prorogued_message: {
48        en: 'Parliament is prorogued.',
49        fr:  'Le Parlement est prorogé.'
50    },
51    decision: {
52        en: 'Decision:',
53        fr: 'Décision :'
54    },
55    speaker_decision: {
56        en: 'Speaker\'s Casting Vote:',
57        fr: 'Voix prépondérante du Président :'
58    },
59    yes: {
60        en: 'Yeas:',
61        fr: 'Pour :'
62    },
63    no: {
64        en: 'Nays:',
65        fr: 'Contre :'
66    },
67    subsequent_divisions_title: {
68        en: 'Next Votes',
69        fr: 'Votes à venir'
70    },
71    the_speaker: {
72        en: 'The Speaker',
73        fr: 'Le Président'
74    },
75    the_speakerFe: {
76        en: 'The Speaker',
77        fr: 'La Présidente'
78    },
79    the_speakerOther: {
80        en: 'The Speaker',
81        fr: 'Le Président·e'
82    },
83    deputy_chair: {
84        en: 'The Assistant Deputy Speaker ',
85        fr: 'Le Vice-président adjoint'
86    },
87    deputy_chairFe: {
88        en: 'The Assistant Deputy Speaker ',
89        fr: 'La Vice-présidente adjointe'
90    },
91    deputy_chairOther: {
92        en: 'The Assistant Deputy Speaker ',
93        fr: 'Le Vice-président·e adjoint·e'
94    },
95    assistant_deputy_chair: {
96        en: 'The Assistant Deputy Speaker',
97        fr: 'Le Vice-président adjoint'
98    },
99    assistant_deputy_chairFe: {
100        en: 'The Assistant Deputy Speaker',
101        fr: 'La Vice-présidente adjointe'
102    },
103    assistant_deputy_chairOther: {
104        en: 'The Assistant Deputy Speaker',
105        fr: 'Le Vice-président·e adjoint·e'
106    },
107    deputy_speaker: {
108        en: 'The Deputy Speaker',
109        fr: 'Le Vice-président',
110    },
111    deputy_speakerFe: {
112        en: 'The Deputy Speaker',
113        fr: 'La Vice-présidente'
114    },
115    deputy_speakerOther: {
116        en: 'The Deputy Speaker',
117        fr: 'Le Vice-président·e'
118    },
119    error_event_capture_offline: {
120        en: ' Connection lost. Attempting to reconnect.',
121        fr: ' Connexion perdue. Nous tentons de la rétablir.'
122    },
123    error_signalR_connection: {
124        en: ' Connection lost. Attempting to reconnect.',
125        fr: ' Connexion perdue. Nous tentons de la rétablir.'
126    },
127    error_signalR_slow: {
128        en: ' Slow connection. Site may not update.',
129        fr: ' Connexion lente. Il est possible que le site ne soit pas à jour.'
130    },
131    bells_are_ringing: {
132        en: 'Bells are Ringing',
133        fr: 'Le timbre d’appel sonne'
134    },
135    short_language: {
136        en: 'en',
137        fr: 'fr'
138    },
139    bells_are_ringing_message: {
140        en: 'Members are called to the House',
141        fr: 'Les députés sont convoqués à la Chambre'
142    },
143    show_more: {
144        en: 'Show more',
145        fr: 'Afficher plus'
146    },
147    show_less: {
148        en: 'Show less',
149        fr: 'Afficher moins'
150    },
151    Tooltips: [ 
152         {
153            en: "Approval, by a representative of the Crown, of a bill passed by the House and the Senate, making it into an Act of Parliament.",
154            fr: "La sanction royale est accordée par un représentant de la Couronne à un projet de loi adopté par la Chambre et le Sénat; le projet de loi devient alors une loi du Parlement.",
155            titleEn: "Royal Assent",
156            titleFr: "Sanction royale",
157            sectionId : "102"
158        },
159        {
160            en: "A decision on the procedural acceptability of some matter before the House.",
161            fr: "Jugement sur la recevabilité d'une question de procédure dont la Chambre est saisie.",
162            titleEn: "Speaker's Ruling",
163            titleFr: "Décision de la présidence",
164            sectionId : "134"
165        },
166        {
167            en: "At the opening of the Wednesday sitting, a designated Member leads the House in singing <em>O Canada</em>.",
168            fr: "À l'ouverture de la séance du mercredi, un député désigné entonne l'<em>Ô Canada</em>.",
169            titleEn: "National Anthem",
170            titleFr: "Hymne national",
171            sectionId : "136"
172        },
173        {
174            en: "Delivered at the opening of a session of Parliament, the Speech from the Throne outlines the government's plans for the session.",
175            fr: "Prononcé à l'ouverture d'une session du Parlement, le discours du Trône donne un aperçu du programme du gouvernement pour la session.",
176            titleEn: "Address in Reply to the Speech from the Throne",
177            titleFr: "Adresse en réponse au discours du Trône",
178            sectionId : "168"
179        },
180        {
181            en: "A 30-minute period prior to the daily adjournment, during which Members may raise matters they believe have not been dealt with satisfactorily. ",
182            fr: "Période de 30 minutes avant l'ajournement quotidien, au cours de laquelle les députés peuvent soulever des questions auxquelles, à leur avis, on n'a pas répondu d'une façon satisfaisante.",
183            titleEn: "Adjournment Proceedings",
184            titleFr: "Débat d'ajournement",
185            sectionId : "173"
186        },
187        {
188            en: "A debate held on a motion to adjourn, devoted to the discussion of a specific and important matter requiring urgent consideration.",
189            fr: "Débat sur une motion d'ajournement consacré à la discussion d'une affaire précise et importante dont l'étude s'impose d'urgence.",
190            titleEn: "Emergency Debate",
191            titleFr: "Débat d'urgence",
192            sectionId : "181"
193        },
194        {
195            en: "A daily 15-minute period when Members may make brief statements on matters of national, regional or local importance.",
196            fr: "Période quotidienne de 15 minutes au cours de laquelle des députés peuvent faire de brèves déclarations sur des questions d'intérêt national, régional ou local.",
197            titleEn: "Statements by Members",
198            titleFr: "Déclarations de députés",
199            sectionId : "2"
200        },
201        {
202            en: "When a Member requests an emergency debate, the Speaker decides whether or not the request will be granted, weighing certain criteria to determine if the matter relates to a genuine emergency and if it can be dealt with by the House in another way.",
203            fr: "Quand un député demande un débat d'urgence, le Président décide s'il accepte ou non la demande en tenant compte de certains critères, afin de déterminer si l'affaire est vraiment urgente et si la Chambre peut en être saisie d'une autre manière.",
204            titleEn: "Requests for Emergency Debates",
205            titleFr: "Demandes de débats d'urgence",
206            sectionId : "203"
207        },
208        {
209            en: "Under Routine Proceedings, a Minister may make a short factual announcement or statement of government policy. Spokespersons of recognized opposition parties are given an opportunity to comment.",
210            fr: "Pendant les Affaires courantes, un ministre peut prononcer un court exposé de faits ou de politique gouvernementale. Un porte-parole de chaque parti de l'opposition officiellement reconnu peut ensuite commenter l'exposé.",
211            titleEn: "Statements by Ministers",
212            titleFr: "Déclarations de députés",
213            sectionId : "208"
214        },
215        {
216            en: "Business of a basic nature for which a daily period is set aside in the House.",
217            fr: "Affaires usuelles pour lesquelles la Chambre réserve une période quotidienne.",
218            titleEn: "Routine Proceedings",
219            titleFr: "Affaires courantes",
220            sectionId : "3"
221        },
222        {
223            en: "The process by which the government submits its projected annual expenditures for parliamentary approval.",
224            fr: "Processus par lequel le gouvernement soumet ses prévisions annuelles de dépenses à l'approbation du Parlement.",
225            titleEn: "Business of Supply",
226            titleFr: "Travaux des subsides",
227            sectionId : "60003"
228        },
229        {
230            en: "Bills and motions sponsored by private Members, for which a period is devoted each sitting day.",
231            fr: "Chaque jour de séance, une période de temps est réservée pour l’étude des projets de loi et motions parrainés par de simples députés.",
232            titleEn: "Private Members' Business",
233            titleFr: "Affaires émanant des députés",
234            sectionId : "60004"
235        },
236        {
237            en: "The process by which the government obtains the necessary resources to meet its expenses.",
238            fr: "Processus par lequel le gouvernement obtient les fonds nécessaires en prévision de ses dépenses.",
239            titleEn: "Ways and Means",
240            titleFr: "Voies et moyens",
241            sectionId : "60007"
242        },
243        {
244            en: "The allocation of a specific period of time for the consideration of one or more stages of a public bill.",
245            fr: "Désignation d'une période de temps déterminée pour les délibérations sur une ou plusieurs étapes d'un projet de loi d'intérêt public",
246            titleEn: "Time Allocation",
247            titleFr: "Attribution de temps",
248            sectionId : "60014"
249        },
250        {
251            en: "A procedure preventing further adjournment of debate on any motion and requiring that the motion come to a vote at the end of the sitting in which it is invoked.",
252            fr: "Procédure qui a pour effet d'empêcher qu'un débat sur une motion soit de nouveau ajourné et de provoquer un vote à la fin de la séance en cours.",
253            titleEn: "Closure",
254            titleFr: "Clôture",
255            sectionId : "60016"
256        },
257        {
258            en: "A statement normally made at the end of Question Period on Thursday, outlining the government business to be considered in the following week.",
259            fr: "Déclaration ayant lieu normalement à la fin de la période des questions orales du jeudi, au cours de laquelle on donne un aperçu des travaux qui seront abordés la semaine suivante.",
260            titleEn: "Business of the House - Weekly Statement",
261            titleFr: "Travaux de la Chambre - Déclaration hebdomadaire",
262            sectionId : "60100"
263        },
264        {
265            en: "Delivered at the opening of a session of Parliament, the Speech from the Throne outlines the government's plans for the session.",
266            fr: "Prononcé à l'ouverture d'une session du Parlement, le discours du Trône donne un aperçu du programme du gouvernement pour la session.",
267            titleEn: "Consideration of Her Excellency's Speech from the Throne",
268            titleFr: "Prise en considération du discours du Trône de Son Excellence",
269            sectionId : "60101"
270        },
271        {
272            en: "Bills concerned with public policy introduced by a Minister in the House. ",
273            fr: "Projet de loi d’intérêt public présenté à la Chambre par un ministre.",
274            titleEn: "Government Bills (Commons)",
275            titleFr: "Projets de loi émanant du gouvernement (Communes)",
276            sectionId : "60102"
277        },
278        {
279            en: "Bills concerned with public policy sponsored by a Minister in the House, but first introduced in the Senate.",
280            fr: "Projet de loi d’intérêt public parrainé à la Chambre par un ministre, mais d’abord présenté au Sénat.",
281            titleEn: "Government Bills (Senate)",
282            titleFr: "Projets de loi émanant du gouvernement (Sénat)",
283            sectionId : "60103"
284        },
285        {
286            en: "Motions initiated by a Minister or Parliamentary Secretary.",
287            fr: "Motions initiées par un ministre ou un secrétaire parlementaire.",
288            titleEn: "Government Business",
289            titleFr: "Affaires émanant du gouvernement",
290            sectionId : "600104"
291        },
292        {
293            en: "Funds approved by Parliament to cover government expenditures for the peri
293od from April 1 to June 23 of each fiscal year, pending approval of the main estimates in June.",
294            fr: "Crédits que le Parlement accorde chaque année au gouvernement pour la période du 1er avril au 23 juin afin de couvrir ses dépenses au cours des mois précédant l'adoption du budget principal des dépenses en juin.",
295            titleEn: "Interim Supply",
296            titleFr: "Crédits provisoires",
297            sectionId : "60105"
298        },
299        {
300            en: "Consideration of the government’s planned spending for the coming fiscal year.",
301            fr: "Étude des dépenses prévues du gouvernement pour le prochain exercice financier.",
302            titleEn: "Main Estimates",
303            titleFr: "Budget principal des dépenses",
304            sectionId : "60106"
305        },
306        {
307            en: "Motion moved by a Member of the opposition on an allotted day under business of supply. Such a motion may relate to any matter within Parliament’s jurisdiction.",
308            fr: "Motion proposée par un député de l’opposition lors d’un jour désigné en vertu des travaux des subsides. Une telle motion peut traiter de toute question relevant de la compétence du Parlement.",
309            titleEn: "Opposition Motion",
310            titleFr: "Motion de l'opposition",
311            sectionId : "60107"
312        },
313        {
314            en: "The Standing Orders provide for a one-day special debate on the Standing Orders and procedures of the House and its committees early in each Parliament.",
315            fr: "Le Règlement exige un débat spécial d’un jour sur le Règlement et la procédure de la Chambre et de ses comités au début de chaque législature.",
316            titleEn: "Standing Orders and Procedure",
317            titleFr: "Règlement et procédure",
318            sectionId : "60108"
319        },
320        {
321            en: "An expenditure plan introduced to provide funds to the government to meet new or increased expenses.",
322            fr: "Plan de dépenses visant à obtenir des crédits supplémentaires pour faire face à des imprévus ou à une augmentation des dépenses du gouvernement.",
323            titleEn: "Supplementary Estimates",
324            titleFr: "Budget supplémentaire des dépenses",
325            sectionId : "60109"
326        },
327        {
328            en: "Vote on the Designation of an Item",
329            fr: "Vote sur la désignation d'une affaire",
330            titleEn: "Vote on the Designation of an Item",
331            titleFr: "Vote sur la désignation d'une affaire",
332            sectionId : "60110"
333        },
334        {
335            en: "The process by which the government submits its projected annual expenditures for parliamentary approval.",
336            fr: "Processus par lequel le gouvernement soumet ses prévisions annuelles de dépenses à l'approbation du Parlement.",
337            titleEn: "Business of Supply",
338            titleFr: "Travaux des subsides",
339            sectionId : "60124"
340        },
341       {
342            en: "Funds approved by Parliament to cover government expenditures for the period from April 1 to June 23 of each fiscal year, pending approval of the main estimates in June.",
343            fr: "Crédits que le Parlement accorde chaque année au gouvernement pour la période du 1er avril au 23 juin afin de couvrir ses dépenses au cours des mois précédant l'adoption du budget principal des dépenses en juin.",
344            titleEn: "Interim Supply",
345            titleFr: "Crédits provisoires",
346            sectionId : "60130"
347        },
348        {
349            en: "A question raised by a Member and decided by the Speaker, with respect to any departure from the Standing Orders or customary procedures.",
350            fr: "Question soulevée par un député et tranchée par le Président, à la suite de toute dérogation au Règlement ou à la procédure courante.",
351            titleEn: "Point of Order",
352            titleFr: "Rappel au Règlement",
353            sectionId : "96"
354        }            
355    ]
356
357};
358
359
360
361
362/*!
363 * Knockout JavaScript library v3.5.1
364 * (c) The Knockout.js team - http://knockoutjs.com/
365 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
366 */
367
368(function () {
369    var DEBUG = true;
370    (function (undefined) {
371        // (0, eval)('this') is a robust way of getting a reference to the global object
372        // For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023
373        var window = this || (0, eval)('this'),
374            document = window['document'],
375            navigator = window['navigator'],
376            jQueryInstance = window["jQuery"],
377            JSON = window["JSON"];
378
379        if (!jQueryInstance && typeof jQuery !== "undefined") {
380            jQueryInstance = jQuery;
381        }
382        (function (factory) {
vendor: 386,997 bytes, lines 383-6943
383            // Support three module loading scenarios
384            if (typeof define === 'function' && define['amd']) {
385                // [1] AMD anonymous module
386                define(['exports', 'require'], factory);
387            } else if (typeof exports === 'object' && typeof module === 'object') {
388                // [2] CommonJS/Node.js
389                factory(module['exports'] || exports);  // module.exports is for Node.js
390            } else {
391                // [3] No module loader (plain <script> tag) - put directly in global namespace
392                factory(window['ko'] = {});
393            }
394        }(function (koExports, amdRequire) {
395            // Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
396            // In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
397            var ko = typeof koExports !== 'undefined' ? koExports : {};
398            // Google Closure Compiler helpers (used only to make the minified file smaller)
399            ko.exportSymbol = function (koPath, object) {
400                var tokens = koPath.split(".");
401
402                // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
403                // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
404                var target = ko;
405
406                for (var i = 0; i < tokens.length - 1; i++)
407                    target = target[tokens[i]];
408                target[tokens[tokens.length - 1]] = object;
409            };
410            ko.exportProperty = function (owner, publicName, object) {
411                owner[publicName] = object;
412            };
413            ko.version = "3.5.1";
414
415            ko.exportSymbol('version', ko.version);
416            // For any options that may affect various areas of Knockout and aren't directly associated with data binding.
417            ko.options = {
418                'deferUpdates': false,
419                'useOnlyNativeEvents': false,
420                'foreachHidesDestroyed': false
421            };
422
423            //ko.exportSymbol('options', ko.options);   // 'options' isn't minified
424            ko.utils = (function () {
425                var hasOwnProperty = Object.prototype.hasOwnProperty;
426
427                function objectForEach(obj, action) {
428                    for (var prop in obj) {
429                        if (hasOwnProperty.call(obj, prop)) {
430                            action(prop, obj[prop]);
431                        }
432                    }
433                }
434
435                function extend(target, source) {
436                    if (source) {
437                        for (var prop in source) {
438                            if (hasOwnProperty.call(source, prop)) {
439                                target[prop] = source[prop];
440                            }
441                        }
442                    }
443                    return target;
444                }
445
446                function setPrototypeOf(obj, proto) {
447                    obj.__proto__ = proto;
448                    return obj;
449                }
450
451                var canSetPrototype = ({ __proto__: [] } instanceof Array);
452                var canUseSymbols = !DEBUG && typeof Symbol === 'function';
453
454                // 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)
455                var knownEvents = {}, knownEventTypesByEventName = {};
456                var keyEventTypeName = (navigator && /Firefox\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
457                knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
458                knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
459                objectForEach(knownEvents, function (eventType, knownEventsForType) {
460                    if (knownEventsForType.length) {
461                        for (var i = 0, j = knownEventsForType.length; i < j; i++)
462                            knownEventTypesByEventName[knownEventsForType[i]] = eventType;
463                    }
464                });
465                var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
466
467                // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
468                // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
469                // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
470                // If there is a future need to detect specific versions of IE10+, we will amend this.
471                var ieVersion = document && (function () {
472                    var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
473
474                    // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
475                    while (
476                        div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
477                        iElems[0]
478                    ) { }
479                    return version > 4 ? version : undefined;
480                }());
481                var isIe6 = ieVersion === 6,
482                    isIe7 = ieVersion === 7;
483
484                function isClickOnCheckableElement(element, eventType) {
485                    if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
486                    if (eventType.toLowerCase() != "click") return false;
487                    var inputType = element.type;
488                    return (inputType == "checkbox") || (inputType == "radio");
489                }
490
491                // For details on the pattern for changing node classes
492                // see: https://github.com/knockout/knockout/issues/1597
493                var cssClassNameRegex = /\S+/g;
494
495                var jQueryEventAttachName;
496
497                function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
498                    var addOrRemoveFn;
499                    if (classNames) {
500                        if (typeof node.classList === 'object') {
501                            addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];
502                            ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function (className) {
503                                addOrRemoveFn.call(node.classList, className);
504                            });
505                        } else if (typeof node.className['baseVal'] === 'string') {
506                            // SVG tag .classNames is an SVGAnimatedString instance
507                            toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);
508                        } else {
509                            // node.className ought to be a string.
510                            toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);
511                        }
512                    }
513                }
514
515                function toggleObjectClassPropertyString(obj, prop, classNames, shouldHaveClass) {
516                    // obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.
517                    var currentClassNames = obj[prop].match(cssClassNameRegex) || [];
518                    ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function (className) {
519                        ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);
520                    });
521                    obj[prop] = currentClassNames.join(" ");
522                }
523
524                return {
525                    fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
526
527                    arrayForEach: function (array, action, actionOwner) {
528                        for (var i = 0, j = array.length; i < j; i++) {
529                            action.call(actionOwner, array[i], i, array);
530                        }
531                    },
532
533                    arrayIndexOf: typeof Array.prototype.indexOf == "function"
534                        ? function (array, item) {
535                            return Array.prototype.indexOf.call(array, item);
536                        }
537                        : function (array, item) {
538                            for (var i = 0, j = array.length; i < j; i++) {
539                                if (array[i] === item)
540                                    return i;
541                            }
542                            return -1;
543                        },
544
545                    arrayFirst: function (array, predicate, predicateOwner) {
546                        for (var i = 0, j = array.length; i < j; i++) {
547                            if (predicate.call(predicateOwner, array[i], i, array))
548                                return array[i];
549                        }
550                        return undefined;
551                    },
552
553                    arrayRemoveItem: function (array, itemToRemove) {
554                        var index = ko.utils.arrayIndexOf(array, itemToRemove);
555                        if (index > 0) {
556                            array.splice(index, 1);
557                        }
558                        else if (index === 0) {
559                            array.shift();
560                        }
561                    },
562
563                    arrayGetDistinctValues: function (array) {
564                        var result = [];
565                        if (array) {
566                            ko.utils.arrayForEach(array, function (item) {
567                                if (ko.utils.arrayIndexOf(result, item) < 0)
568                                    result.push(item);
569                            });
570                        }
571                        return result;
572                    },
573
574                    arrayMap: function (array, mapping, mappingOwner) {
575                        var result = [];
576                        if (array) {
577                            for (var i = 0, j = array.length; i < j; i++)
578                                result.push(mapping.call(mappingOwner, array[i], i));
579                        }
580                        return result;
581                    },
582
583                    arrayFilter: function (array, predicate, predicateOwner) {
584                        var result = [];
585                        if (array) {
586                            for (var i = 0, j = array.length; i < j; i++)
587                                if (predicate.call(predicateOwner, array[i], i))
588                                    result.push(array[i]);
589                        }
590                        return result;
591                    },
592
593                    arrayPushAll: function (array, valuesToPush) {
594                        if (valuesToPush instanceof Array)
595                            array.push.apply(array, valuesToPush);
596                        else
597                            for (var i = 0, j = valuesToPush.length; i < j; i++)
598                                array.push(valuesToPush[i]);
599                        return array;
600                    },
601
602                    addOrRemoveItem: function (array, value, included) {
603                        var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.peekObservable(array), value);
604                        if (existingEntryIndex < 0) {
605                            if (included)
606                                array.push(value);
607                        } else {
608                            if (!included)
609                                array.splice(existingEntryIndex, 1);
610                        }
611                    },
612
613                    canSetPrototype: canSetPrototype,
614
615                    extend: extend,
616
617                    setPrototypeOf: setPrototypeOf,
618
619                    setPrototypeOfOrExtend: canSetPrototype ? setPrototypeOf : extend,
620
621                    objectForEach: objectForEach,
622
623                    objectMap: function (source, mapping, mappingOwner) {
624                        if (!source)
625                            return source;
626                        var target = {};
627                        for (var prop in source) {
628                            if (hasOwnProperty.call(source, prop)) {
629                                target[prop] = mapping.call(mappingOwner, source[prop], prop, source);
630                            }
631                        }
632                        return target;
633                    },
634
635                    emptyDomNode: function (domNode) {
636                        while (domNode.firstChild) {
637                            ko.removeNode(domNode.firstChild);
638                        }
639                    },
640
641                    moveCleanedNodesToContainerElement: function (nodes) {
642                        // Ensure it's a real array, as we're about to reparent the nodes and
643                        // we don't want the underlying collection to change while we're doing that.
644                        var nodesArray = ko.utils.makeArray(nodes);
645                        var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;
646
647                        var container = templateDocument.createElement('div');
648                        for (var i = 0, j = nodesArray.length; i < j; i++) {
649                            container.appendChild(ko.cleanNode(nodesArray[i]));
650                        }
651                        return container;
652                    },
653
654                    cloneNodes: function (nodesArray, shouldCleanNodes) {
655                        for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
656                            var clonedNode = nodesArray[i].cloneNode(true);
657                            newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
658                        }
659                        return newNodesArray;
660                    },
661
662                    setDomNodeChildren: function (domNode, childNodes) {
663                        ko.utils.emptyDomNode(domNode);
664                        if (childNodes) {
665                            for (var i = 0, j = childNodes.length; i < j; i++)
666                                domNode.appendChild(childNodes[i]);
667                        }
668                    },
669
670                    replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
671                        var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
672                        if (nodesToReplaceArray.length > 0) {
673                            var insertionPoint = nodesToReplaceArray[0];
674                            var parent = insertionPoint.parentNode;
675                            for (var i = 0, j = newNodesArray.length; i < j; i++)
676                                parent.insertBefore(newNodesArray[i], insertionPoint);
677                            for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
678                                ko.removeNode(nodesToReplaceArray[i]);
679                            }
680                        }
681                    },
682
683                    fixUpContinuousNodeArray: function (continuousNodeArray, parentNode) {
684                        // Before acting on a set of nodes that were previously outputted by a template function, we have to reconcile
685                        // them against what is in the DOM right now. It may be that some of the nodes have already been removed, or that
686                        // new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
687                        // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
688                        // So, this function translates the old "map" output array into its best guess of the set of current DOM nodes.
689                        //
690                        // Rules:
691                        //   [A] Any leading nodes that have been removed should be ignored
692                        //       These most likely correspond to memoization nodes that were already removed during binding
693                        //       See https://github.com/knockout/knockout/pull/440
694                        //   [B] Any trailing nodes that have been remove should be ignored
695                        //       This prevents the code here from adding unrelated nodes to the array while processing rule [C]
696                        //       See https://github.com/knockout/knockout/pull/1903
697                        //   [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed,
698                        //       and include any nodes that have been inserted among the previous collection
699
700                        if (continuousNodeArray.length) {
701                            // The parent node can be a virtual element; so get the real parent node
702                            parentNode = (parentNode.nodeType === 8 && parentNode.parentNode) || parentNode;
703
704                            // Rule [A]
705                            while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode)
706                                continuousNodeArray.splice(0, 1);
707
708                            // Rule [B]
709                            while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
710                                continuousNodeArray.length--;
711
712                            // Rule [C]
713                            if (continuousNodeArray.length > 1) {
714                                var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1];
715                                // Replace with the actual new continuous node set
716                                continuousNodeArray.length = 0;
717                                while (current !== last) {
718                                    continuousNodeArray.push(current);
719                                    current = current.nextSibling;
720                                }
721                                continuousNodeArray.push(last);
722                            }
723                        }
724                        return continuousNodeArray;
725                    },
726
727                    setOptionNodeSelectionState: function (optionNode, isSelected) {
728                        // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
729                        if (ieVersion < 7)
730                            optionNode.setAttribute("selected", isSelected);
731                        else
732                            optionNode.selected = isSelected;
733                    },
734
735                    stringTrim: function (string) {
736                        return string === null || string === undefined ? '' :
737                            string.trim ?
738                                string.trim() :
739                                string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
740                    },
741
742                    stringStartsWith: function (string, startsWith) {
743                        string = string || "";
744                        if (startsWith.length > string.length)
745                            return false;
746                        return string.substring(0, startsWith.length) === startsWith;
747                    },
748
749                    domNodeIsContainedBy: function (node, containedByNode) {
750                        if (node === containedByNode)
751                            return true;
752                        if (node.nodeType === 11)
753                            return false; // Fixes issue #1162 - can't use node.contains for document fragments on IE8
754                        if (containedByNode.contains)
755                            return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node);
756                        if (containedByNode.compareDocumentPosition)
757                            return (containedByNode.compareDocumentPosition(node) & 16) == 16;
758                        while (node && node != containedByNode) {
759                            node = node.parentNode;
760                        }
761                        return !!node;
762                    },
763
764                    domNodeIsAttachedToDocument: function (node) {
765                        return ko.utils.domNodeIsContainedBy(node, node.ownerDocument.documentElement);
766                    },
767
768                    anyDomNodeIsAttachedToDocument: function (nodes) {
769                        return !!ko.utils.arrayFirst(nodes, ko.utils.domNodeIsAttachedToDocument);
770                    },
771
772                    tagNameLower: function (element) {
773                        // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
774                        // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
775                        // we don't need to do the .toLowerCase() as it will always be lower case anyway.
776                        return element && element.tagName && element.tagName.toLowerCase();
777                    },
778
779                    catchFunctionErrors: function (delegate) {
780                        return ko['onError'] ? function () {
781                            try {
782                                return delegate.apply(this, arguments);
783                            } catch (e) {
784                                ko['onError'] && ko['onError'](e);
785                                throw e;
786                            }
787                        } : delegate;
788                    },
789
790                    setTimeout: function (handler, timeout) {
791                        return setTimeout(ko.utils.catchFunctionErrors(handler), timeout);
792                    },
793
794                    deferError: function (error) {
795                        setTimeout(function () {
796                            ko['onError'] && ko['onError'](error);
797                            throw error;
798                        }, 0);
799                    },
800
801                    registerEventHandler: function (element, eventType, handler) {
802                        var wrappedHandler = ko.utils.catchFunctionErrors(handler);
803
804                        var mustUseAttachEvent = eventsThatMustBeRegisteredUsingAttachEvent[eventType];
805                        if (!ko.options['useOnlyNativeEvents'] && !mustUseAttachEvent && jQueryInstance) {
806                            if (!jQueryEventAttachName) {
807                                jQueryEventAttachName = (typeof jQueryInstance(element)['on'] == 'function') ? 'on' : 'bind';
808                            }
809                            jQueryInstance(element)[jQueryEventAttachName](eventType, wrappedHandler);
810                        } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
811                            element.addEventListener(eventType, wrappedHandler, false);
812                        else if (typeof element.attachEvent != "undefined") {
813                            var attachEventHandler = function (event) { wrappedHandler.call(element, event); },
814                                attachEventName = "on" + eventType;
815                            element.attachEvent(attachEventName, attachEventHandler);
816
817                            // IE does not dispose attachEvent handlers automatically (unlike with addEventListener)
818                            // so to avoid leaks, we have to remove them manually. See bug #856
819                            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
820                                element.detachEvent(attachEventName, attachEventHandler);
821                            });
822                        } else
823                            throw new Error("Browser doesn't support addEventListener or attachEvent");
824                    },
825
826                    triggerEvent: function (element, eventType) {
827                        if (!(element && element.nodeType))
828                            throw new Error("element must be a DOM node when calling triggerEvent");
829
830                        // For click events on checkboxes and radio buttons, jQuery toggles the element checked state *after* the
831                        // event handler runs instead of *before*. (This was fixed in 1.9 for checkboxes but not for radio buttons.)
832                        // IE doesn't change the checked state when you trigger the click event using "fireEvent".
833                        // In both cases, we'll use the click method instead.
834                        var useClickWorkaround = isClickOnCheckableElement(element, eventType);
835
836                        if (!ko.options['useOnlyNativeEvents'] && jQueryInstance && !useClickWorkaround) {
837                            jQueryInstance(element)['trigger'](eventType);
838                        } else if (typeof document.createEvent == "function") {
839                            if (typeof element.dispatchEvent == "function") {
840                                var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
841                                var event = document.createEvent(eventCategory);
842                                event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
843                                element.dispatchEvent(event);
844                            }
845                            else
846                                throw new Error("The supplied element doesn't support dispatchEvent");
847                        } else if (useClickWorkaround && element.click) {
848                            element.click();
849                        } else if (typeof element.fireEvent != "undefined") {
850                            element.fireEvent("on" + eventType);
851                        } else {
852                            throw new Error("Browser doesn't support triggering events");
853                        }
854                    },
855
856                    unwrapObservable: function (value) {
857                        return ko.isObservable(value) ? value() : value;
858                    },
859
860                    peekObservable: function (value) {
861                        return ko.isObservable(value) ? value.peek() : value;
862                    },
863
864                    toggleDomNodeCssClass: toggleDomNodeCssClass,
865
866                    setTextContent: function (element, textContent) {
867                        var value = ko.utils.unwrapObservable(textContent);
868                        if ((value === null) || (value === undefined))
869                            value = "";
870
871                        // We need there to be exactly one child: a text node.
872                        // If there are no children, more than one, or if it's not a text node,
873                        // we'll clear everything and create a single text node.
874                        var innerTextNode = ko.virtualElements.firstChild(element);
875                        if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
876                            ko.virtualElements.setDomNodeChildren(element, [element.ownerDocument.createTextNode(value)]);
877                        } else {
878                            innerTextNode.data = value;
879                        }
880
881                        ko.utils.forceRefresh(element);
882                    },
883
884                    setElementName: function (element, name) {
885                        element.name = name;
886
887                        // Workaround IE 6/7 issue
888                        // - https://github.com/SteveSanderson/knockout/issues/197
889                        // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
890                        if (ieVersion <= 7) {
891                            try {
892                                var escapedName = element.name.replace(/[&<>'"]/g, function (r) { return "&#" + r.charCodeAt(0) + ";"; });
893                                element.mergeAttributes(document.createElement("<input name='" + escapedName + "'/>"), false);
894                            }
895                            catch (e) { } // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
896                        }
897                    },
898
899                    forceRefresh: function (node) {
900                        // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
901                        if (ieVersion >= 9) {
902                            // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
903                            var elem = node.nodeType == 1 ? node : node.parentNode;
904                            if (elem.style)
905                                elem.style.zoom = elem.style.zoom;
906                        }
907                    },
908
909                    ensureSelectElementIsRenderedCorrectly: function (selectElement) {
910                        // 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.
911                        // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
912                        // Also fixes IE7 and IE8 bug that causes selects to be zero width if enclosed by 'if' or 'with'. (See issue #839)
913                        if (ieVersion) {
914                            var originalWidth = selectElement.style.width;
915                            selectElement.style.width = 0;
916                            selectElement.style.width = originalWidth;
917                        }
918                    },
919
920                    range: function (min, max) {
921                        min = ko.utils.unwrapObservable(min);
922                        max = ko.utils.unwrapObservable(max);
923                        var result = [];
924                        for (var i = min; i <= max; i++)
925                            result.push(i);
926                        return result;
927                    },
928
929                    makeArray: function (arrayLikeObject) {
930                        var result = [];
931                        for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
932                            result.push(arrayLikeObject[i]);
933                        };
934                        return result;
935                    },
936
937                    createSymbolOrString: function (identifier) {
938                        return canUseSymbols ? Symbol(identifier) : identifier;
939                    },
940
941                    isIe6: isIe6,
942                    isIe7: isIe7,
943                    ieVersion: ieVersion,
944
945                    getFormFields: function (form, fieldName) {
946                        var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
947                        var isMatchingField = (typeof fieldName == 'string')
948                            ? function (field) { return field.name === fieldName }
949                            : function (field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
950                        var matches = [];
951                        for (var i = fields.length - 1; i >= 0; i--) {
952                            if (isMatchingField(fields[i]))
953                                matches.push(fields[i]);
954                        };
955                        return matches;
956                    },
957
958                    parseJson: function (jsonString) {
959                        if (typeof jsonString == "string") {
960                            jsonString = ko.utils.stringTrim(jsonString);
961                            if (jsonString) {
962                                if (JSON && JSON.parse) // Use native parsing where available
963                                    return JSON.parse(jsonString);
964                                return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
965                            }
966                        }
967                        return null;
968                    },
969
970                    stringifyJson: function (data, replacer, space) {   // replacer and space are optional
971                        if (!JSON || !JSON.stringify)
972                            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");
973                        return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
974                    },
975
976                    postJson: function (urlOrForm, data, options) {
977                        options = options || {};
978                        var params = options['params'] || {};
979                        var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
980                        var url = urlOrForm;
981
982                        // If we were given a form, use its 'action' URL and pick out any requested field values
983                        if ((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
984                            var originalForm = urlOrForm;
985                            url = originalForm.action;
986                            for (var i = includeFields.length - 1; i >= 0; i--) {
987                                var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
988                                for (var j = fields.length - 1; j >= 0; j--)
989                                    params[fields[j].name] = fields[j].value;
990                            }
991                        }
992
993                        data = ko.utils.unwrapObservable(data);
994                        var form = document.createElement("form");
995                        form.style.display = "none";
996                        form.action = url;
997                        form.method = "post";
998                        for (var key in data) {
999                            // Since 'data' this is a model object, we include all properties including those inherited from its prototype
1000                            var input = document.createElement("input");
1001                            input.type = "hidden";
1002                            input.name = key;
1003                            input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
1004                            form.appendChild(input);
1005                        }
1006                        objectForEach(params, function (key, value) {
1007                            var input = document.createElement("input");
1008                            input.type = "hidden";
1009                            input.name = key;
1010                            input.value = value;
1011                            form.appendChild(input);
1012                        });
1013                        document.body.appendChild(form);
1014                        options['submitter'] ? options['submitter'](form) : form.submit();
1015                        setTimeout(function () { form.parentNode.removeChild(form); }, 0);
1016                    }
1017                }
1018            }());
1019
1020            ko.exportSymbol('utils', ko.utils);
1021            ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
1022            ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
1023            ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
1024            ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
1025            ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
1026            ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
1027            ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
1028            ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
1029            ko.exportSymbol('utils.cloneNodes', ko.utils.cloneNodes);
1030            ko.exportSymbol('utils.createSymbolOrString', ko.utils.createSymbolOrString);
1031            ko.exportSymbol('utils.extend', ko.utils.extend);
1032            ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
1033            ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
1034            ko.exportSymbol('utils.objectMap', ko.utils.objectMap);
1035            ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
1036            ko.exportSymbol('utils.postJson', ko.utils.postJson);
1037            ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
1038            ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
1039            ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
1040            ko.exportSymbol('utils.range', ko.utils.range);
1041            ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
1042            ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
1043            ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
1044            ko.exportSymbol('utils.objectForEach', ko.utils.objectForEach);
1045            ko.exportSymbol('utils.addOrRemoveItem', ko.utils.addOrRemoveItem);
1046            ko.exportSymbol('utils.setTextContent', ko.utils.setTextContent);
1047            ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly
1048
1049            if (!Function.prototype['bind']) {
1050                // 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)
1051                // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
1052                Function.prototype['bind'] = function (object) {
1053                    var originalFunction = this;
1054                    if (arguments.length === 1) {
1055                        return function () {
1056                            return originalFunction.apply(object, arguments);
1057                        };
1058                    } else {
1059                        var partialArgs = Array.prototype.slice.call(arguments, 1);
1060                        return function () {
1061                            var args = partialArgs.slice(0);
1062                            args.push.apply(args, arguments);
1063                            return originalFunction.apply(object, args);
1064                        };
1065                    }
1066                };
1067            }
1068
1069            ko.utils.domData = new (function () {
1070                var uniqueId = 0;
1071                var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
1072                var dataStore = {};
1073
1074                var getDataForNode, clear;
1075                if (!ko.utils.ieVersion) {
1076                    // We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using
1077                    // it cross-window, so instead we just store the data directly on the node.
1078                    // See https://github.com/knockout/knockout/issues/2141
1079                    getDataForNode = function (node, createIfNotFound) {
1080                        var dataForNode = node[dataStoreKeyExpandoPropertyName];
1081                        if (!dataForNode && createIfNotFound) {
1082                            dataForNode = node[dataStoreKeyExpandoPropertyName] = {};
1083                        }
1084                        return dataForNode;
1085                    };
1086                    clear = function (node) {
1087                        if (node[dataStoreKeyExpandoPropertyName]) {
1088                            delete node[dataStoreKeyExpandoPropertyName];
1089                            return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
1090                        }
1091                        return false;
1092                    };
1093                } else {
1094                    // Old IE versions have memory issues if you store objects on the node, so we use a
1095                    // separate data storage and link to it from the node using a string key.
1096                    getDataForNode = function (node, createIfNotFound) {
1097                        var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
1098                        var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
1099                        if (!hasExistingDataStore) {
1100                            if (!createIfNotFound)
1101                                return undefined;
1102                            dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
1103                            dataStore[dataStoreKey] = {};
1104                        }
1105                        return dataStore[dataStoreKey];
1106                    };
1107                    clear = function (node) {
1108                        var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
1109                        if (dataStoreKey) {
1110                            delete dataStore[dataStoreKey];
1111                            node[dataStoreKeyExpandoPropertyName] = null;
1112                            return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
1113                        }
1114                        return false;
1115                    };
1116                }
1117
1118                return {
1119                    get: function (node, key) {
1120                        var dataForNode = getDataForNode(node, false);
1121                        return dataForNode && dataForNode[key];
1122                    },
1123                    set: function (node, key, value) {
1124                        // Make sure we don't actually create a new domData key if we are actually deleting a value
1125                        var dataForNode = getDataForNode(node, value !== undefined /* createIfNotFound */);
1126                        dataForNode && (dataForNode[key] = value);
1127                    },
1128                    getOrSet: function (node, key, value) {
1129                        var dataForNode = getDataForNode(node, true /* createIfNotFound */);
1130                        return dataForNode[key] || (dataForNode[key] = value);
1131                    },
1132                    clear: clear,
1133
1134                    nextKey: function () {
1135                        return (uniqueId++) + dataStoreKeyExpandoPropertyName;
1136                    }
1137                };
1138            })();
1139
1140            ko.exportSymbol('utils.domData', ko.utils.domData);
1141            ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
1142
1143            ko.utils.domNodeDisposal = new (function () {
1144                var domDataKey = ko.utils.domData.nextKey();
1145                var cleanableNodeTypes = { 1: true, 8: true, 9: true };       // Element, Comment, Document
1146                var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
1147
1148                function getDisposeCallbacksCollection(node, createIfNotFound) {
1149                    var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
1150                    if ((allDisposeCallbacks === undefined) && createIfNotFound) {
1151                        allDisposeCallbacks = [];
1152                        ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
1153                    }
1154                    return allDisposeCallbacks;
1155                }
1156                function destroyCallbacksCollection(node) {
1157                    ko.utils.domData.set(node, domDataKey, undefined);
1158                }
1159
1160                function cleanSingleNode(node) {
1161                    // Run all the dispose callbacks
1162                    var callbacks = getDisposeCallbacksCollection(node, false);
1163                    if (callbacks) {
1164                        callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
1165                        for (var i = 0; i < callbacks.length; i++)
1166                            callbacks[i](node);
1167                    }
1168
1169                    // Erase the DOM data
1170                    ko.utils.domData.clear(node);
1171
1172                    // Perform cleanup needed by external libraries (currently only jQuery, but can be extended)
1173                    ko.utils.domNodeDisposal["cleanExternalData"](node);
1174
1175                    // Clear any immediate-child comment nodes, as these wouldn't have been found by
1176                    // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
1177                    if (cleanableNodeTypesWithDescendants[node.nodeType]) {
1178                        cleanNodesInList(node.childNodes, true/*onlyComments*/);
1179                    }
1180                }
1181
1182                function cleanNodesInList(nodeList, onlyComments) {
1183                    var cleanedNodes = [], lastCleanedNode;
1184                    for (var i = 0; i < nodeList.length; i++) {
1185                        if (!onlyComments || nodeList[i].nodeType === 8) {
1186                            cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]);
1187                            if (nodeList[i] !== lastCleanedNode) {
1188                                while (i-- && ko.utils.arrayIndexOf(cleanedNodes, nodeList[i]) == -1) { }
1189                            }
1190                        }
1191                    }
1192                }
1193
1194                return {
1195                    addDisposeCallback: function (node, callback) {
1196                        if (typeof callback != "function")
1197                            throw new Error("Callback must be a function");
1198                        getDisposeCallbacksCollection(node, true).push(callback);
1199                    },
1200
1201                    removeDisposeCallback: function (node, callback) {
1202                        var callbacksCollection = getDisposeCallbacksCollection(node, false);
1203                        if (callbacksCollection) {
1204                            ko.utils.arrayRemoveItem(callbacksCollection, callback);
1205                            if (callbacksCollection.length == 0)
1206                                destroyCallbacksCollection(node);
1207                        }
1208                    },
1209
1210                    cleanNode: function (node) {
1211                        ko.dependencyDetection.ignore(function () {
1212                            // First clean this node, where applicable
1213                            if (cleanableNodeTypes[node.nodeType]) {
1214                                cleanSingleNode(node);
1215
1216                                // ... then its descendants, where applicable
1217                                if (cleanableNodeTypesWithDescendants[node.nodeType]) {
1218                                    cleanNodesInList(node.getElementsByTagName("*"));
1219                                }
1220                            }
1221                        });
1222
1223                        return node;
1224                    },
1225
1226                    removeNode: function (node) {
1227                        ko.cleanNode(node);
1228                        if (node.parentNode)
1229                            node.parentNode.removeChild(node);
1230                    },
1231
1232                    "cleanExternalData": function (node) {
1233                        // Special support for jQuery here because it's so commonly used.
1234                        // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
1235                        // so notify it to tear down any resources associated with the node & descendants here.
1236                        if (jQueryInstance && (typeof jQueryInstance['cleanData'] == "function"))
1237                            jQueryInstance['cleanData']([node]);
1238                    }
1239                };
1240            })();
1241            ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
1242            ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
1243            ko.exportSymbol('cleanNode', ko.cleanNode);
1244            ko.exportSymbol('removeNode', ko.removeNode);
1245            ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
1246            ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
1247            ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
1248            (function () {
1249                var none = [0, "", ""],
1250                    table = [1, "<table>", "</table>"],
1251                    tbody = [2, "<table><tbody>", "</tbody></table>"],
1252                    tr = [3, "<table><tbody><tr>", "</tr></tbody></table>"],
1253                    select = [1, "<select multiple='multiple'>", "</select>"],
1254                    lookup = {
1255                        'thead': table,
1256                        'tbody': table,
1257                        'tfoot': table,
1258                        'tr': tbody,
1259                        'td': tr,
1260                        'th': tr,
1261                        'option': select,
1262                        'optgroup': select
1263                    },
1264
1265                    // This is needed for old IE if you're *not* using either jQuery or innerShiv. Doesn't affect other cases.
1266                    mayRequireCreateElementHack = ko.utils.ieVersion <= 8;
1267
1268                function getWrap(tags) {
1269                    var m = tags.match(/^(?:<!--.*?-->\s*?)*?<([a-z]+)[\s>]/);
1270                    return (m && lookup[m[1]]) || none;
1271                }
1272
1273                function simpleHtmlParse(html, documentContext) {
1274                    documentContext || (documentContext = document);
1275                    var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;
1276
1277                    // Based on jQuery's "clean" function, but only accounting for table-related elements.
1278                    // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
1279
1280                    // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
1281                    // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
1282                    // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
1283                    // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
1284
1285                    // Trim whitespace, otherwise indexOf won't work as expected
1286                    var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div"),
1287                        wrap = getWrap(tags),
1288                        depth = wrap[0];
1289
1290                    // Go to html and back, then peel off extra wrappers
1291                    // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
1292                    var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
1293                    if (typeof windowContext['innerShiv'] == "function") {
1294                        // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
1295                        // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
1296                        // somehow shims the native APIs so it just works anyway)
1297                        div.appendChild(windowContext['innerShiv'](markup));
1298                    } else {
1299                        if (mayRequireCreateElementHack) {
1300                            // The document.createElement('my-element') trick to enable custom elements in IE6-8
1301                            // only works if we assign innerHTML on an element associated with that document.
1302                            documentContext.body.appendChild(div);
1303                        }
1304
1305                        div.innerHTML = markup;
1306
1307                        if (mayRequireCreateElementHack) {
1308                            div.parentNode.removeChild(div);
1309                        }
1310                    }
1311
1312                    // Move to the right depth
1313                    while (depth--)
1314                        div = div.lastChild;
1315
1316                    return ko.utils.makeArray(div.lastChild.childNodes);
1317                }
1318
1319                function jQueryHtmlParse(html, documentContext) {
1320                    // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
1321                    if (jQueryInstance['parseHTML']) {
1322                        return jQueryInstance['parseHTML'](html, documentContext) || []; // Ensure we always return an array and never null
1323                    } else {
1324                        // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
1325                        var elems = jQueryInstance['clean']([html], documentContext);
1326
1327                        // 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.
1328                        // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
1329                        // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
1330                        if (elems && elems[0]) {
1331                            // Find the top-most parent element that's a direct child of a document fragment
1332                            var elem = elems[0];
1333                            while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
1334                                elem = elem.parentNode;
1335                            // ... then detach it
1336                            if (elem.parentNode)
1337                                elem.parentNode.removeChild(elem);
1338                        }
1339
1340                        return elems;
1341                    }
1342                }
1343
1344                ko.utils.parseHtmlFragment = function (html, documentContext) {
1345                    return jQueryInstance ?
1346                        jQueryHtmlParse(html, documentContext) :   // As below, benefit from jQuery's optimisations where possible
1347                        simpleHtmlParse(html, documentContext);  // ... otherwise, this simple logic will do in most common cases.
1348                };
1349
1350                ko.utils.parseHtmlForTemplateNodes = function (html, documentContext) {
1351                    var nodes = ko.utils.parseHtmlFragment(html, documentContext);
1352                    return (nodes.length && nodes[0].parentElement) || ko.utils.moveCleanedNodesToContainerElement(nodes);
1353                };
1354
1355                ko.utils.setHtml = function (node, html) {
1356                    ko.utils.emptyDomNode(node);
1357
1358                    // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
1359                    html = ko.utils.unwrapObservable(html);
1360
1361                    if ((html !== null) && (html !== undefined)) {
1362                        if (typeof html != 'string')
1363                            html = html.toString();
1364
1365                        // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
1366                        // for example <tr> elements which are not normally allowed to exist on their own.
1367                        // If you've referenced jQuery we'll use that rather than duplicating its code.
1368                        if (jQueryInstance) {
1369                            jQueryInstance(node)['html'](html);
1370                        } else {
1371                            // ... otherwise, use KO's own parsing logic.
1372                            var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);
1373                            for (var i = 0; i < parsedNodes.length; i++)
1374                                node.appendChild(parsedNodes[i]);
1375                        }
1376                    }
1377                };
1378            })();
1379
1380            ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
1381            ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
1382
1383            ko.memoization = (function () {
1384                var memos = {};
1385
1386                function randomMax8HexChars() {
1387                    return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
1388                }
1389                function generateRandomId() {
1390                    return randomMax8HexChars() + randomMax8HexChars();
1391                }
1392                function findMemoNodes(rootNode, appendToArray) {
1393                    if (!rootNode)
1394                        return;
1395                    if (rootNode.nodeType == 8) {
1396                        var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
1397                        if (memoId != null)
1398                            appendToArray.push({ domNode: rootNode, memoId: memoId });
1399                    } else if (rootNode.nodeType == 1) {
1400                        for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
1401                            findMemoNodes(childNodes[i], appendToArray);
1402                    }
1403                }
1404
1405                return {
1406                    memoize: function (callback) {
1407                        if (typeof callback != "function")
1408                            throw new Error("You can only pass a function to ko.memoization.memoize()");
1409                        var memoId = generateRandomId();
1410                        memos[memoId] = callback;
1411                        return "<!--[ko_memo:" + memoId + "]-->";
1412                    },
1413
1414                    unmemoize: function (memoId, callbackParams) {
1415                        var callback = memos[memoId];
1416                        if (callback === undefined)
1417                            throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
1418                        try {
1419                            callback.apply(null, callbackParams || []);
1420                            return true;
1421                        }
1422                        finally { delete memos[memoId]; }
1423                    },
1424
1425                    unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
1426                        var memos = [];
1427                        findMemoNodes(domNode, memos);
1428                        for (var i = 0, j = memos.length; i < j; i++) {
1429                            var node = memos[i].domNode;
1430                            var combinedParams = [node];
1431                            if (extraCallbackParamsArray)
1432                                ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
1433                            ko.memoization.unmemoize(memos[i].memoId, combinedParams);
1434                            node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
1435                            if (node.parentNode)
1436                                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)
1437                        }
1438                    },
1439
1440                    parseMemoText: function (memoText) {
1441                        var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
1442                        return match ? match[1] : null;
1443                    }
1444                };
1445            })();
1446
1447            ko.exportSymbol('memoization', ko.memoization);
1448            ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
1449            ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
1450            ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
1451            ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
1452            ko.tasks = (function () {
1453                var scheduler,
1454                    taskQueue = [],
1455                    taskQueueLength = 0,
1456                    nextHandle = 1,
1457                    nextIndexToProcess = 0;
1458
1459                if (window['MutationObserver']) {
1460                    // Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+
1461                    // From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
1462                    scheduler = (function (callback) {
1463                        var div = document.createElement("div");
1464                        new MutationObserver(callback).observe(div, { attributes: true });
1465                        return function () { div.classList.toggle("foo"); };
1466                    })(scheduledProcess);
1467                } else if (document && "onreadystatechange" in document.createElement("script")) {
1468                    // IE 6-10
1469                    // From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
1470                    scheduler = function (callback) {
1471                        var script = document.createElement("script");
1472                        script.onreadystatechange = function () {
1473                            script.onreadystatechange = null;
1474                            document.documentElement.removeChild(script);
1475                            script = null;
1476                            callback();
1477                        };
1478                        document.documentElement.appendChild(script);
1479                    };
1480                } else {
1481                    scheduler = function (callback) {
1482                        setTimeout(callback, 0);
1483                    };
1484                }
1485
1486                function processTasks() {
1487                    if (taskQueueLength) {
1488                        // Each mark represents the end of a logical group of tasks and the number of these groups is
1489                        // limited to prevent unchecked recursion.
1490                        var mark = taskQueueLength, countMarks = 0;
1491
1492                        // nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
1493                        for (var task; nextIndexToProcess < taskQueueLength;) {
1494                            if (task = taskQueue[nextIndexToProcess++]) {
1495                                if (nextIndexToProcess > mark) {
1496                                    if (++countMarks >= 5000) {
1497                                        nextIndexToProcess = taskQueueLength;   // skip all tasks remaining in the queue since any of them could be causing the recursion
1498                                        ko.utils.deferError(Error("'Too much recursion' after processing " + countMarks + " task groups."));
1499                                        break;
1500                                    }
1501                                    mark = taskQueueLength;
1502                                }
1503                                try {
1504                                    task();
1505                                } catch (ex) {
1506                                    ko.utils.deferError(ex);
1507                                }
1508                            }
1509                        }
1510                    }
1511                }
1512
1513                function scheduledProcess() {
1514                    processTasks();
1515
1516                    // Reset the queue
1517                    nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
1518                }
1519
1520                function scheduleTaskProcessing() {
1521                    ko.tasks['scheduler'](scheduledProcess);
1522                }
1523
1524                var tasks = {
1525                    'scheduler': scheduler,     // Allow overriding the scheduler
1526
1527                    schedule: function (func) {
1528                        if (!taskQueueLength) {
1529                            scheduleTaskProcessing();
1530                        }
1531
1532                        taskQueue[taskQueueLength++] = func;
1533                        return nextHandle++;
1534                    },
1535
1536                    cancel: function (handle) {
1537                        var index = handle - (nextHandle - taskQueueLength);
1538                        if (index >= nextIndexToProcess && index < taskQueueLength) {
1539                            taskQueue[index] = null;
1540                        }
1541                    },
1542
1543                    // For testing only: reset the queue and return the previous queue length
1544                    'resetForTesting': function () {
1545                        var length = taskQueueLength - nextIndexToProcess;
1546                        nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
1547                        return length;
1548                    },
1549
1550                    runEarly: processTasks
1551                };
1552
1553                return tasks;
1554            })();
1555
1556            ko.exportSymbol('tasks', ko.tasks);
1557            ko.exportSymbol('tasks.schedule', ko.tasks.schedule);
1558            //ko.exportSymbol('tasks.cancel', ko.tasks.cancel);  "cancel" isn't minified
1559            ko.exportSymbol('tasks.runEarly', ko.tasks.runEarly);
1560            ko.extenders = {
1561                'throttle': function (target, timeout) {
1562                    // Throttling means two things:
1563
1564                    // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
1565                    //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
1566                    target['throttleEvaluation'] = timeout;
1567
1568                    // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
1569                    //     so the target cannot change value synchronously or faster than a certain rate
1570                    var writeTimeoutInstance = null;
1571                    return ko.dependentObservable({
1572                        'read': target,
1573                        'write': function (value) {
1574                            clearTimeout(writeTimeoutInstance);
1575                            writeTimeoutInstance = ko.utils.setTimeout(function () {
1576                                target(value);
1577                            }, timeout);
1578                        }
1579                    });
1580                },
1581
1582                'rateLimit': function (target, options) {
1583                    var timeout, method, limitFunction;
1584
1585                    if (typeof options == 'number') {
1586                        timeout = options;
1587                    } else {
1588                        timeout = options['timeout'];
1589                        method = options['method'];
1590                    }
1591
1592                    // rateLimit supersedes deferred updates
1593                    target._deferUpdates = false;
1594
1595                    limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ? debounce : throttle;
1596                    target.limit(function (callback) {
1597                        return limitFunction(callback, timeout, options);
1598                    });
1599                },
1600
1601                'deferred': function (target, options) {
1602                    if (options !== true) {
1603                        throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.')
1604                    }
1605
1606                    if (!target._deferUpdates) {
1607                        target._deferUpdates = true;
1608                        target.limit(function (callback) {
1609                            var handle,
1610                                ignoreUpdates = false;
1611                            return function () {
1612                                if (!ignoreUpdates) {
1613                                    ko.tasks.cancel(handle);
1614                                    handle = ko.tasks.schedule(callback);
1615
1616                                    try {
1617                                        ignoreUpdates = true;
1618                                        target['notifySubscribers'](undefined, 'dirty');
1619                                    } finally {
1620                                        ignoreUpdates = false;
1621                                    }
1622                                }
1623                            };
1624                        });
1625                    }
1626                },
1627
1628                'notify': function (target, notifyWhen) {
1629                    target["equalityComparer"] = notifyWhen == "always" ?
1630                        null :  // null equalityComparer means to always notify
1631                        valuesArePrimitiveAndEqual;
1632                }
1633            };
1634
1635            var primitiveTypes = { 'undefined': 1, 'boolean': 1, 'number': 1, 'string': 1 };
1636            function valuesArePrimitiveAndEqual(a, b) {
1637                var oldValueIsPrimitive = (a === null) || (typeof (a) in primitiveTypes);
1638                return oldValueIsPrimitive ? (a === b) : false;
1639            }
1640
1641            function throttle(callback, timeout) {
1642                var timeoutInstance;
1643                return function () {
1644                    if (!timeoutInstance) {
1645                        timeoutInstance = ko.utils.setTimeout(function () {
1646                            timeoutInstance = undefined;
1647                            callback();
1648                        }, timeout);
1649                    }
1650                };
1651            }
1652
1653            function debounce(callback, timeout) {
1654                var timeoutInstance;
1655                return function () {
1656                    clearTimeout(timeoutInstance);
1657                    timeoutInstance = ko.utils.setTimeout(callback, timeout);
1658                };
1659            }
1660
1661            function applyExtenders(requestedExtenders) {
1662                var target = this;
1663                if (requestedExtenders) {
1664                    ko.utils.objectForEach(requestedExtenders, function (key, value) {
1665                        var extenderHandler = ko.extenders[key];
1666                        if (typeof extenderHandler == 'function') {
1667                            target = extenderHandler(target, value) || target;
1668                        }
1669                    });
1670                }
1671                return target;
1672            }
1673
1674            ko.exportSymbol('extenders', ko.extenders);
1675
1676            ko.subscription = function (target, callback, disposeCallback) {
1677                this._target = target;
1678                this._callback = callback;
1679                this._disposeCallback = disposeCallback;
1680                this._isDisposed = false;
1681                this._node = null;
1682                this._domNodeDisposalCallback = null;
1683                ko.exportProperty(this, 'dispose', this.dispose);
1684                ko.exportProperty(this, 'disposeWhenNodeIsRemoved', this.disposeWhenNodeIsRemoved);
1685            };
1686            ko.subscription.prototype.dispose = function () {
1687                var self = this;
1688                if (!self._isDisposed) {
1689                    if (self._domNodeDisposalCallback) {
1690                        ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);
1691                    }
1692                    self._isDisposed = true;
1693                    self._disposeCallback();
1694
1695                    self._target = self._callback = self._disposeCallback = self._node = self._domNodeDisposalCallback = null;
1696                }
1697            };
1698            ko.subscription.prototype.disposeWhenNodeIsRemoved = function (node) {
1699                this._node = node;
1700                ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this));
1701            };
1702
1703            ko.subscribable = function () {
1704                ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);
1705                ko_subscribable_fn.init(this);
1706            }
1707
1708            var defaultEvent = "change";
1709
1710            // Moved out of "limit" to avoid the extra closure
1711            function limitNotifySubscribers(value, event) {
1712                if (!event || event === defaultEvent) {
1713                    this._limitChange(value);
1714                } else if (event === 'beforeChange') {
1715                    this._limitBeforeChange(value);
1716                } else {
1717                    this._origNotifySubscribers(value, event);
1718                }
1719            }
1720
1721            var ko_subscribable_fn = {
1722                init: function (instance) {
1723                    instance._subscriptions = { "change": [] };
1724                    instance._versionNumber = 1;
1725                },
1726
1727                subscribe: function (callback, callbackTarget, event) {
1728                    var self = this;
1729
1730                    event = event || defaultEvent;
1731                    var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
1732
1733                    var subscription = new ko.subscription(self, boundCallback, function () {
1734                        ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
1735                        if (self.afterSubscriptionRemove)
1736                            self.afterSubscriptionRemove(event);
1737                    });
1738
1739                    if (self.beforeSubscriptionAdd)
1740                        self.beforeSubscriptionAdd(event);
1741
1742                    if (!self._subscriptions[event])
1743                        self._subscriptions[event] = [];
1744                    self._subscriptions[event].push(subscription);
1745
1746                    return subscription;
1747                },
1748
1749                "notifySubscribers": function (valueToNotify, event) {
1750                    event = event || defaultEvent;
1751                    if (event === defaultEvent) {
1752                        this.updateVersion();
1753                    }
1754                    if (this.hasSubscriptionsForEvent(event)) {
1755                        var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);
1756                        try {
1757                            ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
1758                            for (var i = 0, subscription; subscription = subs[i]; ++i) {
1759                                // In case a subscription was disposed during the arrayForEach cycle, check
1760                                // for isDisposed on each subscription before invoking its callback
1761                                if (!subscription._isDisposed)
1762                                    subscription._callback(valueToNotify);
1763                            }
1764                        } finally {
1765                            ko.dependencyDetection.end(); // End suppressing dependency detection
1766                        }
1767                    }
1768                },
1769
1770                getVersion: function () {
1771                    return this._versionNumber;
1772                },
1773
1774                hasChanged: function (versionToCheck) {
1775                    return this.getVersion() !== versionToCheck;
1776                },
1777
1778                updateVersion: function () {
1779                    ++this._versionNumber;
1780                },
1781
1782                limit: function (limitFunction) {
1783                    var self = this, selfIsObservable = ko.isObservable(self),
1784                        ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,
1785                        beforeChange = 'beforeChange';
1786
1787                    if (!self._origNotifySubscribers) {
1788                        self._origNotifySubscribers = self["notifySubscribers"];
1789                        self["notifySubscribers"] = limitNotifySubscribers;
1790                    }
1791
1792                    var finish = limitFunction(function () {
1793                        self._notificationIsPending = false;
1794
1795                        // If an observable provided a reference to itself, access it to get the latest value.
1796                        // This allows computed observables to delay calculating their value until needed.
1797                        if (selfIsObservable && pendingValue === self) {
1798                            pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
1799                        }
1800                        var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));
1801
1802                        didUpdate = notifyNextChange = ignoreBeforeChange = false;
1803
1804                        if (shouldNotify) {
1805                            self._origNotifySubscribers(previousValue = pendingValue);
1806                        }
1807                    });
1808
1809                    self._limitChange = function (value, isDirty) {
1810                        if (!isDirty || !self._notificationIsPending) {
1811                            didUpdate = !isDirty;
1812                        }
1813                        self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);
1814                        self._notificationIsPending = ignoreBeforeChange = true;
1815                        pendingValue = value;
1816                        finish();
1817                    };
1818                    self._limitBeforeChange = function (value) {
1819                        if (!ignoreBeforeChange) {
1820                            previousValue = value;
1821                            self._origNotifySubscribers(value, beforeChange);
1822                        }
1823                    };
1824                    self._recordUpdate = function () {
1825                        didUpdate = true;
1826                    };
1827                    self._notifyNextChangeIfValueIsDifferent = function () {
1828                        if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
1829                            notifyNextChange = true;
1830                        }
1831                    };
1832                },
1833
1834                hasSubscriptionsForEvent: function (event) {
1835                    return this._subscriptions[event] && this._subscriptions[event].length;
1836                },
1837
1838                getSubscriptionsCount: function (event) {
1839                    if (event) {
1840                        return this._subscriptions[event] && this._subscriptions[event].length || 0;
1841                    } else {
1842                        var total = 0;
1843                        ko.utils.objectForEach(this._subscriptions, function (eventName, subscriptions) {
1844                            if (eventName !== 'dirty')
1845                                total += subscriptions.length;
1846                        });
1847                        return total;
1848                    }
1849                },
1850
1851                isDifferent: function (oldValue, newValue) {
1852                    return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
1853                },
1854
1855                toString: function () {
1856                    return '[object Object]'
1857                },
1858
1859                extend: applyExtenders
1860            };
1861
1862            ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init);
1863            ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe);
1864            ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend);
1865            ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount);
1866
1867            // For browsers that support proto assignment, we overwrite the prototype of each
1868            // observable instance. Since observables are functions, we need Function.prototype
1869            // to still be in the prototype chain.
1870            if (ko.utils.canSetPrototype) {
1871                ko.utils.setPrototypeOf(ko_subscribable_fn, Function.prototype);
1872            }
1873
1874            ko.subscribable['fn'] = ko_subscribable_fn;
1875
1876
1877            ko.isSubscribable = function (instance) {
1878                return instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
1879            };
1880
1881            ko.exportSymbol('subscribable', ko.subscribable);
1882            ko.exportSymbol('isSubscribable', ko.isSubscribable);
1883
1884            ko.computedContext = ko.dependencyDetection = (function () {
1885                var outerFrames = [],
1886                    currentFrame,
1887                    lastId = 0;
1888
1889                // Return a unique ID that can be assigned to an observable for dependency tracking.
1890                // Theoretically, you could eventually overflow the number storage size, resulting
1891                // in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
1892                // or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
1893                // take over 285 years to reach that number.
1894                // Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
1895                function getId() {
1896                    return ++lastId;
1897                }
1898
1899                function begin(options) {
1900                    outerFrames.push(currentFrame);
1901                    currentFrame = options;
1902                }
1903
1904                function end() {
1905                    currentFrame = outerFrames.pop();
1906                }
1907
1908                return {
1909                    begin: begin,
1910
1911                    end: end,
1912
1913                    registerDependency: function (subscribable) {
1914                        if (currentFrame) {
1915                            if (!ko.isSubscribable(subscribable))
1916                                throw new Error("Only subscribable things can act as dependencies");
1917                            currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
1918                        }
1919                    },
1920
1921                    ignore: function (callback, callbackTarget, callbackArgs) {
1922                        try {
1923                            begin();
1924                            return callback.apply(callbackTarget, callbackArgs || []);
1925                        } finally {
1926                            end();
1927                        }
1928                    },
1929
1930                    getDependenciesCount: function () {
1931                        if (currentFrame)
1932                            return currentFrame.computed.getDependenciesCount();
1933                    },
1934
1935                    getDependencies: function () {
1936                        if (currentFrame)
1937                            return currentFrame.computed.getDependencies();
1938                    },
1939
1940                    isInitial: function () {
1941                        if (currentFrame)
1942                            return currentFrame.isInitial;
1943                    },
1944
1945                    computed: function () {
1946                        if (currentFrame)
1947                            return currentFrame.computed;
1948                    }
1949                };
1950            })();
1951
1952            ko.exportSymbol('computedContext', ko.computedContext);
1953            ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);
1954            ko.exportSymbol('computedContext.getDependencies', ko.computedContext.getDependencies);
1955            ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);
1956            ko.exportSymbol('computedContext.registerDependency', ko.computedContext.registerDependency);
1957
1958            ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);
1959            var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');
1960
1961            ko.observable = function (initialValue) {
1962                function observable() {
1963                    if (arguments.length > 0) {
1964                        // Write
1965
1966                        // Ignore writes if the value hasn't changed
1967                        if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
1968                            observable.valueWillMutate();
1969                            observable[observableLatestValue] = arguments[0];
1970                            observable.valueHasMutated();
1971                        }
1972                        return this; // Permits chained assignments
1973                    }
1974                    else {
1975                        // Read
1976                        ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
1977                        return observable[observableLatestValue];
1978                    }
1979                }
1980
1981                observable[observableLatestValue] = initialValue;
1982
1983                // Inherit from 'subscribable'
1984                if (!ko.utils.canSetPrototype) {
1985                    // 'subscribable' won't be on the prototype chain unless we put it there directly
1986                    ko.utils.extend(observable, ko.subscribable['fn']);
1987                }
1988                ko.subscribable['fn'].init(observable);
1989
1990                // Inherit from 'observable'
1991                ko.utils.setPrototypeOfOrExtend(observable, observableFn);
1992
1993                if (ko.options['deferUpdates']) {
1994                    ko.extenders['deferred'](observable, true);
1995                }
1996
1997                return observable;
1998            }
1999
2000            // Define prototype for observables
2001            var observableFn = {
2002                'equalityComparer': valuesArePrimitiveAndEqual,
2003                peek: function () { return this[observableLatestValue]; },
2004                valueHasMutated: function () {
2005                    this['notifySubscribers'](this[observableLatestValue], 'spectate');
2006                    this['notifySubscribers'](this[observableLatestValue]);
2007                },
2008                valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }
2009            };
2010
2011            // Note that for browsers that don't support proto assignment, the
2012            // inheritance chain is created manually in the ko.observable constructor
2013            if (ko.utils.canSetPrototype) {
2014                ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);
2015            }
2016
2017            var protoProperty = ko.observable.protoProperty = '__ko_proto__';
2018            observableFn[protoProperty] = ko.observable;
2019
2020            ko.isObservable = function (instance) {
2021                var proto = typeof instance == 'function' && instance[protoProperty];
2022                if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) {
2023                    throw Error("Invalid object that looks like an observable; possibly from another Knockout instance");
2024                }
2025                return !!proto;
2026            };
2027
2028            ko.isWriteableObservable = function (instance) {
2029                return (typeof instance == 'function' && (
2030                    (instance[protoProperty] === observableFn[protoProperty]) ||  // Observable
2031                    (instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction)));   // Writable computed observable
2032            };
2033
2034            ko.exportSymbol('observable', ko.observable);
2035            ko.exportSymbol('isObservable', ko.isObservable);
2036            ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
2037            ko.exportSymbol('isWritableObservable', ko.isWriteableObservable);
2038            ko.exportSymbol('observable.fn', observableFn);
2039            ko.exportProperty(observableFn, 'peek', observableFn.peek);
2040            ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);
2041            ko.exportProperty(observableFn, 'valueWillMutate', observableFn.valueWillMutate);
2042            ko.observableArray = function (initialValues) {
2043                initialValues = initialValues || [];
2044
2045                if (typeof initialValues != 'object' || !('length' in initialValues))
2046                    throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
2047
2048                var result = ko.observable(initialValues);
2049                ko.utils.setPrototypeOfOrExtend(result, ko.observableArray['fn']);
2050                return result.extend({ 'trackArrayChanges': true });
2051            };
2052
2053            ko.observableArray['fn'] = {
2054                'remove': function (valueOrPredicate) {
2055                    var underlyingArray = this.peek();
2056                    var removedValues = [];
2057                    var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
2058                    for (var i = 0; i < underlyingArray.length; i++) {
2059                        var value = underlyingArray[i];
2060                        if (predicate(value)) {
2061                            if (removedValues.length === 0) {
2062                                this.valueWillMutate();
2063                            }
2064                            if (underlyingArray[i] !== value) {
2065                                throw Error("Array modified during remove; cannot remove item");
2066                            }
2067                            removedValues.push(value);
2068                            underlyingArray.splice(i, 1);
2069                            i--;
2070                        }
2071                    }
2072                    if (removedValues.length) {
2073                        this.valueHasMutated();
2074                    }
2075                    return removedValues;
2076                },
2077
2078                'removeAll': function (arrayOfValues) {
2079                    // If you passed zero args, we remove everything
2080                    if (arrayOfValues === undefined) {
2081                        var underlyingArray = this.peek();
2082                        var allValues = underlyingArray.slice(0);
2083                        this.valueWillMutate();
2084                        underlyingArray.splice(0, underlyingArray.length);
2085                        this.valueHasMutated();
2086                        return allValues;
2087                    }
2088                    // If you passed an arg, we interpret it as an array of entries to remove
2089                    if (!arrayOfValues)
2090                        return [];
2091                    return this['remove'](function (value) {
2092                        return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
2093                    });
2094                },
2095
2096                'destroy': function (valueOrPredicate) {
2097                    var underlyingArray = this.peek();
2098                    var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
2099                    this.valueWillMutate();
2100                    for (var i = underlyingArray.length - 1; i >= 0; i--) {
2101                        var value = underlyingArray[i];
2102                        if (predicate(value))
2103                            value["_destroy"] = true;
2104                    }
2105                    this.valueHasMutated();
2106                },
2107
2108                'destroyAll': function (arrayOfValues) {
2109                    // If you passed zero args, we destroy everything
2110                    if (arrayOfValues === undefined)
2111                        return this['destroy'](function () { return true });
2112
2113                    // If you passed an arg, we interpret it as an array of entries to destroy
2114                    if (!arrayOfValues)
2115                        return [];
2116                    return this['destroy'](function (value) {
2117                        return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
2118                    });
2119                },
2120
2121                'indexOf': function (item) {
2122                    var underlyingArray = this();
2123                    return ko.utils.arrayIndexOf(underlyingArray, item);
2124                },
2125
2126                'replace': function (oldItem, newItem) {
2127                    var index = this['indexOf'](oldItem);
2128                    if (index >= 0) {
2129                        this.valueWillMutate();
2130                        this.peek()[index] = newItem;
2131                        this.valueHasMutated();
2132                    }
2133                },
2134
2135                'sorted': function (compareFunction) {
2136                    var arrayCopy = this().slice(0);
2137                    return compareFunction ? arrayCopy.sort(compareFunction) : arrayCopy.sort();
2138                },
2139
2140                'reversed': function () {
2141                    return this().slice(0).reverse();
2142                }
2143            };
2144
2145            // Note that for browsers that don't support proto assignment, the
2146            // inheritance chain is created manually in the ko.observableArray constructor
2147            if (ko.utils.canSetPrototype) {
2148                ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);
2149            }
2150
2151            // Populate ko.observableArray.fn with read/write functions from native arrays
2152            // Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
2153            // because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
2154            ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
2155                ko.observableArray['fn'][methodName] = function () {
2156                    // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
2157                    // (for consistency with mutating regular observables)
2158                    var underlyingArray = this.peek();
2159                    this.valueWillMutate();
2160                    this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
2161                    var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
2162                    this.valueHasMutated();
2163                    // The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
2164                    return methodCallResult === underlyingArray ? this : methodCallResult;
2165                };
2166            });
2167
2168            // Populate ko.observableArray.fn with read-only functions from native arrays
2169            ko.utils.arrayForEach(["slice"], function (methodName) {
2170                ko.observableArray['fn'][methodName] = function () {
2171                    var underlyingArray = this();
2172                    return underlyingArray[methodName].apply(underlyingArray, arguments);
2173                };
2174            });
2175
2176            ko.isObservableArray = function (instance) {
2177                return ko.isObservable(instance)
2178                    && typeof instance["remove"] == "function"
2179                    && typeof instance["push"] == "function";
2180            };
2181
2182            ko.exportSymbol('observableArray', ko.observableArray);
2183            ko.exportSymbol('isObservableArray', ko.isObservableArray);
2184            var arrayChangeEventName = 'arrayChange';
2185            ko.extenders['trackArrayChanges'] = function (target, options) {
2186                // Use the provided options--each call to trackArrayChanges overwrites the previously set options
2187                target.compareArrayOptions = {};
2188                if (options && typeof options == "object") {
2189                    ko.utils.extend(target.compareArrayOptions, options);
2190                }
2191                target.compareArrayOptions['sparse'] = true;
2192
2193                // Only modify the target observable once
2194                if (target.cacheDiffForKnownOperation) {
2195                    return;
2196                }
2197                var trackingChanges = false,
2198                    cachedDiff = null,
2199                    changeSubscription,
2200                    spectateSubscription,
2201                    pendingChanges = 0,
2202                    previousContents,
2203                    underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,
2204                    underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;
2205
2206                // Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
2207                target.beforeSubscriptionAdd = function (event) {
2208                    if (underlyingBeforeSubscriptionAddFunction) {
2209                        underlyingBeforeSubscriptionAddFunction.call(target, event);
2210                    }
2211                    if (event === arrayChangeEventName) {
2212                        trackChanges();
2213                    }
2214                };
2215                // Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
2216                target.afterSubscriptionRemove = function (event) {
2217                    if (underlyingAfterSubscriptionRemoveFunction) {
2218                        underlyingAfterSubscriptionRemoveFunction.call(target, event);
2219                    }
2220                    if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
2221                        if (changeSubscription) {
2222                            changeSubscription.dispose();
2223                        }
2224                        if (spectateSubscription) {
2225                            spectateSubscription.dispose();
2226                        }
2227                        spectateSubscription = changeSubscription = null;
2228                        trackingChanges = false;
2229                        previousContents = undefined;
2230                    }
2231                };
2232
2233                function trackChanges() {
2234                    if (trackingChanges) {
2235                        // Whenever there's a new subscription and there are pending notifications, make sure all previous
2236                        // subscriptions are notified of the change so that all subscriptions are in sync.
2237                        notifyChanges();
2238                        return;
2239                    }
2240
2241                    trackingChanges = true;
2242
2243                    // Track how many times the array actually changed value
2244                    spectateSubscription = target.subscribe(function () {
2245                        ++pendingChanges;
2246                    }, null, "spectate");
2247
2248                    // Each time the array changes value, capture a clone so that on the next
2249                    // change it's possible to produce a diff
2250                    previousContents = [].concat(target.peek() || []);
2251                    cachedDiff = null;
2252                    changeSubscription = target.subscribe(notifyChanges);
2253
2254                    function notifyChanges() {
2255                        if (pendingChanges) {
2256                            // Make a copy of the current contents and ensure it's an array
2257                            var currentContents = [].concat(target.peek() || []), changes;
2258
2259                            // Compute the diff and issue notifications, but only if someone is listening
2260                            if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
2261                                changes = getChanges(previousContents, currentContents);
2262                            }
2263
2264                            // Eliminate references to the old, removed items, so they can be GCed
2265                            previousContents = currentContents;
2266                            cachedDiff = null;
2267                            pendingChanges = 0;
2268
2269                            if (changes && changes.length) {
2270                                target['notifySubscribers'](changes, arrayChangeEventName);
2271                            }
2272                        }
2273                    }
2274                }
2275
2276                function getChanges(previousContents, currentContents) {
2277                    // We try to re-use cached diffs.
2278                    // The scenarios where pendingChanges > 1 are when using rate limiting or deferred updates,
2279                    // which without this check would not be compatible with arrayChange notifications. Normally,
2280                    // notifications are issued immediately so we wouldn't be queueing up more than one.
2281                    if (!cachedDiff || pendingChanges > 1) {
2282                        cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions);
2283                    }
2284
2285                    return cachedDiff;
2286                }
2287
2288                target.cacheDiffForKnownOperation = function (rawArray, operationName, args) {
2289                    // Only run if we're currently tracking changes for this observable array
2290                    // and there aren't any pending deferred notifications.
2291                    if (!trackingChanges || pendingChanges) {
2292                        return;
2293                    }
2294                    var diff = [],
2295                        arrayLength = rawArray.length,
2296                        argsLength = args.length,
2297                        offset = 0;
2298
2299                    function pushDiff(status, value, index) {
2300                        return diff[diff.length] = { 'status': status, 'value': value, 'index': index };
2301                    }
2302                    switch (operationName) {
2303                        case 'push':
2304                            offset = arrayLength;
2305                        case 'unshift':
2306                            for (var index = 0; index < argsLength; index++) {
2307                                pushDiff('added', args[index], offset + index);
2308                            }
2309                            break;
2310
2311                        case 'pop':
2312                            offset = arrayLength - 1;
2313                        case 'shift':
2314                            if (arrayLength) {
2315                                pushDiff('deleted', rawArray[offset], offset);
2316                            }
2317                            break;
2318
2319                        case 'splice':
2320                            // Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].
2321                            // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
2322                            var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength),
2323                                endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength),
2324                                endAddIndex = startIndex + argsLength - 2,
2325                                endIndex = Math.max(endDeleteIndex, endAddIndex),
2326                                additions = [], deletions = [];
2327                            for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
2328                                if (index < endDeleteIndex)
2329                                    deletions.push(pushDiff('deleted', rawArray[index], index));
2330                                if (index < endAddIndex)
2331                                    additions.push(pushDiff('added', args[argsIndex], index));
2332                            }
2333                            ko.utils.findMovesInArrayComparison(deletions, additions);
2334                            break;
2335
2336                        default:
2337                            return;
2338                    }
2339                    cachedDiff = diff;
2340                };
2341            };
2342            var computedState = ko.utils.createSymbolOrString('_state');
2343
2344            ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
2345                if (typeof evaluatorFunctionOrOptions === "object") {
2346                    // Single-parameter syntax - everything is on this "options" param
2347                    options = evaluatorFunctionOrOptions;
2348                } else {
2349                    // Multi-parameter syntax - construct the options according to the params passed
2350                    options = options || {};
2351                    if (evaluatorFunctionOrOptions) {
2352                        options["read"] = evaluatorFunctionOrOptions;
2353                    }
2354                }
2355                if (typeof options["read"] != "function")
2356                    throw Error("Pass a function that returns the value of the ko.computed");
2357
2358                var writeFunction = options["write"];
2359                var state = {
2360                    latestValue: undefined,
2361                    isStale: true,
2362                    isDirty: true,
2363                    isBeingEvaluated: false,
2364                    suppressDisposalUntilDisposeWhenReturnsFalse: false,
2365                    isDisposed: false,
2366                    pure: false,
2367                    isSleeping: false,
2368                    readFunction: options["read"],
2369                    evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
2370                    disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
2371                    disposeWhen: options["disposeWhen"] || options.disposeWhen,
2372                    domNodeDisposalCallback: null,
2373                    dependencyTracking: {},
2374                    dependenciesCount: 0,
2375                    evaluationTimeoutInstance: null
2376                };
2377
2378                function computedObservable() {
2379                    if (arguments.length > 0) {
2380                        if (typeof writeFunction === "function") {
2381                            // Writing a value
2382                            writeFunction.apply(state.evaluatorFunctionTarget, arguments);
2383                        } else {
2384                            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.");
2385                        }
2386                        return this; // Permits chained assignments
2387                    } else {
2388                        // Reading the value
2389                        if (!state.isDisposed) {
2390                            ko.dependencyDetection.registerDependency(computedObservable);
2391                        }
2392                        if (state.isDirty || (state.isSleeping && computedObservable.haveDependenciesChanged())) {
2393                            computedObservable.evaluateImmediate();
2394                        }
2395                        return state.latestValue;
2396                    }
2397                }
2398
2399                computedObservable[computedState] = state;
2400                computedObservable.hasWriteFunction = typeof writeFunction === "function";
2401
2402                // Inherit from 'subscribable'
2403                if (!ko.utils.canSetPrototype) {
2404                    // 'subscribable' won't be on the prototype chain unless we put it there directly
2405                    ko.utils.extend(computedObservable, ko.subscribable['fn']);
2406                }
2407                ko.subscribable['fn'].init(computedObservable);
2408
2409                // Inherit from 'computed'
2410                ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);
2411
2412                if (options['pure']) {
2413                    state.pure = true;
2414                    state.isSleeping = true;     // Starts off sleeping; will awake on the first subscription
2415                    ko.utils.extend(computedObservable, pureComputedOverrides);
2416                } else if (options['deferEvaluation']) {
2417                    ko.utils.extend(computedObservable, deferEvaluationOverrides);
2418                }
2419
2420                if (ko.options['deferUpdates']) {
2421                    ko.extenders['deferred'](computedObservable, true);
2422                }
2423
2424                if (DEBUG) {
2425                    // #1731 - Aid debugging by exposing the computed's options
2426                    computedObservable["_options"] = options;
2427                }
2428
2429                if (state.disposeWhenNodeIsRemoved) {
2430                    // Since this computed is associated with a DOM node, and we don't want to dispose the computed
2431                    // until the DOM node is *removed* from the document (as opposed to never having been in the document),
2432                    // we'll prevent disposal until "disposeWhen" first returns false.
2433                    state.suppressDisposalUntilDisposeWhenReturnsFalse = true;
2434
2435                    // disposeWhenNodeIsRemoved: true can be used to opt into the "only dispose after first false result"
2436                    // behaviour even if there's no specific node to watch. In that case, clear the option so we don't try
2437                    // to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't
2438                    // be documented or used by application code, as it's likely to change in a future version of KO.
2439                    if (!state.disposeWhenNodeIsRemoved.nodeType) {
2440                        state.disposeWhenNodeIsRemoved = null;
2441                    }
2442                }
2443
2444                // Evaluate, unless sleeping or deferEvaluation is true
2445                if (!state.isSleeping && !options['deferEvaluation']) {
2446                    computedObservable.evaluateImmediate();
2447                }
2448
2449                // Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
2450                // removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
2451                if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {
2452                    ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {
2453                        computedObservable.dispose();
2454                    });
2455                }
2456
2457                return computedObservable;
2458            };
2459
2460            // Utility function that disposes a given dependencyTracking entry
2461            function computedDisposeDependencyCallback(id, entryToDispose) {
2462                if (entryToDispose !== null && entryToDispose.dispose) {
2463                    entryToDispose.dispose();
2464                }
2465            }
2466
2467            // This function gets called each time a dependency is detected while evaluating a computed.
2468            // It's factored out as a shared function to avoid creating unnecessary function instances during evaluation.
2469            function computedBeginDependencyDetectionCallback(subscribable, id) {
2470                var computedObservable = this.computedObservable,
2471                    state = computedObservable[computedState];
2472                if (!state.isDisposed) {
2473                    if (this.disposalCount && this.disposalCandidates[id]) {
2474                        // Don't want to dispose this subscription, as it's still being used
2475                        computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]);
2476                        this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway
2477                        --this.disposalCount;
2478                    } else if (!state.dependencyTracking[id]) {
2479                        // Brand new subscription - add it
2480                        computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable));
2481                    }
2482                    // If the observable we've accessed has a pending notification, ensure we get notified of the actual final value (bypass equality checks)
2483                    if (subscribable._notificationIsPending) {
2484                        subscribable._notifyNextChangeIfValueIsDifferent();
2485                    }
2486                }
2487            }
2488
2489            var computedFn = {
2490                "equalityComparer": valuesArePrimitiveAndEqual,
2491                getDependenciesCount: function () {
2492                    return this[computedState].dependenciesCount;
2493                },
2494                getDependencies: function () {
2495                    var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];
2496
2497                    ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
2498                        dependentObservables[dependency._order] = dependency._target;
2499                    });
2500
2501                    return dependentObservables;
2502                },
2503                hasAncestorDependency: function (obs) {
2504                    if (!this[computedState].dependenciesCount) {
2505                        return false;
2506                    }
2507                    var dependencies = this.getDependencies();
2508                    if (ko.utils.arrayIndexOf(dependencies, obs) !== -1) {
2509                        return true;
2510                    }
2511                    return !!ko.utils.arrayFirst(dependencies, function (dep) {
2512                        return dep.hasAncestorDependency && dep.hasAncestorDependency(obs);
2513                    });
2514                },
2515                addDependencyTracking: function (id, target, trackingObj) {
2516                    if (this[computedState].pure && target === this) {
2517                        throw Error("A 'pure' computed must not be called recursively");
2518                    }
2519
2520                    this[computedState].dependencyTracking[id] = trackingObj;
2521                    trackingObj._order = this[computedState].dependenciesCount++;
2522                    trackingObj._version = target.getVersion();
2523                },
2524                haveDependenciesChanged: function () {
2525                    var id, dependency, dependencyTracking = this[computedState].dependencyTracking;
2526                    for (id in dependencyTracking) {
2527                        if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) {
2528                            dependency = dependencyTracking[id];
2529                            if ((this._evalDelayed && dependency._target._notificationIsPending) || dependency._target.hasChanged(dependency._version)) {
2530                                return true;
2531                            }
2532                        }
2533                    }
2534                },
2535                markDirty: function () {
2536                    // Process "dirty" events if we can handle delayed notifications
2537                    if (this._evalDelayed && !this[computedState].isBeingEvaluated) {
2538                        this._evalDelayed(false /*isChange*/);
2539                    }
2540                },
2541                isActive: function () {
2542                    var state = this[computedState];
2543                    return state.isDirty || state.dependenciesCount > 0;
2544                },
2545                respondToChange: function () {
2546                    // Ignore "change" events if we've already scheduled a delayed notification
2547                    if (!this._notificationIsPending) {
2548                        this.evaluatePossiblyAsync();
2549                    } else if (this[computedState].isDirty) {
2550                        this[computedState].isStale = true;
2551                    }
2552                },
2553                subscribeToDependency: function (target) {
2554                    if (target._deferUpdates) {
2555                        var dirtySub = target.subscribe(this.markDirty, this, 'dirty'),
2556                            changeSub = target.subscribe(this.respondToChange, this);
2557                        return {
2558                            _target: target,
2559                            dispose: function () {
2560                                dirtySub.dispose();
2561                                changeSub.dispose();
2562                            }
2563                        };
2564                    } else {
2565                        return target.subscribe(this.evaluatePossiblyAsync, this);
2566                    }
2567                },
2568                evaluatePossiblyAsync: function () {
2569                    var computedObservable = this,
2570                        throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
2571                    if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
2572                        clearTimeout(this[computedState].evaluationTimeoutInstance);
2573                        this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {
2574                            computedObservable.evaluateImmediate(true /*notifyChange*/);
2575                        }, throttleEvaluationTimeout);
2576                    } else if (computedObservable._evalDelayed) {
2577                        computedObservable._evalDelayed(true /*isChange*/);
2578                    } else {
2579                        computedObservable.evaluateImmediate(true /*notifyChange*/);
2580                    }
2581                },
2582                evaluateImmediate: function (notifyChange) {
2583                    var computedObservable = this,
2584                        state = computedObservable[computedState],
2585                        disposeWhen = state.disposeWhen,
2586                        changed = false;
2587
2588                    if (state.isBeingEvaluated) {
2589                        // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
2590                        // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
2591                        // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
2592                        // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
2593                        return;
2594                    }
2595
2596                    // Do not evaluate (and possibly capture new dependencies) if disposed
2597                    if (state.isDisposed) {
2598                        return;
2599                    }
2600
2601                    if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) {
2602                        // See comment above about suppressDisposalUntilDisposeWhenReturnsFalse
2603                        if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) {
2604                            computedObservable.dispose();
2605                            return;
2606                        }
2607                    } else {
2608                        // It just did return false, so we can stop suppressing now
2609                        state.suppressDisposalUntilDisposeWhenReturnsFalse = false;
2610                    }
2611
2612                    state.isBeingEvaluated = true;
2613                    try {
2614                        changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);
2615                    } finally {
2616                        state.isBeingEvaluated = false;
2617                    }
2618
2619                    return changed;
2620                },
2621                evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {
2622                    // This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else.
2623                    // Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate,
2624                    // which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least).
2625
2626                    var computedObservable = this,
2627                        state = computedObservable[computedState],
2628                        changed = false;
2629
2630                    // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
2631                    // Then, during evaluation, we cross off any that are in fact still being used.
2632                    var isInitial = state.pure ? undefined : !state.dependenciesCount,   // If we're evaluating when there are no previous dependencies, it must be the first time
2633                        dependencyDetectionContext = {
2634                            computedObservable: computedObservable,
2635                            disposalCandidates: state.dependencyTracking,
2636                            disposalCount: state.dependenciesCount
2637                        };
2638
2639                    ko.dependencyDetection.begin({
2640                        callbackTarget: dependencyDetectionContext,
2641                        callback: computedBeginDependencyDetectionCallback,
2642                        computed: computedObservable,
2643                        isInitial: isInitial
2644                    });
2645
2646                    state.dependencyTracking = {};
2647                    state.dependenciesCount = 0;
2648
2649                    var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext);
2650
2651                    if (!state.dependenciesCount) {
2652                        computedObservable.dispose();
2653                        changed = true; // When evaluation causes a disposal, make sure all dependent computeds get notified so they'll see the new state
2654                    } else {
2655                        changed = computedObservable.isDifferent(state.latestValue, newValue);
2656                    }
2657
2658                    if (changed) {
2659                        if (!state.isSleeping) {
2660                            computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
2661                        } else {
2662                            computedObservable.updateVersion();
2663                        }
2664
2665                        state.latestValue = newValue;
2666                        if (DEBUG) computedObservable._latestValue = newValue;
2667
2668                        computedObservable["notifySubscribers"](state.latestValue, "spectate");
2669
2670                        if (!state.isSleeping && notifyChange) {
2671                            computedObservable["notifySubscribers"](state.latestValue);
2672                        }
2673                        if (computedObservable._recordUpdate) {
2674                            computedObservable._recordUpdate();
2675                        }
2676                    }
2677
2678                    if (isInitial) {
2679                        computedObservable["notifySubscribers"](state.latestValue, "awake");
2680                    }
2681
2682                    return changed;
2683                },
2684                evaluateImmediate_CallReadThenEndDependencyDetection: function (state, dependencyDetectionContext) {
2685                    // This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic.
2686                    // You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection
2687                    // can be independent of try/finally blocks, which contributes to saving about 40% off the CPU
2688                    // overhead of computed evaluation (on V8 at least).
2689
2690                    try {
2691                        var readFunction = state.readFunction;
2692                        return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
2693                    } finally {
2694                        ko.dependencyDetection.end();
2695
2696                        // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
2697                        if (dependencyDetectionContext.disposalCount && !state.isSleeping) {
2698                            ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);
2699                        }
2700
2701                        state.isStale = state.isDirty = false;
2702                    }
2703                },
2704                peek: function (evaluate) {
2705                    // By default, peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set.
2706                    // Pass in true to evaluate if needed.
2707                    var state = this[computedState];
2708                    if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) {
2709                        this.evaluateImmediate();
2710                    }
2711                    return state.latestValue;
2712                },
2713                limit: function (limitFunction) {
2714                    // Override the limit function with one that delays evaluation as well
2715                    ko.subscribable['fn'].limit.call(this, limitFunction);
2716                    this._evalIfChanged = function () {
2717                        if (!this[computedState].isSleeping) {
2718                            if (this[computedState].isStale) {
2719                                this.evaluateImmediate();
2720                            } else {
2721                                this[computedState].isDirty = false;
2722                            }
2723                        }
2724                        return this[computedState].latestValue;
2725                    };
2726                    this._evalDelayed = function (isChange) {
2727                        this._limitBeforeChange(this[computedState].latestValue);
2728
2729                        // Mark as dirty
2730                        this[computedState].isDirty = true;
2731                        if (isChange) {
2732                            this[computedState].isStale = true;
2733                        }
2734
2735                        // Pass the observable to the "limit" code, which will evaluate it when
2736                        // it's time to do the notification.
2737                        this._limitChange(this, !isChange /* isDirty */);
2738                    };
2739                },
2740                dispose: function () {
2741                    var state = this[computedState];
2742                    if (!state.isSleeping && state.dependencyTracking) {
2743                        ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
2744                            if (dependency.dispose)
2745                                dependency.dispose();
2746                        });
2747                    }
2748                    if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {
2749                        ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);
2750                    }
2751                    state.dependencyTracking = undefined;
2752                    state.dependenciesCount = 0;
2753                    state.isDisposed = true;
2754                    state.isStale = false;
2755                    state.isDirty = false;
2756                    state.isSleeping = false;
2757                    state.disposeWhenNodeIsRemoved = undefined;
2758                    state.disposeWhen = undefined;
2759                    state.readFunction = undefined;
2760                    if (!this.hasWriteFunction) {
2761                        state.evaluatorFunctionTarget = undefined;
2762                    }
2763                }
2764            };
2765
2766            var pureComputedOverrides = {
2767                beforeSubscriptionAdd: function (event) {
2768                    // If asleep, wake up the computed by subscribing to any dependencies.
2769                    var computedObservable = this,
2770                        state = computedObservable[computedState];
2771                    if (!state.isDisposed && state.isSleeping && event == 'change') {
2772                        state.isSleeping = false;
2773                        if (state.isStale || computedObservable.haveDependenciesChanged()) {
2774                            state.dependencyTracking = null;
2775                            state.dependenciesCount = 0;
2776                            if (computedObservable.evaluateImmediate()) {
2777                                computedObservable.updateVersion();
2778                            }
2779                        } else {
2780                            // First put the dependencies in order
2781                            var dependenciesOrder = [];
2782                            ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
2783                                dependenciesOrder[dependency._order] = id;
2784                            });
2785                            // Next, subscribe to each one
2786                            ko.utils.arrayForEach(dependenciesOrder, function (id, order) {
2787                                var dependency = state.dependencyTracking[id],
2788                                    subscription = computedObservable.subscribeToDependency(dependency._target);
2789                                subscription._order = order;
2790                                subscription._version = dependency._version;
2791                                state.dependencyTracking[id] = subscription;
2792                            });
2793                            // Waking dependencies may have triggered effects
2794                            if (computedObservable.haveDependenciesChanged()) {
2795                                if (computedObservable.evaluateImmediate()) {
2796                                    computedObservable.updateVersion();
2797                                }
2798                            }
2799                        }
2800
2801                        if (!state.isDisposed) {     // test since evaluating could trigger disposal
2802                            computedObservable["notifySubscribers"](state.latestValue, "awake");
2803                        }
2804                    }
2805                },
2806                afterSubscriptionRemove: function (event) {
2807                    var state = this[computedState];
2808                    if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
2809                        ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
2810                            if (dependency.dispose) {
2811                                state.dependencyTracking[id] = {
2812                                    _target: dependency._target,
2813                                    _order: dependency._order,
2814                                    _version: dependency._version
2815                                };
2816                                dependency.dispose();
2817                            }
2818                        });
2819                        state.isSleeping = true;
2820                        this["notifySubscribers"](undefined, "asleep");
2821                    }
2822                },
2823                getVersion: function () {
2824                    // Because a pure computed is not automatically updated while it is sleeping, we can't
2825                    // simply return the version number. Instead, we check if any of the dependencies have
2826                    // changed and conditionally re-evaluate the computed observable.
2827                    var state = this[computedState];
2828                    if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) {
2829                        this.evaluateImmediate();
2830                    }
2831                    return ko.subscribable['fn'].getVersion.call(this);
2832                }
2833            };
2834
2835            var deferEvaluationOverrides = {
2836                beforeSubscriptionAdd: function (event) {
2837                    // This will force a computed with deferEvaluation to evaluate when the first subscription is registered.
2838                    if (event == 'change' || event == 'beforeChange') {
2839                        this.peek();
2840                    }
2841                }
2842            };
2843
2844            // Note that for browsers that don't support proto assignment, the
2845            // inheritance chain is created manually in the ko.computed constructor
2846            if (ko.utils.canSetPrototype) {
2847                ko.utils.setPrototypeOf(computedFn, ko.subscribable['fn']);
2848            }
2849
2850            // Set the proto values for ko.computed
2851            var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
2852            computedFn[protoProp] = ko.computed;
2853
2854            ko.isComputed = function (instance) {
2855                return (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);
2856            };
2857
2858            ko.isPureComputed = function (instance) {
2859                return ko.isComputed(instance) && instance[computedState] && instance[computedState].pure;
2860            };
2861
2862            ko.exportSymbol('computed', ko.computed);
2863            ko.exportSymbol('dependentObservable', ko.computed);    // export ko.dependentObservable for backwards compatibility (1.x)
2864            ko.exportSymbol('isComputed', ko.isComputed);
2865            ko.exportSymbol('isPureComputed', ko.isPureComputed);
2866            ko.exportSymbol('computed.fn', computedFn);
2867            ko.exportProperty(computedFn, 'peek', computedFn.peek);
2868            ko.exportProperty(computedFn, 'dispose', computedFn.dispose);
2869            ko.exportProperty(computedFn, 'isActive', computedFn.isActive);
2870            ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);
2871            ko.exportProperty(computedFn, 'getDependencies', computedFn.getDependencies);
2872
2873            ko.pureComputed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget) {
2874                if (typeof evaluatorFunctionOrOptions === 'function') {
2875                    return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, { 'pure': true });
2876                } else {
2877                    evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions);   // make a copy of the parameter object
2878                    evaluatorFunctionOrOptions['pure'] = true;
2879                    return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);
2880                }
2881            }
2882            ko.exportSymbol('pureComputed', ko.pureComputed);
2883
2884            (function () {
2885                var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)
2886
2887                ko.toJS = function (rootObject) {
2888                    if (arguments.length == 0)
2889                        throw new Error("When calling ko.toJS, pass the object you want to convert.");
2890
2891                    // We just unwrap everything at every level in the object graph
2892                    return mapJsObjectGraph(rootObject, function (valueToMap) {
2893                        // Loop because an observable's value might in turn be another observable wrapper
2894                        for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
2895                            valueToMap = valueToMap();
2896                        return valueToMap;
2897                    });
2898                };
2899
2900                ko.toJSON = function (rootObject, replacer, space) {     // replacer and space are optional
2901                    var plainJavaScriptObject = ko.toJS(rootObject);
2902                    return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
2903                };
2904
2905                function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
2906                    visitedObjects = visitedObjects || new objectLookup();
2907
2908                    rootObject = mapInputCallback(rootObject);
2909                    var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
2910                    if (!canHaveProperties)
2911                        return rootObject;
2912
2913                    var outputProperties = rootObject instanceof Array ? [] : {};
2914                    visitedObjects.save(rootObject, outputProperties);
2915
2916                    visitPropertiesOrArrayEntries(rootObject, function (indexer) {
2917                        var propertyValue = mapInputCallback(rootObject[indexer]);
2918
2919                        switch (typeof propertyValue) {
2920                            case "boolean":
2921                            case "number":
2922                            case "string":
2923                            case "function":
2924                                outputProperties[indexer] = propertyValue;
2925                                break;
2926                            case "object":
2927                            case "undefined":
2928                                var previouslyMappedValue = visitedObjects.get(propertyValue);
2929                                outputProperties[indexer] = (previouslyMappedValue !== undefined)
2930                                    ? previouslyMappedValue
2931                                    : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
2932                                break;
2933                        }
2934                    });
2935
2936                    return outputProperties;
2937                }
2938
2939                function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
2940                    if (rootObject instanceof Array) {
2941                        for (var i = 0; i < rootObject.length; i++)
2942                            visitorCallback(i);
2943
2944                        // For arrays, also respect toJSON property for custom mappings (fixes #278)
2945                        if (typeof rootObject['toJSON'] == 'function')
2946                            visitorCallback('toJSON');
2947                    } else {
2948                        for (var propertyName in rootObject) {
2949                            visitorCallback(propertyName);
2950                        }
2951                    }
2952                };
2953
2954                function objectLookup() {
2955                    this.keys = [];
2956                    this.values = [];
2957                };
2958
2959                objectLookup.prototype = {
2960                    constructor: objectLookup,
2961                    save: function (key, value) {
2962                        var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
2963                        if (existingIndex >= 0)
2964                            this.values[existingIndex] = value;
2965                        else {
2966                            this.keys.push(key);
2967                            this.values.push(value);
2968                        }
2969                    },
2970                    get: function (key) {
2971                        var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
2972                        return (existingIndex >= 0) ? this.values[existingIndex] : undefined;
2973                    }
2974                };
2975            })();
2976
2977            ko.exportSymbol('toJS', ko.toJS);
2978            ko.exportSymbol('toJSON', ko.toJSON);
2979            ko.when = function (predicate, callback, context) {
2980                function kowhen(resolve) {
2981                    var observable = ko.pureComputed(predicate, context).extend({ notify: 'always' });
2982                    var subscription = observable.subscribe(function (value) {
2983                        if (value) {
2984                            subscription.dispose();
2985                            resolve(value);
2986                        }
2987                    });
2988                    // In case the initial value is true, process it right away
2989                    observable['notifySubscribers'](observable.peek());
2990
2991                    return subscription;
2992                }
2993                if (typeof Promise === "function" && !callback) {
2994                    return new Promise(kowhen);
2995                } else {
2996                    return kowhen(callback.bind(context));
2997                }
2998            };
2999
3000            ko.exportSymbol('when', ko.when);
3001            (function () {
3002                var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
3003
3004                // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
3005                // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
3006                // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
3007                ko.selectExtensions = {
3008                    readValue: function (element) {
3009                        switch (ko.utils.tagNameLower(element)) {
3010                            case 'option':
3011                                if (element[hasDomDataExpandoProperty] === true)
3012                                    return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
3013                                return ko.utils.ieVersion <= 7
3014                                    ? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)
3015                                    : element.value;
3016                            case 'select':
3017                                return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
3018                            default:
3019                                return element.value;
3020                        }
3021                    },
3022
3023                    writeValue: function (element, value, allowUnset) {
3024                        switch (ko.utils.tagNameLower(element)) {
3025                            case 'option':
3026                                if (typeof value === "string") {
3027                                    ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
3028                                    if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
3029                                        delete element[hasDomDataExpandoProperty];
3030                                    }
3031                                    element.value = value;
3032                                }
3033                                else {
3034                                    // Store arbitrary object using DomData
3035                                    ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
3036                                    element[hasDomDataExpandoProperty] = true;
3037
3038                                    // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
3039                                    element.value = typeof value === "number" ? value : "";
3040                                }
3041                                break;
3042                            case 'select':
3043                                if (value === "" || value === null)       // A blank string or null value will select the caption
3044                                    value = undefined;
3045                                var selection = -1;
3046                                for (var i = 0, n = element.options.length, optionValue; i < n; ++i) {
3047                                    optionValue = ko.selectExtensions.readValue(element.options[i]);
3048                                    // Include special check to handle selecting a caption with a blank string value
3049                                    if (optionValue == value || (optionValue === "" && value === undefined)) {
3050                                        selection = i;
3051                                        break;
3052                                    }
3053                                }
3054                                if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) {
3055                                    element.selectedIndex = selection;
3056                                    if (ko.utils.ieVersion === 6) {
3057                                        // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
3058                                        // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
3059                                        // to apply the value as well.
3060                                        ko.utils.setTimeout(function () {
3061                                            element.selectedIndex = selection;
3062                                        }, 0);
3063                                    }
3064                                }
3065                                break;
3066                            default:
3067                                if ((value === null) || (value === undefined))
3068                                    value = "";
3069                                element.value = value;
3070                                break;
3071                        }
3072                    }
3073                };
3074            })();
3075
3076            ko.exportSymbol('selectExtensions', ko.selectExtensions);
3077            ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
3078            ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
3079            ko.expressionRewriting = (function () {
3080                var javaScriptReservedWords = ["true", "false", "null", "undefined"];
3081
3082                // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
3083                // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
3084                // This also will not properly handle nested brackets (e.g., obj1[obj2['prop']]; see #911).
3085                var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
3086
3087                function getWriteableValue(expression) {
3088                    if (ko.utils.arrayIndexOf(javaScriptReservedWords, expression) >= 0)
3089                        return false;
3090                    var match = expression.match(javaScriptAssignmentTarget);
3091                    return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
3092                }
3093
3094                // The following regular expressions will be used to split an object-literal string into tokens
3095
3096                var specials = ',"\'`{}()/:[\\]',    // These characters have special meaning to the parser and must not appear in the middle of a token, except as part of a string.
3097                    // Create the actual regular expression by or-ing the following regex strings. The order is important.
3098                    bindingToken = RegExp([
3099                        // These match strings, either with double quotes, single quotes, or backticks
3100                        '"(?:\\\\.|[^"])*"',
3101                        "'(?:\\\\.|[^'])*'",
3102                        "`(?:\\\\.|[^`])*`",
3103                        // Match C style comments
3104                        "/\\*(?:[^*]|\\*+[^*/])*\\*+/",
3105                        // Match C++ style comments
3106                        "//.*\n",
3107                        // Match a regular expression (text enclosed by slashes), but will also match sets of divisions
3108                        // as a regular expression (this is handled by the parsing loop below).
3109                        '/(?:\\\\.|[^/])+/\w*',
3110                        // Match text (at least two characters) that does not contain any of the above special characters,
3111                        // although some of the special characters are allowed to start it (all but the colon and comma).
3112                        // The text can contain spaces, but leading or trailing spaces are skipped.
3113                        '[^\\s:,/][^' + specials + ']*[^\\s' + specials + ']',
3114                        // Match any non-space character not matched already. This will match colons and commas, since they're
3115                        // not matched by "everyThingElse", but will also match any other single character that wasn't already
3116                        // matched (for example: in "a: 1, b: 2", each of the non-space characters will be matched by oneNotSpace).
3117                        '[^\\s]'
3118                    ].join('|'), 'g'),
3119
3120                    // Match end of previous token to determine whether a slash is a division or regex.
3121                    divisionLookBehind = /[\])"'A-Za-z0-9_$]+$/,
3122                    keywordRegexLookBehind = { 'in': 1, 'return': 1, 'typeof': 1 };
3123
3124                function parseObjectLiteral(objectLiteralString) {
3125                    // Trim leading and trailing spaces from the string
3126                    var str = ko.utils.stringTrim(objectLiteralString);
3127
3128                    // Trim braces '{' surrounding the whole object literal
3129                    if (str.charCodeAt(0) === 123) str = str.slice(1, -1);
3130
3131                    // Add a newline to correctly match a C++ style comment at the end of the string and
3132                    // add a comma so that we don't need a separate code block to deal with the last item
3133                    str += "\n,";
3134
3135                    // Split into tokens
3136                    var result = [], toks = str.match(bindingToken), key, values = [], depth = 0;
3137
3138                    if (toks.length > 1) {
3139                        for (var i = 0, tok; tok = toks[i]; ++i) {
3140                            var c = tok.charCodeAt(0);
3141                            // A comma signals the end of a key/value pair if depth is zero
3142                            if (c === 44) { // ","
3143                                if (depth <= 0) {
3144                                    result.push((key && values.length) ? { key: key, value: values.join('') } : { 'unknown': key || values.join('') });
3145                                    key = depth = 0;
3146                                    values = [];
3147                                    continue;
3148                                }
3149                                // Simply skip the colon that separates the name and value
3150                            } else if (c === 58) { // ":"
3151                                if (!depth && !key && values.length === 1) {
3152                                    key = values.pop();
3153                                    continue;
3154                                }
3155                                // Comments: skip them
3156                            } else if (c === 47 && tok.length > 1 && (tok.charCodeAt(1) === 47 || tok.charCodeAt(1) === 42)) {  // "//" or "/*"
3157                                continue;
3158                                // A set of slashes is initially matched as a regular expression, but could be division
3159                            } else if (c === 47 && i && tok.length > 1) {  // "/"
3160                                // Look at the end of the previous token to determine if the slash is actually division
3161                                var match = toks[i - 1].match(divisionLookBehind);
3162                                if (match && !keywordRegexLookBehind[match[0]]) {
3163                                    // The slash is actually a division punctuator; re-parse the remainder of the string (not including the slash)
3164                                    str = str.substr(str.indexOf(tok) + 1);
3165                                    toks = str.match(bindingToken);
3166                                    i = -1;
3167                                    // Continue with just the slash
3168                                    tok = '/';
3169                                }
3170                                // Increment depth for parentheses, braces, and brackets so that interior commas are ignored
3171                            } else if (c === 40 || c === 123 || c === 91) { // '(', '{', '['
3172                                ++depth;
3173                            } else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']'
3174                                --depth;
3175                                // The key will be the first token; if it's a string, trim the quotes
3176                            } else if (!key && !values.length && (c === 34 || c === 39)) { // '"', "'"
3177                                tok = tok.slice(1, -1);
3178                            }
3179                            values.push(tok);
3180                        }
3181                        if (depth > 0) {
3182                            throw Error("Unbalanced parentheses, braces, or brackets");
3183                        }
3184                    }
3185                    return result;
3186                }
3187
3188                // Two-way bindings include a write function that allow the handler to update the value even if it's not an observable.
3189                var twoWayBindings = {};
3190
3191                function preProcessBindings(bindingsStringOrKeyValueArray, bindingOptions) {
3192                    bindingOptions = bindingOptions || {};
3193
3194                    function processKeyValue(key, val) {
3195                        var writableVal;
3196                        function callPreprocessHook(obj) {
3197                            return (obj && obj['preprocess']) ? (val = obj['preprocess'](val, key, processKeyValue)) : true;
3198                        }
3199                        if (!bindingParams) {
3200                            if (!callPreprocessHook(ko['getBindingHandler'](key)))
3201                                return;
3202
3203                            if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
3204                                // For two-way bindings, provide a write method in case the value
3205                                // isn't a writable observable.
3206                                var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key;
3207                                propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + writableVal + "=_z}");
3208                            }
3209                        }
3210                        // Values are wrapped in a function so that each value can be accessed independently
3211                        if (makeValueAccessors) {
3212                            val = 'function(){return ' + val + ' }';
3213                        }
3214                        resultStrings.push("'" + key + "':" + val);
3215                    }
3216
3217                    var resultStrings = [],
3218                        propertyAccessorResultStrings = [],
3219                        makeValueAccessors = bindingOptions['valueAccessors'],
3220                        bindingParams = bindingOptions['bindingParams'],
3221                        keyValueArray = typeof bindingsStringOrKeyValueArray === "string" ?
3222                            parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray;
3223
3224                    ko.utils.arrayForEach(keyValueArray, function (keyValue) {
3225                        processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value);
3226                    });
3227
3228                    if (propertyAccessorResultStrings.length)
3229                        processKeyValue('_ko_property_writers', "{" + propertyAccessorResultStrings.join(",") + " }");
3230
3231                    return resultStrings.join(",");
3232                }
3233
3234                return {
3235                    bindingRewriteValidators: [],
3236
3237                    twoWayBindings: twoWayBindings,
3238
3239                    parseObjectLiteral: parseObjectLiteral,
3240
3241                    preProcessBindings: preProcessBindings,
3242
3243                    keyValueArrayContainsKey: function (keyValueArray, key) {
3244                        for (var i = 0; i < keyValueArray.length; i++)
3245                            if (keyValueArray[i]['key'] == key)
3246                                return true;
3247                        return false;
3248                    },
3249
3250                    // Internal, private KO utility for updating model properties from within bindings
3251                    // property:            If the property being updated is (or might be) an observable, pass it here
3252                    //                      If it turns out to be a writable observable, it will be written to directly
3253                    // allBindings:         An object with a get method to retrieve bindings in the current execution context.
3254                    //                      This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
3255                    // key:                 The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
3256                    // value:               The value to be written
3257                    // checkIfDifferent:    If true, and if the property being written is a writable observable, the value will only be written if
3258                    //                      it is !== existing value on that writable observable
3259                    writeValueToProperty: function (property, allBindings, key, value, checkIfDifferent) {
3260                        if (!property || !ko.isObservable(property)) {
3261                            var propWriters = allBindings.get('_ko_property_writers');
3262                            if (propWriters && propWriters[key])
3263                                propWriters[key](value);
3264                        } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
3265                            property(value);
3266                        }
3267                    }
3268                };
3269            })();
3270
3271            ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
3272            ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
3273            ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
3274            ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
3275
3276            // Making bindings explicitly declare themselves as "two way" isn't ideal in the long term (it would be better if
3277            // all bindings could use an official 'property writer' API without needing to declare that they might). However,
3278            // since this is not, and has never been, a public API (_ko_property_writers was never documented), it's acceptable
3279            // as an internal implementation detail in the short term.
3280            // For those developers who rely on _ko_property_writers in their custom bindings, we expose _twoWayBindings as an
3281            // undocumented feature that makes it relatively easy to upgrade to KO 3.0. However, this is still not an official
3282            // public API, and we reserve the right to remove it at any time if we create a real public property writers API.
3283            ko.exportSymbol('expressionRewriting._twoWayBindings', ko.expressionRewriting.twoWayBindings);
3284
3285            // For backward compatibility, define the following aliases. (Previously, these function names were misleading because
3286            // they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
3287            ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
3288            ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);
3289            (function () {
3290                // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
3291                // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
3292                // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
3293                // of that virtual hierarchy
3294                //
3295                // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
3296                // without having to scatter special cases all over the binding and templating code.
3297
3298                // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
3299                // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
3300                // So, use node.text where available, and node.nodeValue elsewhere
3301                var commentNodesHaveTextProperty = document && document.createComment("test").text === "<!--test-->";
3302
3303                var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+([\s\S]+))?\s*-->$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/;
3304                var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
3305                var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
3306
3307                function isStartComment(node) {
3308                    return (node.nodeType == 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
3309                }
3310
3311                function isEndComment(node) {
3312                    return (node.nodeType == 8) && endCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
3313                }
3314
3315                function isUnmatchedEndComment(node) {
3316                    return isEndComment(node) && !(ko.utils.domData.get(node, matchedEndCommentDataKey));
3317                }
3318
3319                var matchedEndCommentDataKey = "__ko_matchedEndComment__"
3320
3321                function getVirtualChildren(startComment, allowUnbalanced) {
3322                    var currentNode = startComment;
3323                    var depth = 1;
3324                    var children = [];
3325                    while (currentNode = currentNode.nextSibling) {
3326                        if (isEndComment(currentNode)) {
3327                            ko.utils.domData.set(currentNode, matchedEndCommentDataKey, true);
3328                            depth--;
3329                            if (depth === 0)
3330                                return children;
3331                        }
3332
3333                        children.push(currentNode);
3334
3335                        if (isStartComment(currentNode))
3336                            depth++;
3337                    }
3338                    if (!allowUnbalanced)
3339                        throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
3340                    return null;
3341                }
3342
3343                function getMatchingEndComment(startComment, allowUnbalanced) {
3344                    var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
3345                    if (allVirtualChildren) {
3346                        if (allVirtualChildren.length > 0)
3347                            return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
3348                        return startComment.nextSibling;
3349                    } else
3350                        return null; // Must have no matching end comment, and allowUnbalanced is true
3351                }
3352
3353                function getUnbalancedChildTags(node) {
3354                    // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
3355                    //       from <div>OK</div><!-- /ko --><!-- /ko -->,             returns: <!-- /ko --><!-- /ko -->
3356                    var childNode = node.firstChild, captureRemaining = null;
3357                    if (childNode) {
3358                        do {
3359                            if (captureRemaining)                   // We already hit an unbalanced node and are now just scooping up all subsequent nodes
3360                                captureRemaining.push(childNode);
3361                            else if (isStartComment(childNode)) {
3362                                var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
3363                                if (matchingEndComment)             // It's a balanced tag, so skip immediately to the end of this virtual set
3364                                    childNode = matchingEndComment;
3365                                else
3366                                    captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
3367                            } else if (isEndComment(childNode)) {
3368                                captureRemaining = [childNode];     // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
3369                            }
3370                        } while (childNode = childNode.nextSibling);
3371                    }
3372                    return captureRemaining;
3373                }
3374
3375                ko.virtualElements = {
3376                    allowedBindings: {},
3377
3378                    childNodes: function (node) {
3379                        return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
3380                    },
3381
3382                    emptyNode: function (node) {
3383                        if (!isStartComment(node))
3384                            ko.utils.emptyDomNode(node);
3385                        else {
3386                            var virtualChildren = ko.virtualElements.childNodes(node);
3387                            for (var i = 0, j = virtualChildren.length; i < j; i++)
3388                                ko.removeNode(virtualChildren[i]);
3389                        }
3390                    },
3391
3392                    setDomNodeChildren: function (node, childNodes) {
3393                        if (!isStartComment(node))
3394                            ko.utils.setDomNodeChildren(node, childNodes);
3395                        else {
3396                            ko.virtualElements.emptyNode(node);
3397                            var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
3398                            for (var i = 0, j = childNodes.length; i < j; i++)
3399                                endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
3400                        }
3401                    },
3402
3403                    prepend: function (containerNode, nodeToPrepend) {
3404                        var insertBeforeNode;
3405
3406                        if (isStartComment(containerNode)) {
3407                            // Start comments must always have a parent and at least one following sibling (the end comment)
3408                            insertBeforeNode = containerNode.nextSibling;
3409                            containerNode = containerNode.parentNode;
3410                        } else {
3411                            insertBeforeNode = containerNode.firstChild;
3412                        }
3413
3414                        if (!insertBeforeNode) {
3415                            containerNode.appendChild(nodeToPrepend);
3416                        } else if (nodeToPrepend !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself
3417                            containerNode.insertBefore(nodeToPrepend, insertBeforeNode);
3418                        }
3419                    },
3420
3421                    insertAfter: function (containerNode, nodeToInsert, insertAfterNode) {
3422                        if (!insertAfterNode) {
3423                            ko.virtualElements.prepend(containerNode, nodeToInsert);
3424                        } else {
3425                            // Children of start comments must always have a parent and at least one following sibling (the end comment)
3426                            var insertBeforeNode = insertAfterNode.nextSibling;
3427
3428                            if (isStartComment(containerNode)) {
3429                                containerNode = containerNode.parentNode;
3430                            }
3431
3432                            if (!insertBeforeNode) {
3433                                containerNode.appendChild(nodeToInsert);
3434                            } else if (nodeToInsert !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself
3435                                containerNode.insertBefore(nodeToInsert, insertBeforeNode);
3436                            }
3437                        }
3438                    },
3439
3440                    firstChild: function (node) {
3441                        if (!isStartComment(node)) {
3442                            if (node.firstChild && isEndComment(node.firstChild)) {
3443                                throw new Error("Found invalid end comment, as the first child of " + node);
3444                            }
3445                            return node.firstChild;
3446                        } else if (!node.nextSibling || isEndComment(node.nextSibling)) {
3447                            return null;
3448                        } else {
3449                            return node.nextSibling;
3450                        }
3451                    },
3452
3453                    nextSibling: function (node) {
3454                        if (isStartComment(node)) {
3455                            node = getMatchingEndComment(node);
3456                        }
3457
3458                        if (node.nextSibling && isEndComment(node.nextSibling)) {
3459                            if (isUnmatchedEndComment(node.nextSibling)) {
3460                                throw Error("Found end comment without a matching opening comment, as child of " + node);
3461                            } else {
3462                                return null;
3463                            }
3464                        } else {
3465                            return node.nextSibling;
3466                        }
3467                    },
3468
3469                    hasBindingValue: isStartComment,
3470
3471                    virtualNodeBindingValue: function (node) {
3472                        var regexMatch = (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
3473                        return regexMatch ? regexMatch[1] : null;
3474                    },
3475
3476                    normaliseVirtualElementDomStructure: function (elementVerified) {
3477                        // Workaround for https://github.com/SteveSanderson/knockout/issues/155
3478                        // (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
3479                        // that are direct descendants of <ul> into the preceding <li>)
3480                        if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
3481                            return;
3482
3483                        // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
3484                        // must be intended to appear *after* that child, so move them there.
3485                        var childNode = elementVerified.firstChild;
3486                        if (childNode) {
3487                            do {
3488                                if (childNode.nodeType === 1) {
3489                                    var unbalancedTags = getUnbalancedChildTags(childNode);
3490                                    if (unbalancedTags) {
3491                                        // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
3492                                        var nodeToInsertBefore = childNode.nextSibling;
3493                                        for (var i = 0; i < unbalancedTags.length; i++) {
3494                                            if (nodeToInsertBefore)
3495                                                elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
3496                                            else
3497                                                elementVerified.appendChild(unbalancedTags[i]);
3498                                        }
3499                                    }
3500                                }
3501                            } while (childNode = childNode.nextSibling);
3502                        }
3503                    }
3504                };
3505            })();
3506            ko.exportSymbol('virtualElements', ko.virtualElements);
3507            ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
3508            ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
3509            //ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild);     // firstChild is not minified
3510            ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
3511            //ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling);   // nextSibling is not minified
3512            ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
3513            ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
3514            (function () {
3515                var defaultBindingAttributeName = "data-bind";
3516
3517                ko.bindingProvider = function () {
3518                    this.bindingCache = {};
3519                };
3520
3521                ko.utils.extend(ko.bindingProvider.prototype, {
3522                    'nodeHasBindings': function (node) {
3523                        switch (node.nodeType) {
3524                            case 1: // Element
3525                                return node.getAttribute(defaultBindingAttributeName) != null
3526                                    || ko.components['getComponentNameForNode'](node);
3527                            case 8: // Comment node
3528                                return ko.virtualElements.hasBindingValue(node);
3529                            default: return false;
3530                        }
3531                    },
3532
3533                    'getBindings': function (node, bindingContext) {
3534                        var bindingsString = this['getBindingsString'](node, bindingContext),
3535                            parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
3536                        return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ false);
3537                    },
3538
3539                    'getBindingAccessors': function (node, bindingContext) {
3540                        var bindingsString = this['getBindingsString'](node, bindingContext),
3541                            parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node, { 'valueAccessors': true }) : null;
3542                        return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ true);
3543                    },
3544
3545                    // The following function is only used internally by this default provider.
3546                    // It's not part of the interface definition for a general binding provider.
3547                    'getBindingsString': function (node, bindingContext) {
3548                        switch (node.nodeType) {
3549                            case 1: return node.getAttribute(defaultBindingAttributeName);   // Element
3550                            case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
3551                            default: return null;
3552                        }
3553                    },
3554
3555                    // The following function is only used internally by this default provider.
3556                    // It's not part of the interface definition for a general binding provider.
3557                    'parseBindingsString': function (bindingsString, bindingContext, node, options) {
3558                        try {
3559                            var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache, options);
3560                            return bindingFunction(bindingContext, node);
3561                        } catch (ex) {
3562                            ex.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + ex.message;
3563                            throw ex;
3564                        }
3565                    }
3566                });
3567
3568                ko.bindingProvider['instance'] = new ko.bindingProvider();
3569
3570                function createBindingsStringEvaluatorViaCache(bindingsString, cache, options) {
3571                    var cacheKey = bindingsString + (options && options['valueAccessors'] || '');
3572                    return cache[cacheKey]
3573                        || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options));
3574                }
3575
3576                function createBindingsStringEvaluator(bindingsString, options) {
3577                    // Build the source for a function that evaluates "expression"
3578                    // For each scope variable, add an extra level of "with" nesting
3579                    // Example result: with(sc1) { with(sc0) { return (expression) } }
3580                    var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options),
3581                        functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
3582                    return new Function("$context", "$element", functionBody);
3583                }
3584            })();
3585
3586            ko.exportSymbol('bindingProvider', ko.bindingProvider);
3587            (function () {
3588                // Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294
3589                var contextSubscribable = ko.utils.createSymbolOrString('_subscribable');
3590                var contextAncestorBindingInfo = ko.utils.createSymbolOrString('_ancestorBindingInfo');
3591                var contextDataDependency = ko.utils.createSymbolOrString('_dataDependency');
3592
3593                ko.bindingHandlers = {};
3594
3595                // The following element types will not be recursed into during binding.
3596                var bindingDoesNotRecurseIntoElementTypes = {
3597                    // Don't want bindings that operate on text nodes to mutate <script> and <textarea> contents,
3598                    // because it's unexpected and a potential XSS issue.
3599                    // Also bindings should not operate on <template> elements since this breaks in Internet Explorer
3600                    // and because such elements' contents are always intended to be bound in a different context
3601                    // from where they appear in the document.
3602                    'script': true,
3603                    'textarea': true,
3604                    'template': true
3605                };
3606
3607                // Use an overridable method for retrieving binding handlers so that plugins may support dynamically created handlers
3608                ko['getBindingHandler'] = function (bindingKey) {
3609                    return ko.bindingHandlers[bindingKey];
3610                };
3611
3612                var inheritParentVm = {};
3613
3614                // The ko.bindingContext constructor is only called directly to create the root context. For child
3615                // contexts, use bindingContext.createChildContext or bindingContext.extend.
3616                ko.bindingContext = function (dataItemOrAccessor, parentContext, dataItemAlias, extendCallback, options) {
3617
3618                    // The binding context object includes static properties for the current, parent, and root view models.
3619                    // If a view model is actually stored in an observable, the corresponding binding context object, and
3620                    // any child contexts, must be updated when the view model is changed.
3621                    function updateContext() {
3622                        // Most of the time, the context will directly get a view model object, but if a function is given,
3623                        // we call the function to retrieve the view model. If the function accesses any observables or returns
3624                        // an observable, the dependency is tracked, and those observables can later cause the binding
3625                        // context to be updated.
3626                        var dataItemOrObservable = isFunc ? realDataItemOrAccessor() : realDataItemOrAccessor,
3627                            dataItem = ko.utils.unwrapObservable(dataItemOrObservable);
3628
3629                        if (parentContext) {
3630                            // Copy $root and any custom properties from the parent context
3631                            ko.utils.extend(self, parentContext);
3632
3633                            // Copy Symbol properties
3634                            if (contextAncestorBindingInfo in parentContext) {
3635                                self[contextAncestorBindingInfo] = parentContext[contextAncestorBindingInfo];
3636                            }
3637                        } else {
3638                            self['$parents'] = [];
3639                            self['$root'] = dataItem;
3640
3641                            // Export 'ko' in the binding context so it will be available in bindings and templates
3642                            // even if 'ko' isn't exported as a global, such as when using an AMD loader.
3643                            // See https://github.com/SteveSanderson/knockout/issues/490
3644                            self['ko'] = ko;
3645                        }
3646
3647                        self[contextSubscribable] = subscribable;
3648
3649                        if (shouldInheritData) {
3650                            dataItem = self['$data'];
3651                        } else {
3652                            self['$rawData'] = dataItemOrObservable;
3653                            self['$data'] = dataItem;
3654                        }
3655
3656                        if (dataItemAlias)
3657                            self[dataItemAlias] = dataItem;
3658
3659                        // The extendCallback function is provided when creating a child context or extending a context.
3660                        // It handles the specific actions needed to finish setting up the binding context. Actions in this
3661                        // function could also add dependencies to this binding context.
3662                        if (extendCallback)
3663                            extendCallback(self, parentContext, dataItem);
3664
3665                        // When a "parent" context is given and we don't already have a dependency on its context, register a dependency on it.
3666                        // Thus whenever the parent context is updated, this context will also be updated.
3667                        if (parentContext && parentContext[contextSubscribable] && !ko.computedContext.computed().hasAncestorDependency(parentContext[contextSubscribable])) {
3668                            parentContext[contextSubscribable]();
3669                        }
3670
3671                        if (dataDependency) {
3672                            self[contextDataDependency] = dataDependency;
3673                        }
3674
3675                        return self['$data'];
3676                    }
3677
3678                    var self = this,
3679                        shouldInheritData = dataItemOrAccessor === inheritParentVm,
3680                        realDataItemOrAccessor = shouldInheritData ? undefined : dataItemOrAccessor,
3681                        isFunc = typeof (realDataItemOrAccessor) == "function" && !ko.isObservable(realDataItemOrAccessor),
3682                        nodes,
3683                        subscribable,
3684                        dataDependency = options && options['dataDependency'];
3685
3686                    if (options && options['exportDependencies']) {
3687                        // The "exportDependencies" option means that the calling code will track any dependencies and re-create
3688                        // the binding context when they change.
3689                        updateContext();
3690                    } else {
3691                        subscribable = ko.pureComputed(updateContext);
3692                        subscribable.peek();
3693
3694                        // At this point, the binding context has been initialized, and the "subscribable" computed observable is
3695                        // subscribed to any observables that were accessed in the process. If there is nothing to track, the
3696                        // computed will be inactive, and we can safely throw it away. If it's active, the computed is stored in
3697                        // the context object.
3698                        if (subscribable.isActive()) {
3699                            // Always notify because even if the model ($data) hasn't changed, other context properties might have changed
3700                            subscribable['equalityComparer'] = null;
3701                        } else {
3702                            self[contextSubscribable] = undefined;
3703                        }
3704                    }
3705                }
3706
3707                // Extend the binding context hierarchy with a new view model object. If the parent context is watching
3708                // any observables, the new child context will automatically get a dependency on the parent context.
3709                // But this does not mean that the $data value of the child context will also get updated. If the child
3710                // view model also depends on the parent view model, you must provide a function that returns the correct
3711                // view model on each update.
3712                ko.bindingContext.prototype['createChildContext'] = function (dataItemOrAccessor, dataItemAlias, extendCallback, options) {
3713                    if (!options && dataItemAlias && typeof dataItemAlias == "object") {
3714                        options = dataItemAlias;
3715                        dataItemAlias = options['as'];
3716                        extendCallback = options['extend'];
3717                    }
3718
3719                    if (dataItemAlias && options && options['noChildContext']) {
3720                        var isFunc = typeof (dataItemOrAccessor) == "function" && !ko.isObservable(dataItemOrAccessor);
3721                        return new ko.bindingContext(inheritParentVm, this, null, function (self) {
3722                            if (extendCallback)
3723                                extendCallback(self);
3724                            self[dataItemAlias] = isFunc ? dataItemOrAccessor() : dataItemOrAccessor;
3725                        }, options);
3726                    }
3727
3728                    return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, function (self, parentContext) {
3729                        // Extend the context hierarchy by setting the appropriate pointers
3730                        self['$parentContext'] = parentContext;
3731                        self['$parent'] = parentContext['$data'];
3732                        self['$parents'] = (parentContext['$parents'] || []).slice(0);
3733                        self['$parents'].unshift(self['$parent']);
3734                        if (extendCallback)
3735                            extendCallback(self);
3736                    }, options);
3737                };
3738
3739                // Extend the binding context with new custom properties. This doesn't change the context hierarchy.
3740                // Similarly to "child" contexts, provide a function here to make sure that the correct values are set
3741                // when an observable view model is updated.
3742                ko.bindingContext.prototype['extend'] = function (properties, options) {
3743                    return new ko.bindingContext(inheritParentVm, this, null, function (self, parentContext) {
3744                        ko.utils.extend(self, typeof (properties) == "function" ? properties(self) : properties);
3745                    }, options);
3746                };
3747
3748                var boundElementDomDataKey = ko.utils.domData.nextKey();
3749
3750                function asyncContextDispose(node) {
3751                    var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey),
3752                        asyncContext = bindingInfo && bindingInfo.asyncContext;
3753                    if (asyncContext) {
3754                        bindingInfo.asyncContext = null;
3755                        asyncContext.notifyAncestor();
3756                    }
3757                }
3758                function AsyncCompleteContext(node, bindingInfo, ancestorBindingInfo) {
3759                    this.node = node;
3760                    this.bindingInfo = bindingInfo;
3761                    this.asyncDescendants = [];
3762                    this.childrenComplete = false;
3763
3764                    if (!bindingInfo.asyncContext) {
3765                        ko.utils.domNodeDisposal.addDisposeCallback(node, asyncContextDispose);
3766                    }
3767
3768                    if (ancestorBindingInfo && ancestorBindingInfo.asyncContext) {
3769                        ancestorBindingInfo.asyncContext.asyncDescendants.push(node);
3770                        this.ancestorBindingInfo = ancestorBindingInfo;
3771                    }
3772                }
3773                AsyncCompleteContext.prototype.notifyAncestor = function () {
3774                    if (this.ancestorBindingInfo && this.ancestorBindingInfo.asyncContext) {
3775                        this.ancestorBindingInfo.asyncContext.descendantComplete(this.node);
3776                    }
3777                };
3778                AsyncCompleteContext.prototype.descendantComplete = function (node) {
3779                    ko.utils.arrayRemoveItem(this.asyncDescendants, node);
3780                    if (!this.asyncDescendants.length && this.childrenComplete) {
3781                        this.completeChildren();
3782                    }
3783                };
3784                AsyncCompleteContext.prototype.completeChildren = function () {
3785                    this.childrenComplete = true;
3786                    if (this.bindingInfo.asyncContext && !this.asyncDescendants.length) {
3787                        this.bindingInfo.asyncContext = null;
3788                        ko.utils.domNodeDisposal.removeDisposeCallback(this.node, asyncContextDispose);
3789                        ko.bindingEvent.notify(this.node, ko.bindingEvent.descendantsComplete);
3790                        this.notifyAncestor();
3791                    }
3792                };
3793
3794                ko.bindingEvent = {
3795                    childrenComplete: "childrenComplete",
3796                    descendantsComplete: "descendantsComplete",
3797
3798                    subscribe: function (node, event, callback, context, options) {
3799                        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
3800                        if (!bindingInfo.eventSubscribable) {
3801                            bindingInfo.eventSubscribable = new ko.subscribable;
3802                        }
3803                        if (options && options['notifyImmediately'] && bindingInfo.notifiedEvents[event]) {
3804                            ko.dependencyDetection.ignore(callback, context, [node]);
3805                        }
3806                        return bindingInfo.eventSubscribable.subscribe(callback, context, event);
3807                    },
3808
3809                    notify: function (node, event) {
3810                        var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
3811                        if (bindingInfo) {
3812                            bindingInfo.notifiedEvents[event] = true;
3813                            if (bindingInfo.eventSubscribable) {
3814                                bindingInfo.eventSubscribable['notifySubscribers'](node, event);
3815                            }
3816                            if (event == ko.bindingEvent.childrenComplete) {
3817                                if (bindingInfo.asyncContext) {
3818                                    bindingInfo.asyncContext.completeChildren();
3819                                } else if (bindingInfo.asyncContext === undefined && bindingInfo.eventSubscribable && bindingInfo.eventSubscribable.hasSubscriptionsForEvent(ko.bindingEvent.descendantsComplete)) {
3820                                    // It's currently an error to register a descendantsComplete handler for a node that was never registered as completing asynchronously.
3821                                    // That's because without the asyncContext, we don't have a way to know that all descendants have completed.
3822                                    throw new Error("descendantsComplete event not supported for bindings on this node");
3823                                }
3824                            }
3825                        }
3826                    },
3827
3828                    startPossiblyAsyncContentBinding: function (node, bindingContext) {
3829                        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
3830
3831                        if (!bindingInfo.asyncContext) {
3832                            bindingInfo.asyncContext = new AsyncCompleteContext(node, bindingInfo, bindingContext[contextAncestorBindingInfo]);
3833                        }
3834
3835                        // If the provided context was already extended with this node's binding info, just return the extended context
3836                        if (bindingContext[contextAncestorBindingInfo] == bindingInfo) {
3837                            return bindingContext;
3838                        }
3839
3840                        return bindingContext['extend'](function (ctx) {
3841                            ctx[contextAncestorBindingInfo] = bindingInfo;
3842                        });
3843                    }
3844                };
3845
3846                // Returns the valueAccessor function for a binding value
3847                function makeValueAccessor(value) {
3848                    return function () {
3849                        return value;
3850                    };
3851                }
3852
3853                // Returns the value of a valueAccessor function
3854                function evaluateValueAccessor(valueAccessor) {
3855                    return valueAccessor();
3856                }
3857
3858                // Given a function that returns bindings, create and return a new object that contains
3859                // binding value-accessors functions. Each accessor function calls the original function
3860                // so that it always gets the latest value and all dependencies are captured. This is used
3861                // by ko.applyBindingsToNode and getBindingsAndMakeAccessors.
3862                function makeAccessorsFromFunction(callback) {
3863                    return ko.utils.objectMap(ko.dependencyDetection.ignore(callback), function (value, key) {
3864                        return function () {
3865                            return callback()[key];
3866                        };
3867                    });
3868                }
3869
3870                // Given a bindings function or object, create and return a new object that contains
3871                // binding value-accessors functions. This is used by ko.applyBindingsToNode.
3872                function makeBindingAccessors(bindings, context, node) {
3873                    if (typeof bindings === 'function') {
3874                        return makeAccessorsFromFunction(bindings.bind(null, context, node));
3875                    } else {
3876                        return ko.utils.objectMap(bindings, makeValueAccessor);
3877                    }
3878                }
3879
3880                // This function is used if the binding provider doesn't include a getBindingAccessors function.
3881                // It must be called with 'this' set to the provider instance.
3882                function getBindingsAndMakeAccessors(node, context) {
3883                    return makeAccessorsFromFunction(this['getBindings'].bind(this, node, context));
3884                }
3885
3886                function validateThatBindingIsAllowedForVirtualElements(bindingName) {
3887                    var validator = ko.virtualElements.allowedBindings[bindingName];
3888                    if (!validator)
3889                        throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
3890                }
3891
3892                function applyBindingsToDescendantsInternal(bindingContext, elementOrVirtualElement) {
3893                    var nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
3894
3895                    if (nextInQueue) {
3896                        var currentChild,
3897                            provider = ko.bindingProvider['instance'],
3898                            preprocessNode = provider['preprocessNode'];
3899
3900                        // Preprocessing allows a binding provider to mutate a node before bindings are applied to it. For example it's
3901                        // possible to insert new siblings after it, and/or replace the node with a different one. This can be used to
3902                        // implement custom binding syntaxes, such as {{ value }} for string interpolation, or custom element types that
3903                        // trigger insertion of <template> contents at that point in the document.
3904                        if (preprocessNode) {
3905                            while (currentChild = nextInQueue) {
3906                                nextInQueue = ko.virtualElements.nextSibling(currentChild);
3907                                preprocessNode.call(provider, currentChild);
3908                            }
3909                            // Reset nextInQueue for the next loop
3910                            nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
3911                        }
3912
3913                        while (currentChild = nextInQueue) {
3914                            // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
3915                            nextInQueue = ko.virtualElements.nextSibling(currentChild);
3916                            applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild);
3917                        }
3918                    }
3919                    ko.bindingEvent.notify(elementOrVirtualElement, ko.bindingEvent.childrenComplete);
3920                }
3921
3922                function applyBindingsToNodeAndDescendantsInternal(bindingContext, nodeVerified) {
3923                    var bindingContextForDescendants = bindingContext;
3924
3925                    var isElement = (nodeVerified.nodeType === 1);
3926                    if (isElement) // Workaround IE <= 8 HTML parsing weirdness
3927                        ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
3928
3929                    // Perf optimisation: Apply bindings only if...
3930                    // (1) We need to store the binding info for the node (all element nodes)
3931                    // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
3932                    var shouldApplyBindings = isElement || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified);
3933                    if (shouldApplyBindings)
3934                        bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext)['bindingContextForDescendants'];
3935
3936                    if (bindingContextForDescendants && !bindingDoesNotRecurseIntoElementTypes[ko.utils.tagNameLower(nodeVerified)]) {
3937                        applyBindingsToDescendantsInternal(bindingContextForDescendants, nodeVerified);
3938                    }
3939                }
3940
3941                function topologicalSortBindings(bindings) {
3942                    // Depth-first sort
3943                    var result = [],                // The list of key/handler pairs that we will return
3944                        bindingsConsidered = {},    // A temporary record of which bindings are already in 'result'
3945                        cyclicDependencyStack = []; // Keeps track of a depth-search so that, if there's a cycle, we know which bindings caused it
3946                    ko.utils.objectForEach(bindings, function pushBinding(bindingKey) {
3947                        if (!bindingsConsidered[bindingKey]) {
3948                            var binding = ko['getBindingHandler'](bindingKey);
3949                            if (binding) {
3950                                // First add dependencies (if any) of the current binding
3951                                if (binding['after']) {
3952                                    cyclicDependencyStack.push(bindingKey);
3953                                    ko.utils.arrayForEach(binding['after'], function (bindingDependencyKey) {
3954                                        if (bindings[bindingDependencyKey]) {
3955                                            if (ko.utils.arrayIndexOf(cyclicDependencyStack, bindingDependencyKey) !== -1) {
3956                                                throw Error("Cannot combine the following bindings, because they have a cyclic dependency: " + cyclicDependencyStack.join(", "));
3957                                            } else {
3958                                                pushBinding(bindingDependencyKey);
3959                                            }
3960                                        }
3961                                    });
3962                                    cyclicDependencyStack.length--;
3963                                }
3964                                // Next add the current binding
3965                                result.push({ key: bindingKey, handler: binding });
3966                            }
3967                            bindingsConsidered[bindingKey] = true;
3968                        }
3969                    });
3970
3971                    return result;
3972                }
3973
3974                function applyBindingsToNodeInternal(node, sourceBindings, bindingContext) {
3975                    var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
3976
3977                    // Prevent multiple applyBindings calls for the same node, except when a binding value is specified
3978                    var alreadyBound = bindingInfo.alreadyBound;
3979                    if (!sourceBindings) {
3980                        if (alreadyBound) {
3981                            throw Error("You cannot apply bindings multiple times to the same element.");
3982                        }
3983                        bindingInfo.alreadyBound = true;
3984                    }
3985                    if (!alreadyBound) {
3986                        bindingInfo.context = bindingContext;
3987                    }
3988                    if (!bindingInfo.notifiedEvents) {
3989                        bindingInfo.notifiedEvents = {};
3990                    }
3991
3992                    // Use bindings if given, otherwise fall back on asking the bindings provider to give us some bindings
3993                    var bindings;
3994                    if (sourceBindings && typeof sourceBindings !== 'function') {
3995                        bindings = sourceBindings;
3996                    } else {
3997                        var provider = ko.bindingProvider['instance'],
3998                            getBindings = provider['getBindingAccessors'] || getBindingsAndMakeAccessors;
3999
4000                        // Get the binding from the provider within a computed observable so that we can update the bindings whenever
4001                        // the binding context is updated or if the binding provider accesses observables.
4002                        var bindingsUpdater = ko.dependentObservable(
4003                            function () {
4004                                bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext);
4005                                // Register a dependency on the binding context to support observable view models.
4006                                if (bindings) {
4007                                    if (bindingContext[contextSubscribable]) {
4008                                        bindingContext[contextSubscribable]();
4009                                    }
4010                                    if (bindingContext[contextDataDependency]) {
4011                                        bindingContext[contextDataDependency]();
4012                                    }
4013                                }
4014                                return bindings;
4015                            },
4016                            null, { disposeWhenNodeIsRemoved: node }
4017                        );
4018
4019                        if (!bindings || !bindingsUpdater.isActive())
4020                            bindingsUpdater = null;
4021                    }
4022
4023                    var contextToExtend = bindingContext;
4024                    var bindingHandlerThatControlsDescendantBindings;
4025                    if (bindings) {
4026                        // Return the value accessor for a given binding. When bindings are static (won't be updated because of a binding
4027                        // context update), just return the value accessor from the binding. Otherwise, return a function that always gets
4028                        // the latest binding value and registers a dependency on the binding updater.
4029                        var getValueAccessor = bindingsUpdater
4030                            ? function (bindingKey) {
4031                                return function () {
4032                                    return evaluateValueAccessor(bindingsUpdater()[bindingKey]);
4033                                };
4034                            } : function (bindingKey) {
4035                                return bindings[bindingKey];
4036                            };
4037
4038                        // Use of allBindings as a function is maintained for backwards compatibility, but its use is deprecated
4039                        function allBindings() {
4040                            return ko.utils.objectMap(bindingsUpdater ? bindingsUpdater() : bindings, evaluateValueAccessor);
4041                        }
4042                        // The following is the 3.x allBindings API
4043                        allBindings['get'] = function (key) {
4044                            return bindings[key] && evaluateValueAccessor(getValueAccessor(key));
4045                        };
4046                        allBindings['has'] = function (key) {
4047                            return key in bindings;
4048                        };
4049
4050                        if (ko.bindingEvent.childrenComplete in bindings) {
4051                            ko.bindingEvent.subscribe(node, ko.bindingEvent.childrenComplete, function () {
4052                                var callback = evaluateValueAccessor(bindings[ko.bindingEvent.childrenComplete]);
4053                                if (callback) {
4054                                    var nodes = ko.virtualElements.childNodes(node);
4055                                    if (nodes.length) {
4056                                        callback(nodes, ko.dataFor(nodes[0]));
4057                                    }
4058                                }
4059                            });
4060                        }
4061
4062                        if (ko.bindingEvent.descendantsComplete in bindings) {
4063                            contextToExtend = ko.bindingEvent.startPossiblyAsyncContentBinding(node, bindingContext);
4064                            ko.bindingEvent.subscribe(node, ko.bindingEvent.descendantsComplete, function () {
4065                                var callback = evaluateValueAccessor(bindings[ko.bindingEvent.descendantsComplete]);
4066                                if (callback && ko.virtualElements.firstChild(node)) {
4067                                    callback(node);
4068                                }
4069                            });
4070                        }
4071
4072                        // First put the bindings into the right order
4073                        var orderedBindings = topologicalSortBindings(bindings);
4074
4075                        // Go through the sorted bindings, calling init and update for each
4076                        ko.utils.arrayForEach(orderedBindings, function (bindingKeyAndHandler) {
4077                            // Note that topologicalSortBindings has already filtered out any nonexistent binding handlers,
4078                            // so bindingKeyAndHandler.handler will always be nonnull.
4079                            var handlerInitFn = bindingKeyAndHandler.handler["init"],
4080                                handlerUpdateFn = bindingKeyAndHandler.handler["update"],
4081                                bindingKey = bindingKeyAndHandler.key;
4082
4083                            if (node.nodeType === 8) {
4084                                validateThatBindingIsAllowedForVirtualElements(bindingKey);
4085                            }
4086
4087                            try {
4088                                // Run init, ignoring any dependencies
4089                                if (typeof handlerInitFn == "function") {
4090                                    ko.dependencyDetection.ignore(function () {
4091                                        var initResult = handlerInitFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);
4092
4093                                        // If this binding handler claims to control descendant bindings, make a note of this
4094                                        if (initResult && initResult['controlsDescendantBindings']) {
4095                                            if (bindingHandlerThatControlsDescendantBindings !== undefined)
4096                                                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.");
4097                                            bindingHandlerThatControlsDescendantBindings = bindingKey;
4098                                        }
4099                                    });
4100                                }
4101
4102                                // Run update in its own computed wrapper
4103                                if (typeof handlerUpdateFn == "function") {
4104                                    ko.dependentObservable(
4105                                        function () {
4106                                            handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);
4107                                        },
4108                                        null,
4109                                        { disposeWhenNodeIsRemoved: node }
4110                                    );
4111                                }
4112                            } catch (ex) {
4113                                ex.message = "Unable to process binding \"" + bindingKey + ": " + bindings[bindingKey] + "\"\nMessage: " + ex.message;
4114                                throw ex;
4115                            }
4116                        });
4117                    }
4118
4119                    var shouldBindDescendants = bindingHandlerThatControlsDescendantBindings === undefined;
4120                    return {
4121                        'shouldBindDescendants': shouldBindDescendants,
4122                        'bindingContextForDescendants': shouldBindDescendants && contextToExtend
4123                    };
4124                };
4125
4126                ko.storedBindingContextForNode = function (node) {
4127                    var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
4128                    return bindingInfo && bindingInfo.context;
4129                }
4130
4131                function getBindingContext(viewModelOrBindingContext, extendContextCallback) {
4132                    return viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
4133                        ? viewModelOrBindingContext
4134                        : new ko.bindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);
4135                }
4136
4137                ko.applyBindingAccessorsToNode = function (node, bindings, viewModelOrBindingContext) {
4138                    if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
4139                        ko.virtualElements.normaliseVirtualElementDomStructure(node);
4140                    return applyBindingsToNodeInternal(node, bindings, getBindingContext(viewModelOrBindingContext));
4141                };
4142
4143                ko.applyBindingsToNode = function (node, bindings, viewModelOrBindingContext) {
4144                    var context = getBindingContext(viewModelOrBindingContext);
4145                    return ko.applyBindingAccessorsToNode(node, makeBindingAccessors(bindings, context, node), context);
4146                };
4147
4148                ko.applyBindingsToDescendants = function (viewModelOrBindingContext, rootNode) {
4149                    if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
4150                        applyBindingsToDescendantsInternal(getBindingContext(viewModelOrBindingContext), rootNode);
4151                };
4152
4153                ko.applyBindings = function (viewModelOrBindingContext, rootNode, extendContextCallback) {
4154                    // If jQuery is loaded after Knockout, we won't initially have access to it. So save it here.
4155                    if (!jQueryInstance && window['jQuery']) {
4156                        jQueryInstance = window['jQuery'];
4157                    }
4158
4159                    if (arguments.length < 2) {
4160                        rootNode = document.body;
4161                        if (!rootNode) {
4162                            throw Error("ko.applyBindings: could not find document.body; has the document been loaded?");
4163                        }
4164                    } else if (!rootNode || (rootNode.nodeType !== 1 && rootNode.nodeType !== 8)) {
4165                        throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
4166                    }
4167
4168                    applyBindingsToNodeAndDescendantsInternal(getBindingContext(viewModelOrBindingContext, extendContextCallback), rootNode);
4169                };
4170
4171                // Retrieving binding context from arbitrary nodes
4172                ko.contextFor = function (node) {
4173                    // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
4174                    if (node && (node.nodeType === 1 || node.nodeType === 8)) {
4175                        return ko.storedBindingContextForNode(node);
4176                    }
4177                    return undefined;
4178                };
4179                ko.dataFor = function (node) {
4180                    var context = ko.contextFor(node);
4181                    return context ? context['$data'] : undefined;
4182                };
4183
4184                ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
4185                ko.exportSymbol('bindingEvent', ko.bindingEvent);
4186                ko.exportSymbol('bindingEvent.subscribe', ko.bindingEvent.subscribe);
4187                ko.exportSymbol('bindingEvent.startPossiblyAsyncContentBinding', ko.bindingEvent.startPossiblyAsyncContentBinding);
4188                ko.exportSymbol('applyBindings', ko.applyBindings);
4189                ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
4190                ko.exportSymbol('applyBindingAccessorsToNode', ko.applyBindingAccessorsToNode);
4191                ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
4192                ko.exportSymbol('contextFor', ko.contextFor);
4193                ko.exportSymbol('dataFor', ko.dataFor);
4194            })();
4195            (function (undefined) {
4196                var loadingSubscribablesCache = {}, // Tracks component loads that are currently in flight
4197                    loadedDefinitionsCache = {};    // Tracks component loads that have already completed
4198
4199                ko.components = {
4200                    get: function (componentName, callback) {
4201                        var cachedDefinition = getObjectOwnProperty(loadedDefinitionsCache, componentName);
4202                        if (cachedDefinition) {
4203                            // It's already loaded and cached. Reuse the same definition object.
4204                            // Note that for API consistency, even cache hits complete asynchronously by default.
4205                            // You can bypass this by putting synchronous:true on your component config.
4206                            if (cachedDefinition.isSynchronousComponent) {
4207                                ko.dependencyDetection.ignore(function () { // See comment in loaderRegistryBehaviors.js for reasoning
4208                                    callback(cachedDefinition.definition);
4209                                });
4210                            } else {
4211                                ko.tasks.schedule(function () { callback(cachedDefinition.definition); });
4212                            }
4213                        } else {
4214                            // Join the loading process that is already underway, or start a new one.
4215                            loadComponentAndNotify(componentName, callback);
4216                        }
4217                    },
4218
4219                    clearCachedDefinition: function (componentName) {
4220                        delete loadedDefinitionsCache[componentName];
4221                    },
4222
4223                    _getFirstResultFromLoaders: getFirstResultFromLoaders
4224                };
4225
4226                function getObjectOwnProperty(obj, propName) {
4227                    return Object.prototype.hasOwnProperty.call(obj, propName) ? obj[propName] : undefined;
4228                }
4229
4230                function loadComponentAndNotify(componentName, callback) {
4231                    var subscribable = getObjectOwnProperty(loadingSubscribablesCache, componentName),
4232                        completedAsync;
4233                    if (!subscribable) {
4234                        // It's not started loading yet. Start loading, and when it's done, move it to loadedDefinitionsCache.
4235                        subscribable = loadingSubscribablesCache[componentName] = new ko.subscribable();
4236                        subscribable.subscribe(callback);
4237
4238                        beginLoadingComponent(componentName, function (definition, config) {
4239                            var isSynchronousComponent = !!(config && config['synchronous']);
4240                            loadedDefinitionsCache[componentName] = { definition: definition, isSynchronousComponent: isSynchronousComponent };
4241                            delete loadingSubscribablesCache[componentName];
4242
4243                            // For API consistency, all loads complete asynchronously. However we want to avoid
4244                            // adding an extra task schedule if it's unnecessary (i.e., the completion is already
4245                            // async).
4246                            //
4247                            // You can bypass the 'always asynchronous' feature by putting the synchronous:true
4248                            // flag on your component configuration when you register it.
4249                            if (completedAsync || isSynchronousComponent) {
4250                                // Note that notifySubscribers ignores any dependencies read within the callback.
4251                                // See comment in loaderRegistryBehaviors.js for reasoning
4252                                subscribable['notifySubscribers'](definition);
4253                            } else {
4254                                ko.tasks.schedule(function () {
4255                                    subscribable['notifySubscribers'](definition);
4256                                });
4257                            }
4258                        });
4259                        completedAsync = true;
4260                    } else {
4261                        subscribable.subscribe(callback);
4262                    }
4263                }
4264
4265                function beginLoadingComponent(componentName, callback) {
4266                    getFirstResultFromLoaders('getConfig', [componentName], function (config) {
4267                        if (config) {
4268                            // We have a config, so now load its definition
4269                            getFirstResultFromLoaders('loadComponent', [componentName, config], function (definition) {
4270                                callback(definition, config);
4271                            });
4272                        } else {
4273                            // The component has no config - it's unknown to all the loaders.
4274                            // Note that this is not an error (e.g., a module loading error) - that would abort the
4275                            // process and this callback would not run. For this callback to run, all loaders must
4276                            // have confirmed they don't know about this component.
4277                            callback(null, null);
4278                        }
4279                    });
4280                }
4281
4282                function getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders) {
4283                    // On the first call in the stack, start with the full set of loaders
4284                    if (!candidateLoaders) {
4285                        candidateLoaders = ko.components['loaders'].slice(0); // Use a copy, because we'll be mutating this array
4286                    }
4287
4288                    // Try the next candidate
4289                    var currentCandidateLoader = candidateLoaders.shift();
4290                    if (currentCandidateLoader) {
4291                        var methodInstance = currentCandidateLoader[methodName];
4292                        if (methodInstance) {
4293                            var wasAborted = false,
4294                                synchronousReturnValue = methodInstance.apply(currentCandidateLoader, argsExceptCallback.concat(function (result) {
4295                                    if (wasAborted) {
4296                                        callback(null);
4297                                    } else if (result !== null) {
4298                                        // This candidate returned a value. Use it.
4299                                        callback(result);
4300                                    } else {
4301                                        // Try the next candidate
4302                                        getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);
4303                                    }
4304                                }));
4305
4306                            // Currently, loaders may not return anything synchronously. This leaves open the possibility
4307                            // that we'll extend the API to support synchronous return values in the future. It won't be
4308                            // a breaking change, because currently no loader is allowed to return anything except undefined.
4309                            if (synchronousReturnValue !== undefined) {
4310                                wasAborted = true;
4311
4312                                // Method to suppress exceptions will remain undocumented. This is only to keep
4313                                // KO's specs running tidily, since we can observe the loading got aborted without
4314                                // having exceptions cluttering up the console too.
4315                                if (!currentCandidateLoader['suppressLoaderExceptions']) {
4316                                    throw new Error('Component loaders must supply values by invoking the callback, not by returning values synchronously.');
4317                                }
4318                            }
4319                        } else {
4320                            // This candidate doesn't have the relevant handler. Synchronously move on to the next one.
4321                            getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);
4322                        }
4323                    } else {
4324                        // No candidates returned a value
4325                        callback(null);
4326                    }
4327                }
4328
4329                // Reference the loaders via string name so it's possible for developers
4330                // to replace the whole array by assigning to ko.components.loaders
4331                ko.components['loaders'] = [];
4332
4333                ko.exportSymbol('components', ko.components);
4334                ko.exportSymbol('components.get', ko.components.get);
4335                ko.exportSymbol('components.clearCachedDefinition', ko.components.clearCachedDefinition);
4336            })();
4337            (function (undefined) {
4338
4339                // The default loader is responsible for two things:
4340                // 1. Maintaining the default in-memory registry of component configuration objects
4341                //    (i.e., the thing you're writing to when you call ko.components.register(someName, ...))
4342                // 2. Answering requests for components by fetching configuration objects
4343                //    from that default in-memory registry and resolving them into standard
4344                //    component definition objects (of the form { createViewModel: ..., template: ... })
4345                // Custom loaders may override either of these facilities, i.e.,
4346                // 1. To supply configuration objects from some other source (e.g., conventions)
4347                // 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.
4348
4349                var defaultConfigRegistry = {};
4350
4351                ko.components.register = function (componentName, config) {
4352                    if (!config) {
4353                        throw new Error('Invalid configuration for ' + componentName);
4354                    }
4355
4356                    if (ko.components.isRegistered(componentName)) {
4357                        throw new Error('Component ' + componentName + ' is already registered');
4358                    }
4359
4360                    defaultConfigRegistry[componentName] = config;
4361                };
4362
4363                ko.components.isRegistered = function (componentName) {
4364                    return Object.prototype.hasOwnProperty.call(defaultConfigRegistry, componentName);
4365                };
4366
4367                ko.components.unregister = function (componentName) {
4368                    delete defaultConfigRegistry[componentName];
4369                    ko.components.clearCachedDefinition(componentName);
4370                };
4371
4372                ko.components.defaultLoader = {
4373                    'getConfig': function (componentName, callback) {
4374                        var result = ko.components.isRegistered(componentName)
4375                            ? defaultConfigRegistry[componentName]
4376                            : null;
4377                        callback(result);
4378                    },
4379
4380                    'loadComponent': function (componentName, config, callback) {
4381                        var errorCallback = makeErrorCallback(componentName);
4382                        possiblyGetConfigFromAmd(errorCallback, config, function (loadedConfig) {
4383                            resolveConfig(componentName, errorCallback, loadedConfig, callback);
4384                        });
4385                    },
4386
4387                    'loadTemplate': function (componentName, templateConfig, callback) {
4388                        resolveTemplate(makeErrorCallback(componentName), templateConfig, callback);
4389                    },
4390
4391                    'loadViewModel': function (componentName, viewModelConfig, callback) {
4392                        resolveViewModel(makeErrorCallback(componentName), viewModelConfig, callback);
4393                    }
4394                };
4395
4396                var createViewModelKey = 'createViewModel';
4397
4398                // Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it
4399                // into the standard component definition format:
4400                //    { template: <ArrayOfDomNodes>, createViewModel: function(params, componentInfo) { ... } }.
4401                // Since both template and viewModel may need to be resolved asynchronously, both tasks are performed
4402                // in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,
4403                // so this is implemented manually below.
4404                function resolveConfig(componentName, errorCallback, config, callback) {
4405                    var result = {},
4406                        makeCallBackWhenZero = 2,
4407                        tryIssueCallback = function () {
4408                            if (--makeCallBackWhenZero === 0) {
4409                                callback(result);
4410                            }
4411                        },
4412                        templateConfig = config['template'],
4413                        viewModelConfig = config['viewModel'];
4414
4415                    if (templateConfig) {
4416                        possiblyGetConfigFromAmd(errorCallback, templateConfig, function (loadedConfig) {
4417                            ko.components._getFirstResultFromLoaders('loadTemplate', [componentName, loadedConfig], function (resolvedTemplate) {
4418                                result['template'] = resolvedTemplate;
4419                                tryIssueCallback();
4420                            });
4421                        });
4422                    } else {
4423                        tryIssueCallback();
4424                    }
4425
4426                    if (viewModelConfig) {
4427                        possiblyGetConfigFromAmd(errorCallback, viewModelConfig, function (loadedConfig) {
4428                            ko.components._getFirstResultFromLoaders('loadViewModel', [componentName, loadedConfig], function (resolvedViewModel) {
4429                                result[createViewModelKey] = resolvedViewModel;
4430                                tryIssueCallback();
4431                            });
4432                        });
4433                    } else {
4434                        tryIssueCallback();
4435                    }
4436                }
4437
4438                function resolveTemplate(errorCallback, templateConfig, callback) {
4439                    if (typeof templateConfig === 'string') {
4440                        // Markup - parse it
4441                        callback(ko.utils.parseHtmlFragment(templateConfig));
4442                    } else if (templateConfig instanceof Array) {
4443                        // Assume already an array of DOM nodes - pass through unchanged
4444                        callback(templateConfig);
4445                    } else if (isDocumentFragment(templateConfig)) {
4446                        // Document fragment - use its child nodes
4447                        callback(ko.utils.makeArray(templateConfig.childNodes));
4448                    } else if (templateConfig['element']) {
4449                        var element = templateConfig['element'];
4450                        if (isDomElement(element)) {
4451                            // Element instance - copy its child nodes
4452                            callback(cloneNodesFromTemplateSourceElement(element));
4453                        } else if (typeof element === 'string') {
4454                            // Element ID - find it, then copy its child nodes
4455                            var elemInstance = document.getElementById(element);
4456                            if (elemInstance) {
4457                                callback(cloneNodesFromTemplateSourceElement(elemInstance));
4458                            } else {
4459                                errorCallback('Cannot find element with ID ' + element);
4460                            }
4461                        } else {
4462                            errorCallback('Unknown element type: ' + element);
4463                        }
4464                    } else {
4465                        errorCallback('Unknown template value: ' + templateConfig);
4466                    }
4467                }
4468
4469                function resolveViewModel(errorCallback, viewModelConfig, callback) {
4470                    if (typeof viewModelConfig === 'function') {
4471                        // Constructor - convert to standard factory function format
4472                        // By design, this does *not* supply componentInfo to the constructor, as the intent is that
4473                        // componentInfo contains non-viewmodel data (e.g., the component's element) that should only
4474                        // be used in factory functions, not viewmodel constructors.
4475                        callback(function (params /*, componentInfo */) {
4476                            return new viewModelConfig(params);
4477                        });
4478                    } else if (typeof viewModelConfig[createViewModelKey] === 'function') {
4479                        // Already a factory function - use it as-is
4480                        callback(viewModelConfig[createViewModelKey]);
4481                    } else if ('instance' in viewModelConfig) {
4482                        // Fixed object instance - promote to createViewModel format for API consistency
4483                        var fixedInstance = viewModelConfig['instance'];
4484                        callback(function (params, componentInfo) {
4485                            return fixedInstance;
4486                        });
4487                    } else if ('viewModel' in viewModelConfig) {
4488                        // Resolved AMD module whose value is of the form { viewModel: ... }
4489                        resolveViewModel(errorCallback, viewModelConfig['viewModel'], callback);
4490                    } else {
4491                        errorCallback('Unknown viewModel value: ' + viewModelConfig);
4492                    }
4493                }
4494
4495                function cloneNodesFromTemplateSourceElement(elemInstance) {
4496                    switch (ko.utils.tagNameLower(elemInstance)) {
4497                        case 'script':
4498                            return ko.utils.parseHtmlFragment(elemInstance.text);
4499                        case 'textarea':
4500                            return ko.utils.parseHtmlFragment(elemInstance.value);
4501                        case 'template':
4502                            // For browsers with proper <template> element support (i.e., where the .content property
4503                            // gives a document fragment), use that document fragment.
4504                            if (isDocumentFragment(elemInstance.content)) {
4505                                return ko.utils.cloneNodes(elemInstance.content.childNodes);
4506                            }
4507                    }
4508
4509                    // Regular elements such as <div>, and <template> elements on old browsers that don't really
4510                    // understand <template> and just treat it as a regular container
4511                    return ko.utils.cloneNodes(elemInstance.childNodes);
4512                }
4513
4514                function isDomElement(obj) {
4515                    if (window['HTMLElement']) {
4516                        return obj instanceof HTMLElement;
4517                    } else {
4518                        return obj && obj.tagName && obj.nodeType === 1;
4519                    }
4520                }
4521
4522                function isDocumentFragment(obj) {
4523                    if (window['DocumentFragment']) {
4524                        return obj instanceof DocumentFragment;
4525                    } else {
4526                        return obj && obj.nodeType === 11;
4527                    }
4528                }
4529
4530                function possiblyGetConfigFromAmd(errorCallback, config, callback) {
4531                    if (typeof config['require'] === 'string') {
4532                        // The config is the value of an AMD module
4533                        if (amdRequire || window['require']) {
4534                            (amdRequire || window['require'])([config['require']], function (module) {
4535                                if (module && typeof module === 'object' && module.__esModule && module.default) {
4536                                    module = module.default;
4537                                }
4538                                callback(module);
4539                            });
4540                        } else {
4541                            errorCallback('Uses require, but no AMD loader is present');
4542                        }
4543                    } else {
4544                        callback(config);
4545                    }
4546                }
4547
4548                function makeErrorCallback(componentName) {
4549                    return function (message) {
4550                        throw new Error('Component \'' + componentName + '\': ' + message);
4551                    };
4552                }
4553
4554                ko.exportSymbol('components.register', ko.components.register);
4555                ko.exportSymbol('components.isRegistered', ko.components.isRegistered);
4556                ko.exportSymbol('components.unregister', ko.components.unregister);
4557
4558                // Expose the default loader so that developers can directly ask it for configuration
4559                // or to resolve configuration
4560                ko.exportSymbol('components.defaultLoader', ko.components.defaultLoader);
4561
4562                // By default, the default loader is the only registered component loader
4563                ko.components['loaders'].push(ko.components.defaultLoader);
4564
4565                // Privately expose the underlying config registry for use in old-IE shim
4566                ko.components._allRegisteredComponents = defaultConfigRegistry;
4567            })();
4568            (function (undefined) {
4569                // Overridable API for determining which component name applies to a given node. By overriding this,
4570                // you can for example map specific tagNames to components that are not preregistered.
4571                ko.components['getComponentNameForNode'] = function (node) {
4572                    var tagNameLower = ko.utils.tagNameLower(node);
4573                    if (ko.components.isRegistered(tagNameLower)) {
4574                        // Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
4575                        if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]" || (ko.utils.ieVersion <= 8 && node.tagName === tagNameLower)) {
4576                            return tagNameLower;
4577                        }
4578                    }
4579                };
4580
4581                ko.components.addBindingsForCustomElement = function (allBindings, node, bindingContext, valueAccessors) {
4582                    // Determine if it's really a custom element matching a component
4583                    if (node.nodeType === 1) {
4584                        var componentName = ko.components['getComponentNameForNode'](node);
4585                        if (componentName) {
4586                            // It does represent a component, so add a component binding for it
4587                            allBindings = allBindings || {};
4588
4589                            if (allBindings['component']) {
4590                                // Avoid silently overwriting some other 'component' binding that may already be on the element
4591                                throw new Error('Cannot use the "component" binding on a custom element matching a component');
4592                            }
4593
4594                            var componentBindingValue = { 'name': componentName, 'params': getComponentParamsFromCustomElement(node, bindingContext) };
4595
4596                            allBindings['component'] = valueAccessors
4597                                ? function () { return componentBindingValue; }
4598                                : componentBindingValue;
4599                        }
4600                    }
4601
4602                    return allBindings;
4603                }
4604
4605                var nativeBindingProviderInstance = new ko.bindingProvider();
4606
4607                function getComponentParamsFromCustomElement(elem, bindingContext) {
4608                    var paramsAttribute = elem.getAttribute('params');
4609
4610                    if (paramsAttribute) {
4611                        var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),
4612                            rawParamComputedValues = ko.utils.objectMap(params, function (paramValue, paramName) {
4613                                return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
4614                            }),
4615                            result = ko.utils.objectMap(rawParamComputedValues, function (paramValueComputed, paramName) {
4616                                var paramValue = paramValueComputed.peek();
4617                                // Does the evaluation of the parameter value unwrap any observables?
4618                                if (!paramValueComputed.isActive()) {
4619                                    // No it doesn't, so there's no need for any computed wrapper. Just pass through the supplied value directly.
4620                                    // Example: "someVal: firstName, age: 123" (whether or not firstName is an observable/computed)
4621                                    return paramValue;
4622                                } else {
4623                                    // Yes it does. Supply a computed property that unwraps both the outer (binding expression)
4624                                    // level of observability, and any inner (resulting model value) level of observability.
4625                                    // This means the component doesn't have to worry about multiple unwrapping. If the value is a
4626                                    // writable observable, the computed will also be writable and pass the value on to the observable.
4627                                    return ko.computed({
4628                                        'read': function () {
4629                                            return ko.utils.unwrapObservable(paramValueComputed());
4630                                        },
4631                                        'write': ko.isWriteableObservable(paramValue) && function (value) {
4632                                            paramValueComputed()(value);
4633                                        },
4634                                        disposeWhenNodeIsRemoved: elem
4635                                    });
4636                                }
4637                            });
4638
4639                        // Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called '$raw'
4640                        // This is in case the developer wants to react to outer (binding) observability separately from inner
4641                        // (model value) observability, or in case the model value observable has subobservables.
4642                        if (!Object.prototype.hasOwnProperty.call(result, '$raw')) {
4643                            result['$raw'] = rawParamComputedValues;
4644                        }
4645
4646                        return result;
4647                    } else {
4648                        // For consistency, absence of a "params" attribute is treated the same as the presence of
4649                        // any empty one. Otherwise component viewmodels need special code to check whether or not
4650                        // 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.
4651                        return { '$raw': {} };
4652                    }
4653                }
4654
4655                // --------------------------------------------------------------------------------
4656                // Compatibility code for older (pre-HTML5) IE browsers
4657
4658                if (ko.utils.ieVersion < 9) {
4659                    // Whenever you preregister a component, enable it as a custom element in the current document
4660                    ko.components['register'] = (function (originalFunction) {
4661                        return function (componentName) {
4662                            document.createElement(componentName); // Allows IE<9 to parse markup containing the custom element
4663                            return originalFunction.apply(this, arguments);
4664                        }
4665                    })(ko.components['register']);
4666
4667                    // Whenever you create a document fragment, enable all preregistered component names as custom elements
4668                    // This is needed to make innerShiv/jQuery HTML parsing correctly handle the custom elements
4669                    document.createDocumentFragment = (function (originalFunction) {
4670                        return function () {
4671                            var newDocFrag = originalFunction(),
4672                                allComponents = ko.components._allRegisteredComponents;
4673                            for (var componentName in allComponents) {
4674                                if (Object.prototype.hasOwnProperty.call(allComponents, componentName)) {
4675                                    newDocFrag.createElement(componentName);
4676                                }
4677                            }
4678                            return newDocFrag;
4679                        };
4680                    })(document.createDocumentFragment);
4681                }
4682            })(); (function (undefined) {
4683                var componentLoadingOperationUniqueId = 0;
4684
4685                ko.bindingHandlers['component'] = {
4686                    'init': function (element, valueAccessor, ignored1, ignored2, bindingContext) {
4687                        var currentViewModel,
4688                            currentLoadingOperationId,
4689                            afterRenderSub,
4690                            disposeAssociatedComponentViewModel = function () {
4691                                var currentViewModelDispose = currentViewModel && currentViewModel['dispose'];
4692                                if (typeof currentViewModelDispose === 'function') {
4693                                    currentViewModelDispose.call(currentViewModel);
4694                                }
4695                                if (afterRenderSub) {
4696                                    afterRenderSub.dispose();
4697                                }
4698                                afterRenderSub = null;
4699                                currentViewModel = null;
4700                                // Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
4701                                currentLoadingOperationId = null;
4702                            },
4703                            originalChildNodes = ko.utils.makeArray(ko.virtualElements.childNodes(element));
4704
4705                        ko.virtualElements.emptyNode(element);
4706                        ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);
4707
4708                        ko.computed(function () {
4709                            var value = ko.utils.unwrapObservable(valueAccessor()),
4710                                componentName, componentParams;
4711
4712                            if (typeof value === 'string') {
4713                                componentName = value;
4714                            } else {
4715                                componentName = ko.utils.unwrapObservable(value['name']);
4716                                componentParams = ko.utils.unwrapObservable(value['params']);
4717                            }
4718
4719                            if (!componentName) {
4720                                throw new Error('No component name specified');
4721                            }
4722
4723                            var asyncContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);
4724
4725                            var loadingOperationId = currentLoadingOperationId = ++componentLoadingOperationUniqueId;
4726                            ko.components.get(componentName, function (componentDefinition) {
4727                                // If this is not the current load operation for this element, ignore it.
4728                                if (currentLoadingOperationId !== loadingOperationId) {
4729                                    return;
4730                                }
4731
4732                                // Clean up previous state
4733                                disposeAssociatedComponentViewModel();
4734
4735                                // Instantiate and bind new component. Implicitly this cleans any old DOM nodes.
4736                                if (!componentDefinition) {
4737                                    throw new Error('Unknown component \'' + componentName + '\'');
4738                                }
4739                                cloneTemplateIntoElement(componentName, componentDefinition, element);
4740
4741                                var componentInfo = {
4742                                    'element': element,
4743                                    'templateNodes': originalChildNodes
4744                                };
4745
4746                                var componentViewModel = createViewModel(componentDefinition, componentParams, componentInfo),
4747                                    childBindingContext = asyncContext['createChildContext'](componentViewModel, {
4748                                        'extend': function (ctx) {
4749                                            ctx['$component'] = componentViewModel;
4750                                            ctx['$componentTemplateNodes'] = originalChildNodes;
4751                                        }
4752                                    });
4753
4754                                if (componentViewModel && componentViewModel['koDescendantsComplete']) {
4755                                    afterRenderSub = ko.bindingEvent.subscribe(element, ko.bindingEvent.descendantsComplete, componentViewModel['koDescendantsComplete'], componentViewModel);
4756                                }
4757
4758                                currentViewModel = componentViewModel;
4759                                ko.applyBindingsToDescendants(childBindingContext, element);
4760                            });
4761                        }, null, { disposeWhenNodeIsRemoved: element });
4762
4763                        return { 'controlsDescendantBindings': true };
4764                    }
4765                };
4766
4767                ko.virtualElements.allowedBindings['component'] = true;
4768
4769                function cloneTemplateIntoElement(componentName, componentDefinition, element) {
4770                    var template = componentDefinition['template'];
4771                    if (!template) {
4772                        throw new Error('Component \'' + componentName + '\' has no template');
4773                    }
4774
4775                    var clonedNodesArray = ko.utils.cloneNodes(template);
4776                    ko.virtualElements.setDomNodeChildren(element, clonedNodesArray);
4777                }
4778
4779                function createViewModel(componentDefinition, componentParams, componentInfo) {
4780                    var componentViewModelFactory = componentDefinition['createViewModel'];
4781                    return componentViewModelFactory
4782                        ? componentViewModelFactory.call(componentDefinition, componentParams, componentInfo)
4783                        : componentParams; // Template-only component
4784                }
4785
4786            })();
4787            var attrHtmlToJavaScriptMap = { 'class': 'className', 'for': 'htmlFor' };
4788            ko.bindingHandlers['attr'] = {
4789                'update': function (element, valueAccessor, allBindings) {
4790                    var value = ko.utils.unwrapObservable(valueAccessor()) || {};
4791                    ko.utils.objectForEach(value, function (attrName, attrValue) {
4792                        attrValue = ko.utils.unwrapObservable(attrValue);
4793
4794                        // Find the namespace of this attribute, if any.
4795                        var prefixLen = attrName.indexOf(':');
4796                        var namespace = "lookupNamespaceURI" in element && prefixLen > 0 && element.lookupNamespaceURI(attrName.substr(0, prefixLen));
4797
4798                        // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
4799                        // when someProp is a "no value"-like value (strictly null, false, or undefined)
4800                        // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
4801                        var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
4802                        if (toRemove) {
4803                            namespace ? element.removeAttributeNS(namespace, attrName) : element.removeAttribute(attrName);
4804                        } else {
4805                            attrValue = attrValue.toString();
4806                        }
4807
4808                        // In IE <= 7 and IE8 Quirks Mode, you have to use the JavaScript property name instead of the
4809                        // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
4810                        // but instead of figuring out the mode, we'll just set the attribute through the JavaScript
4811                        // property for IE <= 8.
4812                        if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavaScriptMap) {
4813                            attrName = attrHtmlToJavaScriptMap[attrName];
4814                            if (toRemove)
4815                                element.removeAttribute(attrName);
4816                            else
4817                                element[attrName] = attrValue;
4818                        } else if (!toRemove) {
4819                            namespace ? element.setAttributeNS(namespace, attrName, attrValue) : element.setAttribute(attrName, attrValue);
4820                        }
4821
4822                        // Treat "name" specially - although you can think of it as an attribute, it also needs
4823                        // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
4824                        // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
4825                        // entirely, and there's no strong reason to allow for such casing in HTML.
4826                        if (attrName === "name") {
4827                            ko.utils.setElementName(element, toRemove ? "" : attrValue);
4828                        }
4829                    });
4830                }
4831            };
4832            (function () {
4833
4834                ko.bindingHandlers['checked'] = {
4835                    'after': ['value', 'attr'],
4836                    'init': function (element, valueAccessor, allBindings) {
4837                        var checkedValue = ko.pureComputed(function () {
4838                            // Treat "value" like "checkedValue" when it is included with "checked" binding
4839                            if (allBindings['has']('checkedValue')) {
4840                                return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
4841                            } else if (useElementValue) {
4842                                if (allBindings['has']('value')) {
4843                                    return ko.utils.unwrapObservable(allBindings.get('value'));
4844                                } else {
4845                                    return element.value;
4846                                }
4847                            }
4848                        });
4849
4850                        function updateModel() {
4851                            // This updates the model value from the view value.
4852                            // It runs in response to DOM events (click) and changes in checkedValue.
4853                            var isChecked = element.checked,
4854                                elemValue = checkedValue();
4855
4856                            // When we're first setting up this computed, don't change any model state.
4857                            if (ko.computedContext.isInitial()) {
4858                                return;
4859                            }
4860
4861                            // We can ignore unchecked radio buttons, because some other radio
4862                            // button will be checked, and that one can take care of updating state.
4863                            // Also ignore value changes to an already unchecked checkbox.
4864                            if (!isChecked && (isRadio || ko.computedContext.getDependenciesCount())) {
4865                                return;
4866                            }
4867
4868                            var modelValue = ko.dependencyDetection.ignore(valueAccessor);
4869                            if (valueIsArray) {
4870                                var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue,
4871                                    saveOldValue = oldElemValue;
4872                                oldElemValue = elemValue;
4873
4874                                if (saveOldValue !== elemValue) {
4875                                    // When we're responding to the checkedValue changing, and the element is
4876                                    // currently checked, replace the old elem value with the new elem value
4877                                    // in the model array.
4878                                    if (isChecked) {
4879                                        ko.utils.addOrRemoveItem(writableValue, elemValue, true);
4880                                        ko.utils.addOrRemoveItem(writableValue, saveOldValue, false);
4881                                    }
4882                                } else {
4883                                    // When we're responding to the user having checked/unchecked a checkbox,
4884                                    // add/remove the element value to the model array.
4885                                    ko.utils.addOrRemoveItem(writableValue, elemValue, isChecked);
4886                                }
4887
4888                                if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
4889                                    modelValue(writableValue);
4890                                }
4891                            } else {
4892                                if (isCheckbox) {
4893                                    if (elemValue === undefined) {
4894                                        elemValue = isChecked;
4895                                    } else if (!isChecked) {
4896                                        elemValue = undefined;
4897                                    }
4898                                }
4899                                ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
4900                            }
4901                        };
4902
4903                        function updateView() {
4904                            // This updates the view value from the model value.
4905                            // It runs in response to changes in the bound (checked) value.
4906                            var modelValue = ko.utils.unwrapObservable(valueAccessor()),
4907                                elemValue = checkedValue();
4908
4909                            if (valueIsArray) {
4910                                // When a checkbox is bound to an array, being checked represents its value being present in that array
4911                                element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
4912                                oldElemValue = elemValue;
4913                            } else if (isCheckbox && elemValue === undefined) {
4914                                // When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
4915                                // being checked represents the value being trueish
4916                                element.checked = !!modelValue;
4917                            } else {
4918                                // Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
4919                                element.checked = (checkedValue() === modelValue);
4920                            }
4921                        };
4922
4923                        var isCheckbox = element.type == "checkbox",
4924                            isRadio = element.type == "radio";
4925
4926                        // Only bind to check boxes and radio buttons
4927                        if (!isCheckbox && !isRadio) {
4928                            return;
4929                        }
4930
4931                        var rawValue = valueAccessor(),
4932                            valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),
4933                            rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),
4934                            useElementValue = isRadio || valueIsArray,
4935                            oldElemValue = valueIsArray ? checkedValue() : undefined;
4936
4937                        // IE 6 won't allow radio buttons to be selected unless they have a name
4938                        if (isRadio && !element.name)
4939                            ko.bindingHandlers['uniqueName']['init'](element, function () { return true });
4940
4941                        // Set up two computeds to update the binding:
4942
4943                        // The first responds to changes in the checkedValue value and to element clicks
4944                        ko.computed(updateModel, null, { disposeWhenNodeIsRemoved: element });
4945                        ko.utils.registerEventHandler(element, "click", updateModel);
4946
4947                        // The second responds to changes in the model value (the one associated with the checked binding)
4948                        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
4949
4950                        rawValue = undefined;
4951                    }
4952                };
4953                ko.expressionRewriting.twoWayBindings['checked'] = true;
4954
4955                ko.bindingHandlers['checkedValue'] = {
4956                    'update': function (element, valueAccessor) {
4957                        element.value = ko.utils.unwrapObservable(valueAccessor());
4958                    }
4959                };
4960
4961            })(); var classesWrittenByBindingKey = '__ko__cssValue';
4962            ko.bindingHandlers['class'] = {
4963                'update': function (element, valueAccessor) {
4964                    var value = ko.utils.stringTrim(ko.utils.unwrapObservable(valueAccessor()));
4965                    ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
4966                    element[classesWrittenByBindingKey] = value;
4967                    ko.utils.toggleDomNodeCssClass(element, value, true);
4968                }
4969            };
4970
4971            ko.bindingHandlers['css'] = {
4972                'update': function (element, valueAccessor) {
4973                    var value = ko.utils.unwrapObservable(valueAccessor());
4974                    if (value !== null && typeof value == "object") {
4975                        ko.utils.objectForEach(value, function (className, shouldHaveClass) {
4976                            shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);
4977                            ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
4978                        });
4979                    } else {
4980                        ko.bindingHandlers['class']['update'](element, valueAccessor);
4981                    }
4982                }
4983            };
4984            ko.bindingHandlers['enable'] = {
4985                'update': function (element, valueAccessor) {
4986                    var value = ko.utils.unwrapObservable(valueAccessor());
4987                    if (value && element.disabled)
4988                        element.removeAttribute("disabled");
4989                    else if ((!value) && (!element.disabled))
4990                        element.disabled = true;
4991                }
4992            };
4993
4994            ko.bindingHandlers['disable'] = {
4995                'update': function (element, valueAccessor) {
4996                    ko.bindingHandlers['enable']['update'](element, function () { return !ko.utils.unwrapObservable(valueAccessor()) });
4997                }
4998            };
4999            // For certain common events (currently just 'click'), allow a simplified data-binding syntax
5000            // e.g. click:handler instead of the usual full-length event:{click:handler}
5001            function makeEventHandlerShortcut(eventName) {
5002                ko.bindingHandlers[eventName] = {
5003                    'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5004                        var newValueAccessor = function () {
5005                            var result = {};
5006                            result[eventName] = valueAccessor();
5007                            return result;
5008                        };
5009                        return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindings, viewModel, bindingContext);
5010                    }
5011                }
5012            }
5013
5014            ko.bindingHandlers['event'] = {
5015                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5016                    var eventsToHandle = valueAccessor() || {};
5017                    ko.utils.objectForEach(eventsToHandle, function (eventName) {
5018                        if (typeof eventName == "string") {
5019                            ko.utils.registerEventHandler(element, eventName, function (event) {
5020                                var handlerReturnValue;
5021                                var handlerFunction = valueAccessor()[eventName];
5022                                if (!handlerFunction)
5023                                    return;
5024
5025                                try {
5026                                    // Take all the event args, and prefix with the viewmodel
5027                                    var argsForHandler = ko.utils.makeArray(arguments);
5028                                    viewModel = bindingContext['$data'];
5029                                    argsForHandler.unshift(viewModel);
5030                                    handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
5031                                } finally {
5032                                    if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
5033                                        if (event.preventDefault)
5034                                            event.preventDefault();
5035                                        else
5036                                            event.returnValue = false;
5037                                    }
5038                                }
5039
5040                                var bubble = allBindings.get(eventName + 'Bubble') !== false;
5041                                if (!bubble) {
5042                                    event.cancelBubble = true;
5043                                    if (event.stopPropagation)
5044                                        event.stopPropagation();
5045                                }
5046                            });
5047                        }
5048                    });
5049                }
5050            };
5051            // "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
5052            // "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
5053            ko.bindingHandlers['foreach'] = {
5054                makeTemplateValueAccessor: function (valueAccessor) {
5055                    return function () {
5056                        var modelValue = valueAccessor(),
5057                            unwrappedValue = ko.utils.peekObservable(modelValue);    // Unwrap without setting a dependency here
5058
5059                        // If unwrappedValue is the array, pass in the wrapped value on its own
5060                        // The value will be unwrapped and tracked within the template binding
5061                        // (See https://github.com/SteveSanderson/knockout/issues/523)
5062                        if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
5063                            return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
5064
5065                        // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
5066                        ko.utils.unwrapObservable(modelValue);
5067                        return {
5068                            'foreach': unwrappedValue['data'],
5069                            'as': unwrappedValue['as'],
5070                            'noChildContext': unwrappedValue['noChildContext'],
5071                            'includeDestroyed': unwrappedValue['includeDestroyed'],
5072                            'afterAdd': unwrappedValue['afterAdd'],
5073                            'beforeRemove': unwrappedValue['beforeRemove'],
5074                            'afterRender': unwrappedValue['afterRender'],
5075                            'beforeMove': unwrappedValue['beforeMove'],
5076                            'afterMove': unwrappedValue['afterMove'],
5077                            'templateEngine': ko.nativeTemplateEngine.instance
5078                        };
5079                    };
5080                },
5081                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5082                    return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
5083                },
5084                'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5085                    return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext);
5086                }
5087            };
5088            ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
5089            ko.virtualElements.allowedBindings['foreach'] = true;
5090            var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
5091            var hasfocusLastValue = '__ko_hasfocusLastValue';
5092            ko.bindingHandlers['hasfocus'] = {
5093                'init': function (element, valueAccessor, allBindings) {
5094                    var handleElementFocusChange = function (isFocused) {
5095                        // Where possible, ignore which event was raised and determine focus state using activeElement,
5096                        // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
5097                        // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
5098                        // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
5099                        // from calling 'blur()' on the element when it loses focus.
5100                        // Discussion at https://github.com/SteveSanderson/knockout/pull/352
5101                        element[hasfocusUpdatingProperty] = true;
5102                        var ownerDoc = element.ownerDocument;
5103                        if ("activeElement" in ownerDoc) {
5104                            var active;
5105                            try {
5106                                active = ownerDoc.activeElement;
5107                            } catch (e) {
5108                                // IE9 throws if you access activeElement during page load (see issue #703)
5109                                active = ownerDoc.body;
5110                            }
5111                            isFocused = (active === element);
5112                        }
5113                        var modelValue = valueAccessor();
5114                        ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'hasfocus', isFocused, true);
5115
5116                        //cache the latest value, so we can avoid unnecessarily calling focus/blur in the update function
5117                        element[hasfocusLastValue] = isFocused;
5118                        element[hasfocusUpdatingProperty] = false;
5119                    };
5120                    var handleElementFocusIn = handleElementFocusChange.bind(null, true);
5121                    var handleElementFocusOut = handleElementFocusChange.bind(null, false);
5122
5123                    ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
5124                    ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
5125                    ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
5126                    ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
5127
5128                    // Assume element is not focused (prevents "blur" being called initially)
5129                    element[hasfocusLastValue] = false;
5130                },
5131                'update': function (element, valueAccessor) {
5132                    var value = !!ko.utils.unwrapObservable(valueAccessor());
5133
5134                    if (!element[hasfocusUpdatingProperty] && element[hasfocusLastValue] !== value) {
5135                        value ? element.focus() : element.blur();
5136
5137                        // In IE, the blur method doesn't always cause the element to lose focus (for example, if the window is not in focus).
5138                        // Setting focus to the body element does seem to be reliable in IE, but should only be used if we know that the current
5139                        // element was focused already.
5140                        if (!value && element[hasfocusLastValue]) {
5141                            element.ownerDocument.body.focus();
5142                        }
5143
5144                        // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
5145                        ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]);
5146                    }
5147                }
5148            };
5149            ko.expressionRewriting.twoWayBindings['hasfocus'] = true;
5150
5151            ko.bindingHandlers['hasFocus'] = ko.bindingHandlers['hasfocus']; // Make "hasFocus" an alias
5152            ko.expressionRewriting.twoWayBindings['hasFocus'] = 'hasfocus';
5153            ko.bindingHandlers['html'] = {
5154                'init': function () {
5155                    // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
5156                    return { 'controlsDescendantBindings': true };
5157                },
5158                'update': function (element, valueAccessor) {
5159                    // setHtml will unwrap the value if needed
5160                    ko.utils.setHtml(element, valueAccessor());
5161                }
5162            };
5163            (function () {
5164
5165                // Makes a binding like with or if
5166                function makeWithIfBinding(bindingKey, isWith, isNot) {
5167                    ko.bindingHandlers[bindingKey] = {
5168                        'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5169                            var didDisplayOnLastUpdate, savedNodes, contextOptions = {}, completeOnRender, needAsyncContext, renderOnEveryChange;
5170
5171                            if (isWith) {
5172                                var as = allBindings.get('as'), noChildContext = allBindings.get('noChildContext');
5173                                renderOnEveryChange = !(as && noChildContext);
5174                                contextOptions = { 'as': as, 'noChildContext': noChildContext, 'exportDependencies': renderOnEveryChange };
5175                            }
5176
5177                            completeOnRender = allBindings.get("completeOn") == "render";
5178                            needAsyncContext = completeOnRender || allBindings['has'](ko.bindingEvent.descendantsComplete);
5179
5180                            ko.computed(function () {
5181                                var value = ko.utils.unwrapObservable(valueAccessor()),
5182                                    shouldDisplay = !isNot !== !value, // equivalent to isNot ? !value : !!value,
5183                                    isInitial = !savedNodes,
5184                                    childContext;
5185
5186                                if (!renderOnEveryChange && shouldDisplay === didDisplayOnLastUpdate) {
5187                                    return;
5188                                }
5189
5190                                if (needAsyncContext) {
5191                                    bindingContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);
5192                                }
5193
5194                                if (shouldDisplay) {
5195                                    if (!isWith || renderOnEveryChange) {
5196                                        contextOptions['dataDependency'] = ko.computedContext.computed();
5197                                    }
5198
5199                                    if (isWith) {
5200                                        childContext = bindingContext['createChildContext'](typeof value == "function" ? value : valueAccessor, contextOptions);
5201                                    } else if (ko.computedContext.getDependenciesCount()) {
5202                                        childContext = bindingContext['extend'](null, contextOptions);
5203                                    } else {
5204                                        childContext = bindingContext;
5205                                    }
5206                                }
5207
5208                                // Save a copy of the inner nodes on the initial update, but only if we have dependencies.
5209                                if (isInitial && ko.computedContext.getDependenciesCount()) {
5210                                    savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
5211                                }
5212
5213                                if (shouldDisplay) {
5214                                    if (!isInitial) {
5215                                        ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(savedNodes));
5216                                    }
5217
5218                                    ko.applyBindingsToDescendants(childContext, element);
5219                                } else {
5220                                    ko.virtualElements.emptyNode(element);
5221
5222                                    if (!completeOnRender) {
5223                                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
5224                                    }
5225                                }
5226
5227                                didDisplayOnLastUpdate = shouldDisplay;
5228
5229                            }, null, { disposeWhenNodeIsRemoved: element });
5230
5231                            return { 'controlsDescendantBindings': true };
5232                        }
5233                    };
5234                    ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
5235                    ko.virtualElements.allowedBindings[bindingKey] = true;
5236                }
5237
5238                // Construct the actual binding handlers
5239                makeWithIfBinding('if');
5240                makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
5241                makeWithIfBinding('with', true /* isWith */);
5242
5243            })(); ko.bindingHandlers['let'] = {
5244                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5245                    // Make a modified binding context, with extra properties, and apply it to descendant elements
5246                    var innerContext = bindingContext['extend'](valueAccessor);
5247                    ko.applyBindingsToDescendants(innerContext, element);
5248
5249                    return { 'controlsDescendantBindings': true };
5250                }
5251            };
5252            ko.virtualElements.allowedBindings['let'] = true;
5253            var captionPlaceholder = {};
5254            ko.bindingHandlers['options'] = {
5255                'init': function (element) {
5256                    if (ko.utils.tagNameLower(element) !== "select")
5257                        throw new Error("options binding applies only to SELECT elements");
5258
5259                    // Remove all existing <option>s.
5260                    while (element.length > 0) {
5261                        element.remove(0);
5262                    }
5263
5264                    // Ensures that the binding processor doesn't try to bind the options
5265                    return { 'controlsDescendantBindings': true };
5266                },
5267                'update': function (element, valueAccessor, allBindings) {
5268                    function selectedOptions() {
5269                        return ko.utils.arrayFilter(element.options, function (node) { return node.selected; });
5270                    }
5271
5272                    var selectWasPreviouslyEmpty = element.length == 0,
5273                        multiple = element.multiple,
5274                        previousScrollTop = (!selectWasPreviouslyEmpty && multiple) ? element.scrollTop : null,
5275                        unwrappedArray = ko.utils.unwrapObservable(valueAccessor()),
5276                        valueAllowUnset = allBindings.get('valueAllowUnset') && allBindings['has']('value'),
5277                        includeDestroyed = allBindings.get('optionsIncludeDestroyed'),
5278                        arrayToDomNodeChildrenOptions = {},
5279                        captionValue,
5280                        filteredArray,
5281                        previousSelectedValues = [];
5282
5283                    if (!valueAllowUnset) {
5284                        if (multiple) {
5285                            previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);
5286                        } else if (element.selectedIndex >= 0) {
5287                            previousSelectedValues.push(ko.selectExtensions.readValue(element.options[element.selectedIndex]));
5288                        }
5289                    }
5290
5291                    if (unwrappedArray) {
5292                        if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
5293                            unwrappedArray = [unwrappedArray];
5294
5295                        // Filter out any entries marked as destroyed
5296                        filteredArray = ko.utils.arrayFilter(unwrappedArray, function (item) {
5297                            return includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
5298                        });
5299
5300                        // If caption is included, add it to the array
5301                        if (allBindings['has']('optionsCaption')) {
5302                            captionValue = ko.utils.unwrapObservable(allBindings.get('optionsCaption'));
5303                            // If caption value is null or undefined, don't show a caption
5304                            if (captionValue !== null && captionValue !== undefined) {
5305                                filteredArray.unshift(captionPlaceholder);
5306                            }
5307                        }
5308                    } else {
5309                        // If a falsy value is provided (e.g. null), we'll simply empty the select element
5310                    }
5311
5312                    function applyToObject(object, predicate, defaultValue) {
5313                        var predicateType = typeof predicate;
5314                        if (predicateType == "function")    // Given a function; run it against the data value
5315                            return predicate(object);
5316                        else if (predicateType == "string") // Given a string; treat it as a property name on the data value
5317                            return object[predicate];
5318                        else                                // Given no optionsText arg; use the data value itself
5319                            return defaultValue;
5320                    }
5321
5322                    // The following functions can run at two different times:
5323                    // The first is when the whole array is being updated directly from this binding handler.
5324                    // The second is when an observable value for a specific array entry is updated.
5325                    // oldOptions will be empty in the first case, but will be filled with the previously generated option in the second.
5326                    var itemUpdate = false;
5327                    function optionForArrayItem(arrayEntry, index, oldOptions) {
5328                        if (oldOptions.length) {
5329                            previousSelectedValues = !valueAllowUnset && oldOptions[0].selected ? [ko.selectExtensions.readValue(oldOptions[0])] : [];
5330                            itemUpdate = true;
5331                        }
5332                        var option = element.ownerDocument.createElement("option");
5333                        if (arrayEntry === captionPlaceholder) {
5334                            ko.utils.setTextContent(option, allBindings.get('optionsCaption'));
5335                            ko.selectExtensions.writeValue(option, undefined);
5336                        } else {
5337                            // Apply a value to the option element
5338                            var optionValue = applyToObject(arrayEntry, allBindings.get('optionsValue'), arrayEntry);
5339                            ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
5340
5341                            // Apply some text to the option element
5342                            var optionText = applyToObject(arrayEntry, allBindings.get('optionsText'), optionValue);
5343                            ko.utils.setTextContent(option, optionText);
5344                        }
5345                        return [option];
5346                    }
5347
5348                    // By using a beforeRemove callback, we delay the removal until after new items are added. This fixes a selection
5349                    // problem in IE<=8 and Firefox. See https://github.com/knockout/knockout/issues/1208
5350                    arrayToDomNodeChildrenOptions['beforeRemove'] =
5351                        function (option) {
5352                            element.removeChild(option);
5353                        };
5354
5355                    function setSelectionCallback(arrayEntry, newOptions) {
5356                        if (itemUpdate && valueAllowUnset) {
5357                            // The model value is authoritative, so make sure its value is the one selected
5358                            ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
5359                        } else if (previousSelectedValues.length) {
5360                            // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
5361                            // That's why we first added them without selection. Now it's time to set the selection.
5362                            var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;
5363                            ko.utils.setOptionNodeSelectionState(newOptions[0], isSelected);
5364
5365                            // If this option was changed from being selected during a single-item update, notify the change
5366                            if (itemUpdate && !isSelected) {
5367                                ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
5368                            }
5369                        }
5370                    }
5371
5372                    var callback = setSelectionCallback;
5373                    if (allBindings['has']('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') == "function") {
5374                        callback = function (arrayEntry, newOptions) {
5375                            setSelectionCallback(arrayEntry, newOptions);
5376                            ko.dependencyDetection.ignore(allBindings.get('optionsAfterRender'), null, [newOptions[0], arrayEntry !== captionPlaceholder ? arrayEntry : undefined]);
5377                        }
5378                    }
5379
5380                    ko.utils.setDomNodeChildrenFromArrayMapping(element, filteredArray, optionForArrayItem, arrayToDomNodeChildrenOptions, callback);
5381
5382                    if (!valueAllowUnset) {
5383                        // Determine if the selection has changed as a result of updating the options list
5384                        var selectionChanged;
5385                        if (multiple) {
5386                            // For a multiple-select box, compare the new selection count to the previous one
5387                            // But if nothing was selected before, the selection can't have changed
5388                            selectionChanged = previousSelectedValues.length && selectedOptions().length < previousSelectedValues.length;
5389                        } else {
5390                            // For a single-select box, compare the current value to the previous value
5391                            // But if nothing was selected before or nothing is selected now, just look for a change in selection
5392                            selectionChanged = (previousSelectedValues.length && element.selectedIndex >= 0)
5393                                ? (ko.selectExtensions.readValue(element.options[element.selectedIndex]) !== previousSelectedValues[0])
5394                                : (previousSelectedValues.length || element.selectedIndex >= 0);
5395                        }
5396
5397                        // Ensure consistency between model value and selected option.
5398                        // If the dropdown was changed so that selection is no longer the same,
5399                        // notify the value or selectedOptions binding.
5400                        if (selectionChanged) {
5401                            ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
5402                        }
5403                    }
5404
5405                    if (valueAllowUnset || ko.computedContext.isInitial()) {
5406                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
5407                    }
5408
5409                    // Workaround for IE bug
5410                    ko.utils.ensureSelectElementIsRenderedCorrectly(element);
5411
5412                    if (previousScrollTop && Math.abs(previousScrollTop - element.scrollTop) > 20)
5413                        element.scrollTop = previousScrollTop;
5414                }
5415            };
5416            ko.bindingHandlers['options'].optionValueDomDataKey = ko.utils.domData.nextKey();
5417            ko.bindingHandlers['selectedOptions'] = {
5418                'init': function (element, valueAccessor, allBindings) {
5419                    function updateFromView() {
5420                        var value = valueAccessor(), valueToWrite = [];
5421                        ko.utils.arrayForEach(element.getElementsByTagName("option"), function (node) {
5422                            if (node.selected)
5423                                valueToWrite.push(ko.selectExtensions.readValue(node));
5424                        });
5425                        ko.expressionRewriting.writeValueToProperty(value, allBindings, 'selectedOptions', valueToWrite);
5426                    }
5427
5428                    function updateFromModel() {
5429                        var newValue = ko.utils.unwrapObservable(valueAccessor()),
5430                            previousScrollTop = element.scrollTop;
5431
5432                        if (newValue && typeof newValue.length == "number") {
5433                            ko.utils.arrayForEach(element.getElementsByTagName("option"), function (node) {
5434                                var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
5435                                if (node.selected != isSelected) {      // This check prevents flashing of the select element in IE
5436                                    ko.utils.setOptionNodeSelectionState(node, isSelected);
5437                                }
5438                            });
5439                        }
5440
5441                        element.scrollTop = previousScrollTop;
5442                    }
5443
5444                    if (ko.utils.tagNameLower(element) != "select") {
5445                        throw new Error("selectedOptions binding applies only to SELECT elements");
5446                    }
5447
5448                    var updateFromModelComputed;
5449                    ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
5450                        if (!updateFromModelComputed) {
5451                            ko.utils.registerEventHandler(element, "change", updateFromView);
5452                            updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
5453                        } else {
5454                            updateFromView();
5455                        }
5456                    }, null, { 'notifyImmediately': true });
5457                },
5458                'update': function () { } // Keep for backwards compatibility with code that may have wrapped binding
5459            };
5460            ko.expressionRewriting.twoWayBindings['selectedOptions'] = true;
5461            ko.bindingHandlers['style'] = {
5462                'update': function (element, valueAccessor) {
5463                    var value = ko.utils.unwrapObservable(valueAccessor() || {});
5464                    ko.utils.objectForEach(value, function (styleName, styleValue) {
5465                        styleValue = ko.utils.unwrapObservable(styleValue);
5466
5467                        if (styleValue === null || styleValue === undefined || styleValue === false) {
5468                            // Empty string removes the value, whereas null/undefined have no effect
5469                            styleValue = "";
5470                        }
5471
5472                        if (jQueryInstance) {
5473                            jQueryInstance(element)['css'](styleName, styleValue);
5474                        } else if (/^--/.test(styleName)) {
5475                            // Is styleName a custom CSS property?
5476                            element.style.setProperty(styleName, styleValue);
5477                        } else {
5478                            styleName = styleName.replace(/-(\w)/g, function (all, letter) {
5479                                return letter.toUpperCase();
5480                            });
5481
5482                            var previousStyle = element.style[styleName];
5483                            element.style[styleName] = styleValue;
5484
5485                            if (styleValue !== previousStyle && element.style[styleName] == previousStyle && !isNaN(styleValue)) {
5486                                element.style[styleName] = styleValue + "px";
5487                            }
5488                        }
5489                    });
5490                }
5491            };
5492            ko.bindingHandlers['submit'] = {
5493                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5494                    if (typeof valueAccessor() != "function")
5495                        throw new Error("The value for a submit binding must be a function");
5496                    ko.utils.registerEventHandler(element, "submit", function (event) {
5497                        var handlerReturnValue;
5498                        var value = valueAccessor();
5499                        try { handlerReturnValue = value.call(bindingContext['$data'], element); }
5500                        finally {
5501                            if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
5502                                if (event.preventDefault)
5503                                    event.preventDefault();
5504                                else
5505                                    event.returnValue = false;
5506                            }
5507                        }
5508                    });
5509                }
5510            };
5511            ko.bindingHandlers['text'] = {
5512                'init': function () {
5513                    // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).
5514                    // It should also make things faster, as we no longer have to consider whether the text node might be bindable.
5515                    return { 'controlsDescendantBindings': true };
5516                },
5517                'update': function (element, valueAccessor) {
5518                    ko.utils.setTextContent(element, valueAccessor());
5519                }
5520            };
5521            ko.virtualElements.allowedBindings['text'] = true;
5522            (function () {
5523
5524                if (window && window.navigator) {
5525                    var parseVersion = function (matches) {
5526                        if (matches) {
5527                            return parseFloat(matches[1]);
5528                        }
5529                    };
5530
5531                    // Detect various browser versions because some old versions don't fully support the 'input' event
5532                    var userAgent = window.navigator.userAgent,
5533                        operaVersion, chromeVersion, safariVersion, firefoxVersion, ieVersion, edgeVersion;
5534
5535                    (operaVersion = window.opera && window.opera.version && parseInt(window.opera.version()))
5536                        || (edgeVersion = parseVersion(userAgent.match(/Edge\/([^ ]+)$/)))
5537                        || (chromeVersion = parseVersion(userAgent.match(/Chrome\/([^ ]+)/)))
5538                        || (safariVersion = parseVersion(userAgent.match(/Version\/([^ ]+) Safari/)))
5539                        || (firefoxVersion = parseVersion(userAgent.match(/Firefox\/([^ ]+)/)))
5540                        || (ieVersion = ko.utils.ieVersion || parseVersion(userAgent.match(/MSIE ([^ ]+)/)))      // Detects up to IE 10
5541                        || (ieVersion = parseVersion(userAgent.match(/rv:([^ )]+)/)));      // Detects IE 11
5542                }
5543
5544                // IE 8 and 9 have bugs that prevent the normal events from firing when the value changes.
5545                // But it does fire the 'selectionchange' event on many of those, presumably because the
5546                // cursor is moving and that counts as the selection changing. The 'selectionchange' event is
5547                // fired at the document level only and doesn't directly indicate which element changed. We
5548                // set up just one event handler for the document and use 'activeElement' to determine which
5549                // element was changed.
5550                if (ieVersion >= 8 && ieVersion < 10) {
5551                    var selectionChangeRegisteredName = ko.utils.domData.nextKey(),
5552                        selectionChangeHandlerName = ko.utils.domData.nextKey();
5553                    var selectionChangeHandler = function (event) {
5554                        var target = this.activeElement,
5555                            handler = target && ko.utils.domData.get(target, selectionChangeHandlerName);
5556                        if (handler) {
5557                            handler(event);
5558                        }
5559                    };
5560                    var registerForSelectionChangeEvent = function (element, handler) {
5561                        var ownerDoc = element.ownerDocument;
5562                        if (!ko.utils.domData.get(ownerDoc, selectionChangeRegisteredName)) {
5563                            ko.utils.domData.set(ownerDoc, selectionChangeRegisteredName, true);
5564                            ko.utils.registerEventHandler(ownerDoc, 'selectionchange', selectionChangeHandler);
5565                        }
5566                        ko.utils.domData.set(element, selectionChangeHandlerName, handler);
5567                    };
5568                }
5569
5570                ko.bindingHandlers['textInput'] = {
5571                    'init': function (element, valueAccessor, allBindings) {
5572
5573                        var previousElementValue = element.value,
5574                            timeoutHandle,
5575                            elementValueBeforeEvent;
5576
5577                        var updateModel = function (event) {
5578                            clearTimeout(timeoutHandle);
5579                            elementValueBeforeEvent = timeoutHandle = undefined;
5580
5581                            var elementValue = element.value;
5582                            if (previousElementValue !== elementValue) {
5583                                // Provide a way for tests to know exactly which event was processed
5584                                if (DEBUG && event) element['_ko_textInputProcessedEvent'] = event.type;
5585                                previousElementValue = elementValue;
5586                                ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);
5587                            }
5588                        };
5589
5590                        var deferUpdateModel = function (event) {
5591                            if (!timeoutHandle) {
5592                                // The elementValueBeforeEvent variable is set *only* during the brief gap between an
5593                                // event firing and the updateModel function running. This allows us to ignore model
5594                                // updates that are from the previous state of the element, usually due to techniques
5595                                // such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
5596                                elementValueBeforeEvent = element.value;
5597                                var handler = DEBUG ? updateModel.bind(element, { type: event.type }) : updateModel;
5598                                timeoutHandle = ko.utils.setTimeout(handler, 4);
5599                            }
5600                        };
5601
5602                        // IE9 will mess up the DOM if you handle events synchronously which results in DOM changes (such as other bindings);
5603                        // so we'll make sure all updates are asynchronous
5604                        var ieUpdateModel = ko.utils.ieVersion == 9 ? deferUpdateModel : updateModel,
5605                            ourUpdate = false;
5606
5607                        var updateView = function () {
5608                            var modelValue = ko.utils.unwrapObservable(valueAccessor());
5609
5610                            if (modelValue === null || modelValue === undefined) {
5611                                modelValue = '';
5612                            }
5613
5614                            if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {
5615                                ko.utils.setTimeout(updateView, 4);
5616                                return;
5617                            }
5618
5619                            // Update the element only if the element and model are different. On some browsers, updating the value
5620                            // will move the cursor to the end of the input, which would be bad while the user is typing.
5621                            if (element.value !== modelValue) {
5622                                ourUpdate = true;  // Make sure we ignore events (propertychange) that result from updating the value
5623                                element.value = modelValue;
5624                                ourUpdate = false;
5625                                previousElementValue = element.value; // In case the browser changes the value (see #2281)
5626                            }
5627                        };
5628
5629                        var onEvent = function (event, handler) {
5630                            ko.utils.registerEventHandler(element, event, handler);
5631                        };
5632
5633                        if (DEBUG && ko.bindingHandlers['textInput']['_forceUpdateOn']) {
5634                            // Provide a way for tests to specify exactly which events are bound
5635                            ko.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], function (eventName) {
5636                                if (eventName.slice(0, 5) == 'after') {
5637                                    onEvent(eventName.slice(5), deferUpdateModel);
5638                                } else {
5639                                    onEvent(eventName, updateModel);
5640                                }
5641                            });
5642                        } else {
5643                            if (ieVersion) {
5644                                // All versions (including 11) of Internet Explorer have a bug that they don't generate an input or propertychange event when ESC is pressed
5645                                onEvent('keypress', updateModel);
5646                            }
5647                            if (ieVersion < 11) {
5648                                // Internet Explorer <= 8 doesn't support the 'input' event, but does include 'propertychange' that fires whenever
5649                                // any property of an element changes. Unlike 'input', it also fires if a property is changed from JavaScript code,
5650                                // but that's an acceptable compromise for this binding. IE 9 and 10 support 'input', but since they don't always
5651                                // fire it when using autocomplete, we'll use 'propertychange' for them also.
5652                                onEvent('propertychange', function (event) {
5653                                    if (!ourUpdate && event.propertyName === 'value') {
5654                                        ieUpdateModel(event);
5655                                    }
5656                                });
5657                            }
5658                            if (ieVersion == 8) {
5659                                // IE 8 has a bug where it fails to fire 'propertychange' on the first update following a value change from
5660                                // JavaScript code. It also doesn't fire if you clear the entire value. To fix this, we bind to the following
5661                                // events too.
5662                                onEvent('keyup', updateModel);      // A single keystoke
5663                                onEvent('keydown', updateModel);    // The first character when a key is held down
5664                            }
5665                            if (registerForSelectionChangeEvent) {
5666                                // Internet Explorer 9 doesn't fire the 'input' event when deleting text, including using
5667                                // the backspace, delete, or ctrl-x keys, clicking the 'x' to clear the input, dragging text
5668                                // out of the field, and cutting or deleting text using the context menu. 'selectionchange'
5669                                // can detect all of those except dragging text out of the field, for which we use 'dragend'.
5670                                // These are also needed in IE8 because of the bug described above.
5671                                registerForSelectionChangeEvent(element, ieUpdateModel);  // 'selectionchange' covers cut, paste, drop, delete, etc.
5672                                onEvent('dragend', deferUpdateModel);
5673                            }
5674
5675                            if (!ieVersion || ieVersion >= 9) {
5676                                // All other supported browsers support the 'input' event, which fires whenever the content of the element is changed
5677                                // through the user interface.
5678                                onEvent('input', ieUpdateModel);
5679                            }
5680
5681                            if (safariVersion < 5 && ko.utils.tagNameLower(element) === "textarea") {
5682                                // Safari <5 doesn't fire the 'input' event for <textarea> elements (it does fire 'textInput'
5683                                // but only when typing). So we'll just catch as much as we can with keydown, cut, and paste.
5684                                onEvent('keydown', deferUpdateModel);
5685                                onEvent('paste', deferUpdateModel);
5686                                onEvent('cut', deferUpdateModel);
5687                            } else if (operaVersion < 11) {
5688                                // Opera 10 doesn't always fire the 'input' event for cut, paste, undo & drop operations.
5689                                // We can try to catch some of those using 'keydown'.
5690                                onEvent('keydown', deferUpdateModel);
5691                            } else if (firefoxVersion < 4.0) {
5692                                // Firefox <= 3.6 doesn't fire the 'input' event when text is filled in through autocomplete
5693                                onEvent('DOMAutoComplete', updateModel);
5694
5695                                // Firefox <=3.5 doesn't fire the 'input' event when text is dropped into the input.
5696                                onEvent('dragdrop', updateModel);       // <3.5
5697                                onEvent('drop', updateModel);           // 3.5
5698                            } else if (edgeVersion && element.type === "number") {
5699                                // Microsoft Edge doesn't fire 'input' or 'change' events for number inputs when
5700                                // the value is changed via the up / down arrow keys
5701                                onEvent('keydown', deferUpdateModel);
5702                            }
5703                        }
5704
5705                        // Bind to the change event so that we can catch programmatic updates of the value that fire this event.
5706                        onEvent('change', updateModel);
5707
5708                        // To deal with browsers that don't notify any kind of event for some changes (IE, Safari, etc.)
5709                        onEvent('blur', updateModel);
5710
5711                        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
5712                    }
5713                };
5714                ko.expressionRewriting.twoWayBindings['textInput'] = true;
5715
5716                // textinput is an alias for textInput
5717                ko.bindingHandlers['textinput'] = {
5718                    // preprocess is the only way to set up a full alias
5719                    'preprocess': function (value, name, addBinding) {
5720                        addBinding('textInput', value);
5721                    }
5722                };
5723
5724            })(); ko.bindingHandlers['uniqueName'] = {
5725                'init': function (element, valueAccessor) {
5726                    if (valueAccessor()) {
5727                        var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
5728                        ko.utils.setElementName(element, name);
5729                    }
5730                }
5731            };
5732            ko.bindingHandlers['uniqueName'].currentIndex = 0;
5733            ko.bindingHandlers['using'] = {
5734                'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
5735                    var options;
5736
5737                    if (allBindings['has']('as')) {
5738                        options = { 'as': allBindings.get('as'), 'noChildContext': allBindings.get('noChildContext') };
5739                    }
5740
5741                    var innerContext = bindingContext['createChildContext'](valueAccessor, options);
5742                    ko.applyBindingsToDescendants(innerContext, element);
5743
5744                    return { 'controlsDescendantBindings': true };
5745                }
5746            };
5747            ko.virtualElements.allowedBindings['using'] = true;
5748            ko.bindingHandlers['value'] = {
5749                'init': function (element, valueAccessor, allBindings) {
5750                    var tagName = ko.utils.tagNameLower(element),
5751                        isInputElement = tagName == "input";
5752
5753                    // If the value binding is placed on a radio/checkbox, then just pass through to checkedValue and quit
5754                    if (isInputElement && (element.type == "checkbox" || element.type == "radio")) {
5755                        ko.applyBindingAccessorsToNode(element, { 'checkedValue': valueAccessor });
5756                        return;
5757                    }
5758
5759                    var eventsToCatch = [];
5760                    var requestedEventsToCatch = allBindings.get("valueUpdate");
5761                    var propertyChangedFired = false;
5762                    var elementValueBeforeEvent = null;
5763
5764                    if (requestedEventsToCatch) {
5765                        // Allow both individual event names, and arrays of event names
5766                        if (typeof requestedEventsToCatch == "string") {
5767                            eventsToCatch = [requestedEventsToCatch];
5768                        } else {
5769                            eventsToCatch = ko.utils.arrayGetDistinctValues(requestedEventsToCatch);
5770                        }
5771                        ko.utils.arrayRemoveItem(eventsToCatch, "change");  // We'll subscribe to "change" events later
5772                    }
5773
5774                    var valueUpdateHandler = function () {
5775                        elementValueBeforeEvent = null;
5776                        propertyChangedFired = false;
5777                        var modelValue = valueAccessor();
5778                        var elementValue = ko.selectExtensions.readValue(element);
5779                        ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'value', elementValue);
5780                    }
5781
5782                    // Workaround for https://github.com/SteveSanderson/knockout/issues/122
5783                    // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
5784                    var ieAutoCompleteHackNeeded = ko.utils.ieVersion && isInputElement && element.type == "text"
5785                        && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
5786                    if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
5787                        ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
5788                        ko.utils.registerEventHandler(element, "focus", function () { propertyChangedFired = false });
5789                        ko.utils.registerEventHandler(element, "blur", function () {
5790                            if (propertyChangedFired) {
5791                                valueUpdateHandler();
5792                            }
5793                        });
5794                    }
5795
5796                    ko.utils.arrayForEach(eventsToCatch, function (eventName) {
5797                        // The syntax "after<eventname>" means "run the handler asynchronously after the event"
5798                        // This is useful, for example, to catch "keydown" events after the browser has updated the control
5799                        // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
5800                        var handler = valueUpdateHandler;
5801                        if (ko.utils.stringStartsWith(eventName, "after")) {
5802                            handler = function () {
5803                                // The elementValueBeforeEvent variable is non-null *only* during the brief gap between
5804                                // a keyX event firing and the valueUpdateHandler running, which is scheduled to happen
5805                                // at the earliest asynchronous opportunity. We store this temporary information so that
5806                                // if, between keyX and valueUpdateHandler, the underlying model value changes separately,
5807                                // we can overwrite that model value change with the value the user just typed. Otherwise,
5808                                // techniques like rateLimit can trigger model changes at critical moments that will
5809                                // override the user's inputs, causing keystrokes to be lost.
5810                                elementValueBeforeEvent = ko.selectExtensions.readValue(element);
5811                                ko.utils.setTimeout(valueUpdateHandler, 0);
5812                            };
5813                            eventName = eventName.substring("after".length);
5814                        }
5815                        ko.utils.registerEventHandler(element, eventName, handler);
5816                    });
5817
5818                    var updateFromModel;
5819
5820                    if (isInputElement && element.type == "file") {
5821                        // For file input elements, can only write the empty string
5822                        updateFromModel = function () {
5823                            var newValue = ko.utils.unwrapObservable(valueAccessor());
5824                            if (newValue === null || newValue === undefined || newValue === "") {
5825                                element.value = "";
5826                            } else {
5827                                ko.dependencyDetection.ignore(valueUpdateHandler);  // reset the model to match the element
5828                            }
5829                        }
5830                    } else {
5831                        updateFromModel = function () {
5832                            var newValue = ko.utils.unwrapObservable(valueAccessor());
5833                            var elementValue = ko.selectExtensions.readValue(element);
5834
5835                            if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {
5836                                ko.utils.setTimeout(updateFromModel, 0);
5837                                return;
5838                            }
5839
5840                            var valueHasChanged = newValue !== elementValue;
5841
5842                            if (valueHasChanged || elementValue === undefined) {
5843                                if (tagName === "select") {
5844                                    var allowUnset = allBindings.get('valueAllowUnset');
5845                                    ko.selectExtensions.writeValue(element, newValue, allowUnset);
5846                                    if (!allowUnset && newValue !== ko.selectExtensions.readValue(element)) {
5847                                        // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
5848                                        // because you're not allowed to have a model value that disagrees with a visible UI selection.
5849                                        ko.dependencyDetection.ignore(valueUpdateHandler);
5850                                    }
5851                                } else {
5852                                    ko.selectExtensions.writeValue(element, newValue);
5853                                }
5854                            }
5855                        };
5856                    }
5857
5858                    if (tagName === "select") {
5859                        var updateFromModelComputed;
5860                        ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
5861                            if (!updateFromModelComputed) {
5862                                ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
5863                                updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
5864                            } else if (allBindings.get('valueAllowUnset')) {
5865                                updateFromModel();
5866                            } else {
5867                                valueUpdateHandler();
5868                            }
5869                        }, null, { 'notifyImmediately': true });
5870                    } else {
5871                        ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
5872                        ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
5873                    }
5874                },
5875                'update': function () { } // Keep for backwards compatibility with code that may have wrapped value binding
5876            };
5877            ko.expressionRewriting.twoWayBindings['value'] = true;
5878            ko.bindingHandlers['visible'] = {
5879                'update': function (element, valueAccessor) {
5880                    var value = ko.utils.unwrapObservable(valueAccessor());
5881                    var isCurrentlyVisible = !(element.style.display == "none");
5882                    if (value && !isCurrentlyVisible)
5883                        element.style.display = "";
5884                    else if ((!value) && isCurrentlyVisible)
5885                        element.style.display = "none";
5886                }
5887            };
5888
5889            ko.bindingHandlers['hidden'] = {
5890                'update': function (element, valueAccessor) {
5891                    ko.bindingHandlers['visible']['update'](element, function () { return !ko.utils.unwrapObservable(valueAccessor()) });
5892                }
5893            };
5894            // 'click' is just a shorthand for the usual full-length event:{click:handler}
5895            makeEventHandlerShortcut('click');
5896            // If you want to make a custom template engine,
5897            //
5898            // [1] Inherit from this class (like ko.nativeTemplateEngine does)
5899            // [2] Override 'renderTemplateSource', supplying a function with this signature:
5900            //
5901            //        function (templateSource, bindingContext, options) {
5902            //            // - templateSource.text() is the text of the template you should render
5903            //            // - bindingContext.$data is the data you should pass into the template
5904            //            //   - you might also want to make bindingContext.$parent, bindingContext.$parents,
5905            //            //     and bindingContext.$root available in the template too
5906            //            // - options gives you access to any other properties set on "data-bind: { template: options }"
5907            //            // - templateDocument is the document object of the template
5908            //            //
5909            //            // Return value: an array of DOM nodes
5910            //        }
5911            //
5912            // [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
5913            //
5914            //        function (script) {
5915            //            // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
5916            //            //               For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
5917            //        }
5918            //
5919            //     This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
5920            //     If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
5921            //     and then you don't need to override 'createJavaScriptEvaluatorBlock'.
5922
5923            ko.templateEngine = function () { };
5924
5925            ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
5926                throw new Error("Override renderTemplateSource");
5927            };
5928
5929            ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
5930                throw new Error("Override createJavaScriptEvaluatorBlock");
5931            };
5932
5933            ko.templateEngine.prototype['makeTemplateSource'] = function (template, templateDocument) {
5934                // Named template
5935                if (typeof template == "string") {
5936                    templateDocument = templateDocument || document;
5937                    var elem = templateDocument.getElementById(template);
5938                    if (!elem)
5939                        throw new Error("Cannot find template with ID " + template);
5940                    return new ko.templateSources.domElement(elem);
5941                } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
5942                    // Anonymous template
5943                    return new ko.templateSources.anonymousTemplate(template);
5944                } else
5945                    throw new Error("Unknown template type: " + template);
5946            };
5947
5948            ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
5949                var templateSource = this['makeTemplateSource'](template, templateDocument);
5950                return this['renderTemplateSource'](templateSource, bindingContext, options, templateDocument);
5951            };
5952
5953            ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
5954                // Skip rewriting if requested
5955                if (this['allowTemplateRewriting'] === false)
5956                    return true;
5957                return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
5958            };
5959
5960            ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
5961                var templateSource = this['makeTemplateSource'](template, templateDocument);
5962                var rewritten = rewriterCallback(templateSource['text']());
5963                templateSource['text'](rewritten);
5964                templateSource['data']("isRewritten", true);
5965            };
5966
5967            ko.exportSymbol('templateEngine', ko.templateEngine);
5968
5969            ko.templateRewriting = (function () {
5970                var memoizeDataBindingAttributeSyntaxRegex = /(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi;
5971                var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
5972
5973                function validateDataBindValuesForRewriting(keyValueArray) {
5974                    var allValidators = ko.expressionRewriting.bindingRewriteValidators;
5975                    for (var i = 0; i < keyValueArray.length; i++) {
5976                        var key = keyValueArray[i]['key'];
5977                        if (Object.prototype.hasOwnProperty.call(allValidators, key)) {
5978                            var validator = allValidators[key];
5979
5980                            if (typeof validator === "function") {
5981                                var possibleErrorMessage = validator(keyValueArray[i]['value']);
5982                                if (possibleErrorMessage)
5983                                    throw new Error(possibleErrorMessage);
5984                            } else if (!validator) {
5985                                throw new Error("This template engine does not support the '" + key + "' binding within its templates");
5986                            }
5987                        }
5988                    }
5989                }
5990
5991                function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, nodeName, templateEngine) {
5992                    var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
5993                    validateDataBindValuesForRewriting(dataBindKeyValueArray);
5994                    var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray, { 'valueAccessors': true });
5995
5996                    // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
5997                    // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
5998                    // extra indirection.
5999                    var applyBindingsToNextSiblingScript =
6000                        "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()},'" + nodeName.toLowerCase() + "')";
6001                    return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
6002                }
6003
6004                return {
6005                    ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
6006                        if (!templateEngine['isTemplateRewritten'](template, templateDocument))
6007                            templateEngine['rewriteTemplate'](template, function (htmlString) {
6008                                return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
6009                            }, templateDocument);
6010                    },
6011
6012                    memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
6013                        return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
6014                            return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[4], /* tagToRetain: */ arguments[1], /* nodeName: */ arguments[2], templateEngine);
6015                        }).replace(memoizeVirtualContainerBindingSyntaxRegex, function () {
6016                            return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", /* nodeName: */ "#comment", templateEngine);
6017                        });
6018                    },
6019
6020                    applyMemoizedBindingsToNextSibling: function (bindings, nodeName) {
6021                        return ko.memoization.memoize(function (domNode, bindingContext) {
6022                            var nodeToBind = domNode.nextSibling;
6023                            if (nodeToBind && nodeToBind.nodeName.toLowerCase() === nodeName) {
6024                                ko.applyBindingAccessorsToNode(nodeToBind, bindings, bindingContext);
6025                            }
6026                        });
6027                    }
6028                }
6029            })();
6030
6031
6032            // Exported only because it has to be referenced by string lookup from within rewritten template
6033            ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
6034            (function () {
6035                // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
6036                // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
6037                //
6038                // Two are provided by default:
6039                //  1. ko.templateSources.domElement       - reads/writes the text content of an arbitrary DOM element
6040                //  2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
6041                //                                           without reading/writing the actual element text content, since it will be overwritten
6042                //                                           with the rendered template output.
6043                // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
6044                // Template sources need to have the following functions:
6045                //   text() 			- returns the template text from your storage location
6046                //   text(value)		- writes the supplied template text to your storage location
6047                //   data(key)			- reads values stored using data(key, value) - see below
6048                //   data(key, value)	- associates "value" with this template and the key "key". Is used to store information like "isRewritten".
6049                //
6050                // Optionally, template sources can also have the following functions:
6051                //   nodes()            - returns a DOM element containing the nodes of this template, where available
6052                //   nodes(value)       - writes the given DOM element to your storage location
6053                // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
6054                // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
6055                //
6056                // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
6057                // using and overriding "makeTemplateSource" to return an instance of your custom template source.
6058
6059                ko.templateSources = {};
6060
6061                // ---- ko.templateSources.domElement -----
6062
6063                // template types
6064                var templateScript = 1,
6065                    templateTextArea = 2,
6066                    templateTemplate = 3,
6067                    templateElement = 4;
6068
6069                ko.templateSources.domElement = function (element) {
6070                    this.domElement = element;
6071
6072                    if (element) {
6073                        var tagNameLower = ko.utils.tagNameLower(element);
6074                        this.templateType =
6075                            tagNameLower === "script" ? templateScript :
6076                                tagNameLower === "textarea" ? templateTextArea :
6077                                    // For browsers with proper <template> element support, where the .content property gives a document fragment
6078                                    tagNameLower == "template" && element.content && element.content.nodeType === 11 ? templateTemplate :
6079                                        templateElement;
6080                    }
6081                }
6082
6083                ko.templateSources.domElement.prototype['text'] = function (/* valueToWrite */) {
6084                    var elemContentsProperty = this.templateType === templateScript ? "text"
6085                        : this.templateType === templateTextArea ? "value"
6086                            : "innerHTML";
6087
6088                    if (arguments.length == 0) {
6089                        return this.domElement[elemContentsProperty];
6090                    } else {
6091                        var valueToWrite = arguments[0];
6092                        if (elemContentsProperty === "innerHTML")
6093                            ko.utils.setHtml(this.domElement, valueToWrite);
6094                        else
6095                            this.domElement[elemContentsProperty] = valueToWrite;
6096                    }
6097                };
6098
6099                var dataDomDataPrefix = ko.utils.domData.nextKey() + "_";
6100                ko.templateSources.domElement.prototype['data'] = function (key /*, valueToWrite */) {
6101                    if (arguments.length === 1) {
6102                        return ko.utils.domData.get(this.domElement, dataDomDataPrefix + key);
6103                    } else {
6104                        ko.utils.domData.set(this.domElement, dataDomDataPrefix + key, arguments[1]);
6105                    }
6106                };
6107
6108                var templatesDomDataKey = ko.utils.domData.nextKey();
6109                function getTemplateDomData(element) {
6110                    return ko.utils.domData.get(element, templatesDomDataKey) || {};
6111                }
6112                function setTemplateDomData(element, data) {
6113                    ko.utils.domData.set(element, templatesDomDataKey, data);
6114                }
6115
6116                ko.templateSources.domElement.prototype['nodes'] = function (/* valueToWrite */) {
6117                    var element = this.domElement;
6118                    if (arguments.length == 0) {
6119                        var templateData = getTemplateDomData(element),
6120                            nodes = templateData.containerData || (
6121                                this.templateType === templateTemplate ? element.content :
6122                                    this.templateType === templateElement ? element :
6123                                        undefined);
6124                        if (!nodes || templateData.alwaysCheckText) {
6125                            // If the template is associated with an element that stores the template as text,
6126                            // parse and cache the nodes whenever there's new text content available. This allows
6127                            // the user to update the template content by updating the text of template node.
6128                            var text = this['text']();
6129                            if (text && text !== templateData.textData) {
6130                                nodes = ko.utils.parseHtmlForTemplateNodes(text, element.ownerDocument);
6131                                setTemplateDomData(element, { containerData: nodes, textData: text, alwaysCheckText: true });
6132                            }
6133                        }
6134                        return nodes;
6135                    } else {
6136                        var valueToWrite = arguments[0];
6137                        if (this.templateType !== undefined) {
6138                            this['text']("");   // clear the text from the node
6139                        }
6140                        setTemplateDomData(element, { containerData: valueToWrite });
6141                    }
6142                };
6143
6144                // ---- ko.templateSources.anonymousTemplate -----
6145                // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
6146                // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
6147                // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
6148
6149                ko.templateSources.anonymousTemplate = function (element) {
6150                    this.domElement = element;
6151                }
6152                ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
6153                ko.templateSources.anonymousTemplate.prototype.constructor = ko.templateSources.anonymousTemplate;
6154                ko.templateSources.anonymousTemplate.prototype['text'] = function (/* valueToWrite */) {
6155                    if (arguments.length == 0) {
6156                        var templateData = getTemplateDomData(this.domElement);
6157                        if (templateData.textData === undefined && templateData.containerData)
6158                            templateData.textData = templateData.containerData.innerHTML;
6159                        return templateData.textData;
6160                    } else {
6161                        var valueToWrite = arguments[0];
6162                        setTemplateDomData(this.domElement, { textData: valueToWrite });
6163                    }
6164                };
6165
6166                ko.exportSymbol('templateSources', ko.templateSources);
6167                ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
6168                ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
6169            })();
6170            (function () {
6171                var _templateEngine;
6172                ko.setTemplateEngine = function (templateEngine) {
6173                    if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
6174                        throw new Error("templateEngine must inherit from ko.templateEngine");
6175                    _templateEngine = templateEngine;
6176                }
6177
6178                function invokeForEachNodeInContinuousRange(firstNode, lastNode, action) {
6179                    var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
6180                    while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
6181                        nextInQueue = ko.virtualElements.nextSibling(node);
6182                        action(node, nextInQueue);
6183                    }
6184                }
6185
6186                function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
6187                    // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
6188                    // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
6189                    // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
6190                    // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
6191                    // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
6192
6193                    if (continuousNodeArray.length) {
6194                        var firstNode = continuousNodeArray[0],
6195                            lastNode = continuousNodeArray[continuousNodeArray.length - 1],
6196                            parentNode = firstNode.parentNode,
6197                            provider = ko.bindingProvider['instance'],
6198                            preprocessNode = provider['preprocessNode'];
6199
6200                        if (preprocessNode) {
6201                            invokeForEachNodeInContinuousRange(firstNode, lastNode, function (node, nextNodeInRange) {
6202                                var nodePreviousSibling = node.previousSibling;
6203                                var newNodes = preprocessNode.call(provider, node);
6204                                if (newNodes) {
6205                                    if (node === firstNode)
6206                                        firstNode = newNodes[0] || nextNodeInRange;
6207                                    if (node === lastNode)
6208                                        lastNode = newNodes[newNodes.length - 1] || nodePreviousSibling;
6209                                }
6210                            });
6211
6212                            // Because preprocessNode can change the nodes, including the first and last nodes, update continuousNodeArray to match.
6213                            // We need the full set, including inner nodes, because the unmemoize step might remove the first node (and so the real
6214                            // first node needs to be in the array).
6215                            continuousNodeArray.length = 0;
6216                            if (!firstNode) { // preprocessNode might have removed all the nodes, in which case there's nothing left to do
6217                                return;
6218                            }
6219                            if (firstNode === lastNode) {
6220                                continuousNodeArray.push(firstNode);
6221                            } else {
6222                                continuousNodeArray.push(firstNode, lastNode);
6223                                ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);
6224                            }
6225                        }
6226
6227                        // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
6228                        // whereas a regular applyBindings won't introduce new memoized nodes
6229                        invokeForEachNodeInContinuousRange(firstNode, lastNode, function (node) {
6230                            if (node.nodeType === 1 || node.nodeType === 8)
6231                                ko.applyBindings(bindingContext, node);
6232                        });
6233                        invokeForEachNodeInContinuousRange(firstNode, lastNode, function (node) {
6234                            if (node.nodeType === 1 || node.nodeType === 8)
6235                                ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
6236                        });
6237
6238                        // Make sure any changes done by applyBindings or unmemoize are reflected in the array
6239                        ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);
6240                    }
6241                }
6242
6243                function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
6244                    return nodeOrNodeArray.nodeType ? nodeOrNodeArray
6245                        : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
6246                            : null;
6247                }
6248
6249                function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
6250                    options = options || {};
6251                    var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
6252                    var templateDocument = (firstTargetNode || template || {}).ownerDocument;
6253                    var templateEngineToUse = (options['templateEngine'] || _templateEngine);
6254                    ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
6255                    var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
6256
6257                    // Loosely check result is an array of DOM nodes
6258                    if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
6259                        throw new Error("Template engine must return an array of DOM nodes");
6260
6261                    var haveAddedNodesToParent = false;
6262                    switch (renderMode) {
6263                        case "replaceChildren":
6264                            ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
6265                            haveAddedNodesToParent = true;
6266                            break;
6267                        case "replaceNode":
6268                            ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
6269                            haveAddedNodesToParent = true;
6270                            break;
6271                        case "ignoreTargetNode": break;
6272                        default:
6273                            throw new Error("Unknown renderMode: " + renderMode);
6274                    }
6275
6276                    if (haveAddedNodesToParent) {
6277                        activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
6278                        if (options['afterRender']) {
6279                            ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext[options['as'] || '$data']]);
6280                        }
6281                        if (renderMode == "replaceChildren") {
6282                            ko.bindingEvent.notify(targetNodeOrNodeArray, ko.bindingEvent.childrenComplete);
6283                        }
6284                    }
6285
6286                    return renderedNodesArray;
6287                }
6288
6289                function resolveTemplateName(template, data, context) {
6290                    // The template can be specified as:
6291                    if (ko.isObservable(template)) {
6292                        // 1. An observable, with string value
6293                        return template();
6294                    } else if (typeof template === 'function') {
6295                        // 2. A function of (data, context) returning a string
6296                        return template(data, context);
6297                    } else {
6298                        // 3. A string
6299                        return template;
6300                    }
6301                }
6302
6303                ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
6304                    options = options || {};
6305                    if ((options['templateEngine'] || _templateEngine) == undefined)
6306                        throw new Error("Set a template engine before calling renderTemplate");
6307                    renderMode = renderMode || "replaceChildren";
6308
6309                    if (targetNodeOrNodeArray) {
6310                        var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
6311
6312                        var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
6313                        var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
6314
6315                        return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
6316                            function () {
6317                                // Ensure we've got a proper binding context to work with
6318                                var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
6319                                    ? dataOrBindingContext
6320                                    : new ko.bindingContext(dataOrBindingContext, null, null, null, { "exportDependencies": true });
6321
6322                                var templateName = resolveTemplateName(template, bindingContext['$data'], bindingContext),
6323                                    renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
6324
6325                                if (renderMode == "replaceNode") {
6326                                    targetNodeOrNodeArray = renderedNodesArray;
6327                                    firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
6328                                }
6329                            },
6330                            null,
6331                            { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
6332                        );
6333                    } else {
6334                        // 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
6335                        return ko.memoization.memoize(function (domNode) {
6336                            ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
6337                        });
6338                    }
6339                };
6340
6341                ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
6342                    // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
6343                    // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
6344                    var arrayItemContext, asName = options['as'];
6345
6346                    // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
6347                    var executeTemplateForArrayItem = function (arrayValue, index) {
6348                        // Support selecting template as a function of the data being rendered
6349                        arrayItemContext = parentBindingContext['createChildContext'](arrayValue, {
6350                            'as': asName,
6351                            'noChildContext': options['noChildContext'],
6352                            'extend': function (context) {
6353                                context['$index'] = index;
6354                                if (asName) {
6355                                    context[asName + "Index"] = index;
6356                                }
6357                            }
6358                        });
6359
6360                        var templateName = resolveTemplateName(template, arrayValue, arrayItemContext);
6361                        return executeTemplate(targetNode, "ignoreTargetNode", templateName, arrayItemContext, options);
6362                    };
6363
6364                    // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
6365                    var activateBindingsCallback = function (arrayValue, addedNodesArray, index) {
6366                        activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
6367                        if (options['afterRender'])
6368                            options['afterRender'](addedNodesArray, arrayValue);
6369
6370                        // release the "cache" variable, so that it can be collected by
6371                        // the GC when its value isn't used from within the bindings anymore.
6372                        arrayItemContext = null;
6373                    };
6374
6375                    var setDomNodeChildrenFromArrayMapping = function (newArray, changeList) {
6376                        // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
6377                        // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
6378                        ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, newArray, executeTemplateForArrayItem, options, activateBindingsCallback, changeList]);
6379                        ko.bindingEvent.notify(targetNode, ko.bindingEvent.childrenComplete);
6380                    };
6381
6382                    var shouldHideDestroyed = (options['includeDestroyed'] === false) || (ko.options['foreachHidesDestroyed'] && !options['includeDestroyed']);
6383
6384                    if (!shouldHideDestroyed && !options['beforeRemove'] && ko.isObservableArray(arrayOrObservableArray)) {
6385                        setDomNodeChildrenFromArrayMapping(arrayOrObservableArray.peek());
6386
6387                        var subscription = arrayOrObservableArray.subscribe(function (changeList) {
6388                            setDomNodeChildrenFromArrayMapping(arrayOrObservableArray(), changeList);
6389                        }, null, "arrayChange");
6390                        subscription.disposeWhenNodeIsRemoved(targetNode);
6391
6392                        return subscription;
6393                    } else {
6394                        return ko.dependentObservable(function () {
6395                            var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
6396                            if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
6397                                unwrappedArray = [unwrappedArray];
6398
6399                            if (shouldHideDestroyed) {
6400                                // Filter out any entries marked as destroyed
6401                                unwrappedArray = ko.utils.arrayFilter(unwrappedArray, function (item) {
6402                                    return item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
6403                                });
6404                            }
6405                            setDomNodeChildrenFromArrayMapping(unwrappedArray);
6406
6407                        }, null, { disposeWhenNodeIsRemoved: targetNode });
6408                    }
6409                };
6410
6411                var templateComputedDomDataKey = ko.utils.domData.nextKey();
6412                function disposeOldComputedAndStoreNewOne(element, newComputed) {
6413                    var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
6414                    if (oldComputed && (typeof (oldComputed.dispose) == 'function'))
6415                        oldComputed.dispose();
6416                    ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && (!newComputed.isActive || newComputed.isActive())) ? newComputed : undefined);
6417                }
6418
6419                var cleanContainerDomDataKey = ko.utils.domData.nextKey();
6420                ko.bindingHandlers['template'] = {
6421                    'init': function (element, valueAccessor) {
6422                        // Support anonymous templates
6423                        var bindingValue = ko.utils.unwrapObservable(valueAccessor());
6424                        if (typeof bindingValue == "string" || 'name' in bindingValue) {
6425                            // It's a named template - clear the element
6426                            ko.virtualElements.emptyNode(element);
6427                        } else if ('nodes' in bindingValue) {
6428                            // We've been given an array of DOM nodes. Save them as the template source.
6429                            // There is no known use case for the node array being an observable array (if the output
6430                            // varies, put that behavior *into* your template - that's what templates are for), and
6431                            // the implementation would be a mess, so assert that it's not observable.
6432                            var nodes = bindingValue['nodes'] || [];
6433                            if (ko.isObservable(nodes)) {
6434                                throw new Error('The "nodes" option must be a plain, non-observable array.');
6435                            }
6436
6437                            // If the nodes are already attached to a KO-generated container, we reuse that container without moving the
6438                            // elements to a new one (we check only the first node, as the nodes are always moved together)
6439                            var container = nodes[0] && nodes[0].parentNode;
6440                            if (!container || !ko.utils.domData.get(container, cleanContainerDomDataKey)) {
6441                                container = ko.utils.moveCleanedNodesToContainerElement(nodes);
6442                                ko.utils.domData.set(container, cleanContainerDomDataKey, true);
6443                            }
6444
6445                            new ko.templateSources.anonymousTemplate(element)['nodes'](container);
6446                        } else {
6447                            // It's an anonymous template - store the element contents, then clear the element
6448                            var templateNodes = ko.virtualElements.childNodes(element);
6449                            if (templateNodes.length > 0) {
6450                                var container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
6451                                new ko.templateSources.anonymousTemplate(element)['nodes'](container);
6452                            } else {
6453                                throw new Error("Anonymous template defined, but no template content was provided");
6454                            }
6455                        }
6456                        return { 'controlsDescendantBindings': true };
6457                    },
6458                    'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
6459                        var value = valueAccessor(),
6460                            options = ko.utils.unwrapObservable(value),
6461                            shouldDisplay = true,
6462                            templateComputed = null,
6463                            template;
6464
6465                        if (typeof options == "string") {
6466                            template = value;
6467                            options = {};
6468                        } else {
6469                            template = 'name' in options ? options['name'] : element;
6470
6471                            // Support "if"/"ifnot" conditions
6472                            if ('if' in options)
6473                                shouldDisplay = ko.utils.unwrapObservable(options['if']);
6474                            if (shouldDisplay && 'ifnot' in options)
6475                                shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
6476
6477                            // Don't show anything if an empty name is given (see #2446)
6478                            if (shouldDisplay && !template) {
6479                                shouldDisplay = false;
6480                            }
6481                        }
6482
6483                        if ('foreach' in options) {
6484                            // Render once for each data point (treating data set as empty if shouldDisplay==false)
6485                            var dataArray = (shouldDisplay && options['foreach']) || [];
6486                            templateComputed = ko.renderTemplateForEach(template, dataArray, options, element, bindingContext);
6487                        } else if (!shouldDisplay) {
6488                            ko.virtualElements.emptyNode(element);
6489                        } else {
6490                            // Render once for this single data point (or use the viewModel if no data was provided)
6491                            var innerBindingContext = bindingContext;
6492                            if ('data' in options) {
6493                                innerBindingContext = bindingContext['createChildContext'](options['data'], {
6494                                    'as': options['as'],
6495                                    'noChildContext': options['noChildContext'],
6496                                    'exportDependencies': true
6497                                });
6498                            }
6499                            templateComputed = ko.renderTemplate(template, innerBindingContext, options, element);
6500                        }
6501
6502                        // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
6503                        disposeOldComputedAndStoreNewOne(element, templateComputed);
6504                    }
6505                };
6506
6507                // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
6508                ko.expressionRewriting.bindingRewriteValidators['template'] = function (bindingValue) {
6509                    var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
6510
6511                    if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
6512                        return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)
6513
6514                    if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
6515                        return null; // Named templates can be rewritten, so return "no error"
6516                    return "This template engine does not support anonymous templates nested within its templates";
6517                };
6518
6519                ko.virtualElements.allowedBindings['template'] = true;
6520            })();
6521
6522            ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
6523            ko.exportSymbol('renderTemplate', ko.renderTemplate);
6524            // Go through the items that have been added and deleted and try to find matches between them.
6525            ko.utils.findMovesInArrayComparison = function (left, right, limitFailedCompares) {
6526                if (left.length && right.length) {
6527                    var failedCompares, l, r, leftItem, rightItem;
6528                    for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
6529                        for (r = 0; rightItem = right[r]; ++r) {
6530                            if (leftItem['value'] === rightItem['value']) {
6531                                leftItem['moved'] = rightItem['index'];
6532                                rightItem['moved'] = leftItem['index'];
6533                                right.splice(r, 1);         // This item is marked as moved; so remove it from right list
6534                                failedCompares = r = 0;     // Reset failed compares count because we're checking for consecutive failures
6535                                break;
6536                            }
6537                        }
6538                        failedCompares += r;
6539                    }
6540                }
6541            };
6542
6543            ko.utils.compareArrays = (function () {
6544                var statusNotInOld = 'added', statusNotInNew = 'deleted';
6545
6546                // Simple calculation based on Levenshtein distance.
6547                function compareArrays(oldArray, newArray, options) {
6548                    // For backward compatibility, if the third arg is actually a bool, interpret
6549                    // it as the old parameter 'dontLimitMoves'. Newer code should use { dontLimitMoves: true }.
6550                    options = (typeof options === 'boolean') ? { 'dontLimitMoves': options } : (options || {});
6551                    oldArray = oldArray || [];
6552                    newArray = newArray || [];
6553
6554                    if (oldArray.length < newArray.length)
6555                        return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, options);
6556                    else
6557                        return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, options);
6558                }
6559
6560                function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, options) {
6561                    var myMin = Math.min,
6562                        myMax = Math.max,
6563                        editDistanceMatrix = [],
6564                        smlIndex, smlIndexMax = smlArray.length,
6565                        bigIndex, bigIndexMax = bigArray.length,
6566                        compareRange = (bigIndexMax - smlIndexMax) || 1,
6567                        maxDistance = smlIndexMax + bigIndexMax + 1,
6568                        thisRow, lastRow,
6569                        bigIndexMaxForRow, bigIndexMinForRow;
6570
6571                    for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
6572                        lastRow = thisRow;
6573                        editDistanceMatrix.push(thisRow = []);
6574                        bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
6575                        bigIndexMinForRow = myMax(0, smlIndex - 1);
6576                        for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
6577                            if (!bigIndex)
6578                                thisRow[bigIndex] = smlIndex + 1;
6579                            else if (!smlIndex)  // Top row - transform empty array into new array via additions
6580                                thisRow[bigIndex] = bigIndex + 1;
6581                            else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
6582                                thisRow[bigIndex] = lastRow[bigIndex - 1];                  // copy value (no edit)
6583                            else {
6584                                var northDistance = lastRow[bigIndex] || maxDistance;       // not in big (deletion)
6585                                var westDistance = thisRow[bigIndex - 1] || maxDistance;    // not in small (addition)
6586                                thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
6587                            }
6588                        }
6589                    }
6590
6591                    var editScript = [], meMinusOne, notInSml = [], notInBig = [];
6592                    for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
6593                        meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
6594                        if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex - 1]) {
6595                            notInSml.push(editScript[editScript.length] = {     // added
6596                                'status': statusNotInSml,
6597                                'value': bigArray[--bigIndex],
6598                                'index': bigIndex
6599                            });
6600                        } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
6601                            notInBig.push(editScript[editScript.length] = {     // deleted
6602                                'status': statusNotInBig,
6603                                'value': smlArray[--smlIndex],
6604                                'index': smlIndex
6605                            });
6606                        } else {
6607                            --bigIndex;
6608                            --smlIndex;
6609                            if (!options['sparse']) {
6610                                editScript.push({
6611                                    'status': "retained",
6612                                    'value': bigArray[bigIndex]
6613                                });
6614                            }
6615                        }
6616                    }
6617
6618                    // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
6619                    // smlIndexMax keeps the time complexity of this algorithm linear.
6620                    ko.utils.findMovesInArrayComparison(notInBig, notInSml, !options['dontLimitMoves'] && smlIndexMax * 10);
6621
6622                    return editScript.reverse();
6623                }
6624
6625                return compareArrays;
6626            })();
6627
6628            ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
6629            (function () {
6630                // Objective:
6631                // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
6632                //   map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
6633                // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
6634                //   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
6635                //   previously mapped - retain those nodes, and just insert/delete other ones
6636
6637                // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
6638                // You can use this, for example, to activate bindings on those nodes.
6639
6640                function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
6641                    // Map this array value inside a dependentObservable so we re-map when any dependency changes
6642                    var mappedNodes = [];
6643                    var dependentObservable = ko.dependentObservable(function () {
6644                        var newMappedNodes = mapping(valueToMap, index, ko.utils.fixUpContinuousNodeArray(mappedNodes, containerNode)) || [];
6645
6646                        // On subsequent evaluations, just replace the previously-inserted DOM nodes
6647                        if (mappedNodes.length > 0) {
6648                            ko.utils.replaceDomNodes(mappedNodes, newMappedNodes);
6649                            if (callbackAfterAddingNodes)
6650                                ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
6651                        }
6652
6653                        // Replace the contents of the mappedNodes array, thereby updating the record
6654                        // of which nodes would be deleted if valueToMap was itself later removed
6655                        mappedNodes.length = 0;
6656                        ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
6657                    }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function () { return !ko.utils.anyDomNodeIsAttachedToDocument(mappedNodes); } });
6658                    return { mappedNodes: mappedNodes, dependentObservable: (dependentObservable.isActive() ? dependentObservable : undefined) };
6659                }
6660
6661                var lastMappingResultDomDataKey = ko.utils.domData.nextKey(),
6662                    deletedItemDummyValue = ko.utils.domData.nextKey();
6663
6664                ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes, editScript) {
6665                    array = array || [];
6666                    if (typeof array.length == "undefined") // Coerce single value into array
6667                        array = [array];
6668
6669                    options = options || {};
6670                    var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey);
6671                    var isFirstExecution = !lastMappingResult;
6672
6673                    // Build the new mapping result
6674                    var newMappingResult = [];
6675                    var lastMappingResultIndex = 0;
6676                    var currentArrayIndex = 0;
6677
6678                    var nodesToDelete = [];
6679                    var itemsToMoveFirstIndexes = [];
6680                    var itemsForBeforeRemoveCallbacks = [];
6681                    var itemsForMoveCallbacks = [];
6682                    var itemsForAfterAddCallbacks = [];
6683                    var mapData;
6684                    var countWaitingForRemove = 0;
6685
6686                    function itemAdded(value) {
6687                        mapData = { arrayEntry: value, indexObservable: ko.observable(currentArrayIndex++) };
6688                        newMappingResult.push(mapData);
6689                        if (!isFirstExecution) {
6690                            itemsForAfterAddCallbacks.push(mapData);
6691                        }
6692                    }
6693
6694                    function itemMovedOrRetained(oldPosition) {
6695                        mapData = lastMappingResult[oldPosition];
6696                        if (currentArrayIndex !== mapData.indexObservable.peek())
6697                            itemsForMoveCallbacks.push(mapData);
6698                        // Since updating the index might change the nodes, do so before calling fixUpContinuousNodeArray
6699                        mapData.indexObservable(currentArrayIndex++);
6700                        ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode);
6701                        newMappingResult.push(mapData);
6702                    }
6703
6704                    function callCallback(callback, items) {
6705                        if (callback) {
6706                            for (var i = 0, n = items.length; i < n; i++) {
6707                                ko.utils.arrayForEach(items[i].mappedNodes, function (node) {
6708                                    callback(node, i, items[i].arrayEntry);
6709                                });
6710                            }
6711                        }
6712                    }
6713
6714                    if (isFirstExecution) {
6715                        ko.utils.arrayForEach(array, itemAdded);
6716                    } else {
6717                        if (!editScript || (lastMappingResult && lastMappingResult['_countWaitingForRemove'])) {
6718                            // Compare the provided array against the previous one
6719                            var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; }),
6720                                compareOptions = {
6721                                    'dontLimitMoves': options['dontLimitMoves'],
6722                                    'sparse': true
6723                                };
6724                            editScript = ko.utils.compareArrays(lastArray, array, compareOptions);
6725                        }
6726
6727                        for (var i = 0, editScriptItem, movedIndex, itemIndex; editScriptItem = editScript[i]; i++) {
6728                            movedIndex = editScriptItem['moved'];
6729                            itemIndex = editScriptItem['index'];
6730                            switch (editScriptItem['status']) {
6731                                case "deleted":
6732                                    while (lastMappingResultIndex < itemIndex) {
6733                                        itemMovedOrRetained(lastMappingResultIndex++);
6734                                    }
6735                                    if (movedIndex === undefined) {
6736                                        mapData = lastMappingResult[lastMappingResultIndex];
6737
6738                                        // Stop tracking changes to the mapping for these nodes
6739                                        if (mapData.dependentObservable) {
6740                                            mapData.dependentObservable.dispose();
6741                                            mapData.dependentObservable = undefined;
6742                                        }
6743
6744                                        // Queue these nodes for later removal
6745                                        if (ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode).length) {
6746                                            if (options['beforeRemove']) {
6747                                                newMappingResult.push(mapData);
6748                                                countWaitingForRemove++;
6749                                                if (mapData.arrayEntry === deletedItemDummyValue) {
6750                                                    mapData = null;
6751                                                } else {
6752                                                    itemsForBeforeRemoveCallbacks.push(mapData);
6753                                                }
6754                                            }
6755                                            if (mapData) {
6756                                                nodesToDelete.push.apply(nodesToDelete, mapData.mappedNodes);
6757                                            }
6758                                        }
6759                                    }
6760                                    lastMappingResultIndex++;
6761                                    break;
6762
6763                                case "added":
6764                                    while (currentArrayIndex < itemIndex) {
6765                                        itemMovedOrRetained(lastMappingResultIndex++);
6766                                    }
6767                                    if (movedIndex !== undefined) {
6768                                        itemsToMoveFirstIndexes.push(newMappingResult.length);
6769                                        itemMovedOrRetained(movedIndex);
6770                                    } else {
6771                                        itemAdded(editScriptItem['value']);
6772                                    }
6773                                    break;
6774                            }
6775                        }
6776
6777                        while (currentArrayIndex < array.length) {
6778                            itemMovedOrRetained(lastMappingResultIndex++);
6779                        }
6780
6781                        // Record that the current view may still contain deleted items
6782                        // because it means we won't be able to use a provided editScript.
6783                        newMappingResult['_countWaitingForRemove'] = countWaitingForRemove;
6784                    }
6785
6786                    // Store a copy of the array items we just considered so we can difference it next time
6787                    ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
6788
6789                    // Call beforeMove first before any changes have been made to the DOM
6790                    callCallback(options['beforeMove'], itemsForMoveCallbacks);
6791
6792                    // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
6793                    ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
6794
6795                    var i, j, lastNode, nodeToInsert, mappedNodes, activeElement;
6796
6797                    // Since most browsers remove the focus from an element when it's moved to another location,
6798                    // save the focused element and try to restore it later.
6799                    try {
6800                        activeElement = domNode.ownerDocument.activeElement;
6801                    } catch (e) {
6802                        // IE9 throws if you access activeElement during page load (see issue #703)
6803                    }
6804
6805                    // Try to reduce overall moved nodes by first moving the ones that were marked as moved by the edit script
6806                    if (itemsToMoveFirstIndexes.length) {
6807                        while ((i = itemsToMoveFirstIndexes.shift()) != undefined) {
6808                            mapData = newMappingResult[i];
6809                            for (lastNode = undefined; i;) {
6810                                if ((mappedNodes = newMappingResult[--i].mappedNodes) && mappedNodes.length) {
6811                                    lastNode = mappedNodes[mappedNodes.length - 1];
6812                                    break;
6813                                }
6814                            }
6815                            for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {
6816                                ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);
6817                            }
6818                        }
6819                    }
6820
6821                    // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
6822                    for (i = 0; mapData = newMappingResult[i]; i++) {
6823                        // Get nodes for newly added items
6824                        if (!mapData.mappedNodes)
6825                            ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
6826
6827                        // Put nodes in the right place if they aren't there already
6828                        for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {
6829                            ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);
6830                        }
6831
6832                        // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
6833                        if (!mapData.initialized && callbackAfterAddingNodes) {
6834                            callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
6835                            mapData.initialized = true;
6836                            lastNode = mapData.mappedNodes[mapData.mappedNodes.length - 1];     // get the last node again since it may have been changed by a preprocessor
6837                        }
6838                    }
6839
6840                    // Restore the focused element if it had lost focus
6841                    if (activeElement && domNode.ownerDocument.activeElement != activeElement) {
6842                        activeElement.focus();
6843                    }
6844
6845                    // If there's a beforeRemove callback, call it after reordering.
6846                    // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
6847                    // some sort of animation, which is why we first reorder the nodes that will be removed. If the
6848                    // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
6849                    // Perhaps we'll make that change in the future if this scenario becomes more common.
6850                    callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
6851
6852                    // Replace the stored values of deleted items with a dummy value. This provides two benefits: it marks this item
6853                    // as already "removed" so we won't call beforeRemove for it again, and it ensures that the item won't match up
6854                    // with an actual item in the array and appear as "retained" or "moved".
6855                    for (i = 0; i < itemsForBeforeRemoveCallbacks.length; ++i) {
6856                        itemsForBeforeRemoveCallbacks[i].arrayEntry = deletedItemDummyValue;
6857                    }
6858
6859                    // Finally call afterMove and afterAdd callbacks
6860                    callCallback(options['afterMove'], itemsForMoveCallbacks);
6861                    callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
6862                }
6863            })();
6864
6865            ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
6866            ko.nativeTemplateEngine = function () {
6867                this['allowTemplateRewriting'] = false;
6868            }
6869
6870            ko.nativeTemplateEngine.prototype = new ko.templateEngine();
6871            ko.nativeTemplateEngine.prototype.constructor = ko.nativeTemplateEngine;
6872            ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
6873                var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
6874                    templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
6875                    templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
6876
6877                if (templateNodes) {
6878                    return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
6879                } else {
6880                    var templateText = templateSource['text']();
6881                    return ko.utils.parseHtmlFragment(templateText, templateDocument);
6882                }
6883            };
6884
6885            ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
6886            ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
6887
6888            ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
6889            (function () {
6890                ko.jqueryTmplTemplateEngine = function () {
6891                    // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
6892                    // doesn't expose a version number, so we have to infer it.
6893                    // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
6894                    // which KO internally refers to as version "2", so older versions are no longer detected.
6895                    var jQueryTmplVersion = this.jQueryTmplVersion = (function () {
6896                        if (!jQueryInstance || !(jQueryInstance['tmpl']))
6897                            return 0;
6898                        // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
6899                        try {
6900                            if (jQueryInstance['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
6901                                // Since 1.0.0pre, custom tags should append markup to an array called "__"
6902                                return 2; // Final version of jquery.tmpl
6903                            }
6904                        } catch (ex) { /* Apparently not the version we were looking for */ }
6905
6906                        return 1; // Any older version that we don't support
6907                    })();
6908
6909                    function ensureHasReferencedJQueryTemplates() {
6910                        if (jQueryTmplVersion < 2)
6911                            throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
6912                    }
6913
6914                    function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
6915                        return jQueryInstance['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
6916                    }
6917
6918                    this['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
6919                        templateDocument = templateDocument || document;
6920                        options = options || {};
6921                        ensureHasReferencedJQueryTemplates();
6922
6923                        // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
6924                        var precompiled = templateSource['data']('precompiled');
6925                        if (!precompiled) {
6926                            var templateText = templateSource['text']() || "";
6927                            // Wrap in "with($whatever.koBindingContext) { ... }"
6928                            templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
6929
6930                            precompiled = jQueryInstance['template'](null, templateText);
6931                            templateSource['data']('precompiled', precompiled);
6932                        }
6933
6934                        var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
6935                        var jQueryTemplateOptions = jQueryInstance['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
6936
6937                        var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
6938                        resultNodes['appendTo'](templateDocument.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
6939
6940                        jQueryInstance['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
6941                        return resultNodes;
6942                    };
6943
6944                    this['createJavaScriptEvaluatorBlock'] = function (script) {
6945                        return "{{ko_code ((function() { return " + script + " })()) }}";
6946                    };
6947
6948                    this['addTemplate'] = function (templateName, templateMarkup) {
6949                        document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
6950                    };
6951
6952                    if (jQueryTmplVersion > 0) {
6953                        jQueryInstance['tmpl']['tag']['ko_code'] = {
6954                            open: "__.push($1 || '');"
6955                        };
6956                        jQueryInstance['tmpl']['tag']['ko_with'] = {
6957                            open: "with($1) {",
6958                            close: "} "
6959                        };
6960                    }
6961                };
6962
6963                ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
6964                ko.jqueryTmplTemplateEngine.prototype.constructor = ko.jqueryTmplTemplateEngine;
6965
6966                // Use this one by default *only if jquery.tmpl is referenced*
6967                var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
6968                if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
6969                    ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
6970
6971                ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
6972            })();
6973        }));
6974    }());
6975})();
6976(function () { function n(n) { var u, i, r, f, t; if (typeof resources == "undefined") throw "ko.localationbinding.getLocalizedText: resources object is not defined"; if (typeof locale == "undefined") throw "ko.localationbinding.getLocalizedText: locale object is not defined"; if (Object.prototype.toString.call(n) === "[object String]" && (n = { key: n }), u = n.key, i = resources[u], !i) throw "ko.localationbinding.getLocalizedText: Could not find key '" + u + "'."; if (r = i[locale] || i["default"] || "", n.params) for (f in n.params) t = n.params[f], typeof t == "function" && (t = ko.utils.unwrapObservable(t())), r = r.replace("#" + f, t); return r } ko.bindingHandlers.restext = { update: function (t, i, r, u, f) { var e = n(ko.utils.unwrapObservable(i())); ko.bindingHandlers.text.update(t, function () { return e }, r, u, f) } }; ko.bindingHandlers.reshtml = { update: function (t, i, r, u, f) { var e = n(ko.utils.unwrapObservable(i())); ko.bindingHandlers.html.update(t, function () { return e }, r, u, f) } }; ko.bindingHandlers.reshref = { update: function (t, i, r, u, f) { var e = n(ko.utils.unwrapObservable(i())); ko.bindingHandlers.attr.update(t, function () { return { href: e } }, r, u, f) } }; ko.bindingHandlers.ressrc = { update: function (t, i, r, u, f) { var e = n(ko.utils.unwrapObservable(i())); ko.bindingHandlers.attr.update(t, function () { return { src: e } }, r, u, f) } }; ko.bindingHandlers.resattr = { update: function (t, i, r, u, f) { var o = ko.utils.unwrapObservable(i()), e, s; for (e in o) s = n(o[e]), ko.bindingHandlers.attr.update(t, function () { var n = {}; return n[e] = s, n }, r, u, f) } }; ko.bindingHandlers.restext.getText = n })();
6977var nith, locale, parldataWidgetUrl, membersHomeUrl, prevDescription, isMeetingInProgress;
6978
6979var Hoc = {
6980
6981    GetParameterByName: function (name) {
6982        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
6983        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
6984            results = regex.exec(location.search);
6985        return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
6986
6987    },
6988
6989    //Update Intervention ViewModel
6990    //clear Interventions array
6991    UpdateIntervention: function (e) {
6992
6993        nith.resetInterventions();
6994        $(".interventionsList").empty();
6995        if (e.Interventions) {
6996            for (var i = 0, len = e.Interventions.length; i < len; i++) {
6997                var memberPhotoPath, personTitleEn, personTitleFr;
6998
6999                if (e.Members !== null && e.Members[i] !== null) {
7000
7001                    switch (e.Members[i].AffRolId) {
7002
7003                        case 15: //Speaker of the House                                   
7004                            memberPhotoPath = e.Interventions[i].Photo.replace('.jpg', '_alt.jpg');
7005                            if (e.Interventions[i].PersonGender === 'F') {
7006                                personTitleEn = resources.the_speakerFe[resources.short_language.en];
7007                                personTitleFr = resources.the_speakerFe[resources.short_language.fr];
7008                            } else if (e.Interventions[i].PersonGender === 'M') {
7009                                personTitleEn = resources.the_speaker[resources.short_language.en];
7010                                personTitleFr = resources.the_speaker[resources.short_language.fr];
7011                            }
7012                            else {
7013                                personTitleEn = resources.the_speakerOther[resources.short_language.en];
7014                                personTitleFr = resources.the_speakerOther[resources.short_language.fr];
7015                            }
7016                            break;
7017                        case 22: //Deputy Speaker and Chair of the Committees of the Whole
7018                            memberPhotoPath = e.Interventions[i].Photo.replace('.jpg', '_alt.jpg');
7019                            if (e.Interventions[i].PersonGender === 'F') {
7020                                personTitleEn = resources.deputy_speakerFe[resources.short_language.en];
7021                                personTitleFr = resources.deputy_speakerFe[resources.short_language.fr];
7022                            } else if (e.Interventions[i].PersonGender === 'M') {
7023                                personTitleEn = resources.deputy_speaker[resources.short_language.en];
7024                                personTitleFr = resources.deputy_speaker[resources.short_language.fr];
7025                            } else {
7026                                personTitleEn = resources.deputy_speakerOther[resources.short_language.en];
7027                                personTitleFr = resources.deputy_speakerOther[resources.short_language.fr];
7028                            }
7029                            break;
7030                        case 92: //Deputy Chair Of Committees of the Whole
7031                            memberPhotoPath = e.Interventions[i].Photo.replace('.jpg', '_alt.jpg');
7032                            if (e.Interventions[i].PersonGender === 'F') {
7033                                personTitleEn = resources.deputy_chairFe[resources.short_language.en];
7034                                personTitleFr = resources.deputy_chairFe[resources.short_language.fr];
7035                            } else if (e.Interventions[i].PersonGender === 'M') {
7036                                personTitleEn = resources.deputy_chair[resources.short_language.en];
7037                                personTitleFr = resources.deputy_chair[resources.short_language.fr];
7038                            } else {
7039                                personTitleEn = resources.deputy_chairOther[resources.short_language.en];
7040                                personTitleFr = resources.deputy_chairOther[resources.short_language.fr];
7041                            }
7042                            break;
7043                        case 93: //Assistant Deputy Chair Of Committees of the Whole
7044                            memberPhotoPath = e.Interventions[i].Photo.replace('.jpg', '_alt.jpg');
7045                            if (e.Interventions[i].PersonGender === 'F') {
7046                                personTitleEn = resources.assistant_deputy_chairFe[resources.short_language.en];
7047                                personTitleFr = resources.assistant_deputy_chairFe[resources.short_language.fr];
7048                            } else if (e.Interventions[i].PersonGender === 'M') {
7049                                personTitleEn = resources.assistant_deputy_chair[resources.short_language.en];
7050                                personTitleFr = resources.assistant_deputy_chair[resources.short_language.fr];
7051                            } else {
7052                                personTitleEn = resources.assistant_deputy_chairOther[resources.short_language.en];
7053                                personTitleFr = resources.assistant_deputy_chairOther[resources.short_language.fr];
7054                            }
7055                            break;
7056                        default:
7057                            if (e.Interventions[i].Photo !== null) {
7058                                memberPhotoPath = e.Interventions[i].Photo;
7059                            }
7060                            else {
7061                                if (e.Interventions[i].PersonGender === 'F') {
7062                                    memberPhotoPath = resources.silhouette.f;
7063                                } else {
7064                                    memberPhotoPath = resources.silhouette.m;
7065                                }
7066                            }
7067                            personTitleEn = e.Interventions[i].PersonLongHonorificEn;
7068                            personTitleFr = e.Interventions[i].PersonLongHonorificFr;
7069
7070                    }
7071                }
7072
7073                nith.addInterventions(
7074                    e.Interventions[i].Name,
7075                    e.Interventions[i].PartiShortEn,
7076                    e.Interventions[i].PartiShortFr,
7077                    e.Interventions[i].CaucusWebsiteColor,
7078                    personTitleEn,
7079                    personTitleFr,
7080                    e.Interventions[i].ProvinceEn,
7081                    e.Interventions[i].ProvinceFr,
7082                    e.Interventions[i].RegionEn,
7083                    e.Interventions[i].RegionFr,
7084                    memberPhotoPath,
7085                    e.Interventions[i].PersonGender,
7086                    e.Interventions[i].CurrentPersonId);
7087            }
7088        }
7089
7090    },
7091
7092    //Initialize SignalR connection
7093    InitSignalR: function () {
7094        var prevNithId = 0;
7095        var debug = Hoc.GetParameterByName('debug');
7096        var disablejs = Hoc.GetParameterByName('disablejs');
7097        //Without generated proxy
7098        var url = $('#hoc-real-time-service').data('url'); //TO DO: set var path server side
7099        var connection = $.hubConnection(url);
7100
7101        if (debug === 'true') {
7102            connection.logging = true;
7103        }
7104
7105        //Initialize hub
7106        var parliamentNitHHub = connection.createHubProxy('RealTimeEventHub');
7107
7108        // register a client method on hub to be invoked by the server
7109        parliamentNitHHub.on('updateNowInTheHouse', function (eventMsg) {
7110            // Debugger: show signalR Events in browser console when enabled
7111            if (debug === 'true') {
7112                console.info("RealTimeEventHub Event received:");
7113                console.info(eventMsg);
7114            }
7115            if (disablejs === 'true') {
7116                //do nothing, 
7117                //emulate javascript disabled
7118                //do not update view from signalR
7119            } else {
7120                //Do not update view from signalR if isMeetingInProgress flag (from Parldata) is false or if signalR EventType is not an adjournement (id:340)
7121                if (prevNithId <= eventMsg.Id && (isMeetingInProgress || eventMsg.EventType !== '340')) {
7122                    prevNithId = eventMsg.Id;
7123                    Hoc.UpdateNithModel(eventMsg);
7124                }
7125                else {
7126                    //If adjournemt event comes from signalR, update view, previNithId will not be 0
7127                    if (eventMsg.EventType === '340' && prevNithId > 0) {
7128                        Hoc.UpdateNithModel(eventMsg);
7129                    }
7130                    if (debug === 'true') {
7131                        if (prevNithId > eventMsg.Id) {
7132                            console.info("*** Incorrect sequence detected, nith.Id(" + eventMsg.Id + ") has been ignored, previous Id was " + prevNithId);
7133                        }
7134                        if (prevNithId !== 0) {
7135                            console.info('Adjournement from signalR');
7136                        }
7137                        if (!isMeetingInProgress) {
7138                            console.info("Meeting is not in Progress, signalR updates disabled (source Parldata)");
7139                        }
7140                    }
7141                }
7142            }
7143        });
7144
7145        // Start the connection with the server
7146        connection.start({ transport: ['webSockets', 'longPolling'], jsonp: true })
7147            .done(function (data) {
7148                if (debug === 'true') {
7149                    console.info('RealTimeEventHub connected ' + data.transport.name + ', connection ID= ' + data.id);
7150                }
7151            }).fail(function (error) {
7152                if (debug === 'true') {
7153                    console.info('Error: ' + error);
7154                }
7155            });
7156
7157        // SignalR Timeout set by default at 30 seconds
7158        // SginalR KeepAlive set at 10 seconds
7159        // www.asp.net/signalr/overview/guide-to-the-api/handling-connection-lifetime-events#timeoutkeepalive
7160        //Raised when the connection has disconnected, try to restart connection every 5 seconds
7161
7162        connection.disconnected(function () {
7163
7164            $('.connectionStatus').html("<span class='connectionStatusMessage'>" + "<p class='connectionStatusText-slow'>" + resources.error_signalR_connection[locale] + '</p>' + '</span>');
7165            setTimeout(function () {
7166                console.info('Trying to reconnect...');
7167                connection.start({ transport: ['webSockets', 'longPolling'], jsonp: true }).done(function () {
7168                    console.info('Reconnect succeeded...');
7169                    $('.connectionStatus').html("");
7170                }).fail(function (error) {
7171                    console.info('Error: ' + error);
7172                });
7173            }, 5000);
7174        });
7175
7176        //SignalR Slow: raised when the client detects a slow or frequently dropping connection.
7177        connection.connectionSlow(function () {
7178            $('.connectionStatus').html("<span class='connectionStatusMessage-slow'>" + "<p class='connectionStatusText-slow'>" + resources.error_signalR_slow[locale] + '</p>' + '</span>');
7179        });
7180        //On reconnect, remove connection status message
7181        connection.reconnected(function () {
7182            $(".connectionStatus").html();
7183        });
7184
7185    },
7186
7187    // Helper to find data in a json object by key and value.
7188    GetObjects: function (obj, key, val) {
7189
7190        var objects = [];
7191        for (var i in obj) {
7192            if (!obj.hasOwnProperty(i))
7193                continue;
7194            if (typeof obj[i] === 'object') {
7195                objects = objects.concat(this.GetObjects(obj[i], key, val));
7196            } else if (i === key && obj[key] === val) {
7197                objects.push(obj);
7198            }
7199        }
7200        return objects;
7201    },
7202
7203    // Generate tooltip Icon with desciption
7204    GetDescription: function (description, obj, lang) {
7205        var html = '<i class="hoc-icons  icon-info-circle" data-container="body" data-toggle="tooltip" data-html="true" title="{{title}}" role="tooltip"><span class="visually-hidden">{{title}}</span></i>';
7206        var tooltip;
7207        if (obj.length > 0) {
7208            html = html.replace("{{title}}", obj[0][lang]);
7209            tooltip = description + '&nbsp;' + html;
7210            return tooltip;
7211        } else {
7212            return description;
7213        }
7214    },
7215
7216    // Find tool tip data if defined in resources.Tooltips array
7217    FindTooltipData: function (description) {
7218
7219        return Hoc.GetObjects(resources.Tooltips, 'titleEn', description);
7220
7221    },
7222
7223    //Updates NowInTheHouse View Model
7224    UpdateNithModel: function (e) {
7225
7226        Hoc.UpdateIntervention(e);
7227        Hoc.UpdateMember(e);
7228        Hoc.UpdateDecision(e);
7229        Hoc.UpdateItemOfBusiness(e);
7230        Hoc.UpdateExplanatoryContext(e);
7231        Hoc.UpdateSubsequentDivisions(e);
7232
7233        //Set Order Of Business
7234
7235        // if Order of Business is different from previous one - clear tooltip to prevent the popup to be stuck
7236        if (e.DescriptionEn !== prevDescription) {
7237            $('[data-toggle="tooltip"]').tooltip('hide');
7238            prevDescription = e.DescriptionEn;
7239        }
7240        //Set ParlVU links
7241        nith.parlVuDesktopUrlEn(e.ParlVuDesktopUrlEn);
7242        nith.parlVuDesktopUrlFr(e.ParlVuDesktopUrlFr);
7243        // find Tool tip data
7244        var toolTipObj = Hoc.FindTooltipData(e.DescriptionEn);
7245
7246        // inject tool tip in DOM
7247        nith.descriptionEn(Hoc.GetDescription(e.DescriptionEn, toolTipObj, "en"));
7248        nith.descriptionFr(Hoc.GetDescription(e.DescriptionFr, toolTipObj, "fr"));
7249
7250        //Set EventType
7251        nith.eventType(e.EventType);
7252
7253        //Bells Are Ringing
7254        if (e.BellsAreRinging) {
7255            Hoc.InitBells(e.BellCountdown, e.BellReasonEn, e.BellReasonFr, e.BellStartDateTime);
7256            if (e.BellReasonEn !== "Other") {
7257                nith.bellReasonEn(e.BellReasonEn);
7258                nith.bellReasonFr(e.BellReasonFr);
7259                $(".clock").show();
7260                $("#clock").show();
7261            } else {
7262                nith.bellReasonEn("");
7263                nith.bellReasonFr("");
7264                $(".clock").show();
7265                $("#clock").hide();
7266            }
7267        } else {
7268            $(".clock").hide();
7269        }
7270
7271        // House Status Message
7272        // If Adjourned hide ParlVu button and remove Description
7273
7274        if (e.EventType === 340) {
7275            nith.houseStatusMessage(resources.house_is_adjourned[locale]);
7276            $('.in-the-house-watchlive-server-side').remove();
7277            $('.in-the-house-watchlive-client-side').hide();
7278            $('#now-in-the-housedescription').html('');
7279        } else {
7280            nith.houseStatusMessage(resources.house_is_sitting[locale]);
7281            $('.in-the-house-watchlive-server-side').remove();
7282            $('.in-the-house-watchlive-client-side').show();
7283        }
7284
7285        // Vote in progress message
7286        // Show Live vote message and button link when vote in progress
7287        // ===============================================================================================
7288        // ======= Do to many EventTypeId, we had to use the text compare ================================
7289        // ======= We don't know for how much time the feature will live, for live Vote - PT 52957 =======
7290        // ===============================================================================================
7291        if (e.DescriptionEn === 'Votes' || e.DescriptionFr === 'Votes') {
7292            $('.live-vote-message').show();
7293        } else {
7294            $('.live-vote-message').hide();
7295        }
7296
7297        //Silence Event
7298        nith.silenceReasonEn(e.SilenceReasonEn);
7299        nith.silenceReasonFr(e.SilenceReasonFr);
7300
7301        //If Event Capture flag is on, show message
7302        if (e.IsOffline === true) {
7303            //$('.connectionStatus').html("<span class='connectionStatusMessage ion-ios-close-outline-custom'>" + "<div class='connectionStatusBar'> </div>" + "<p class='connectionStatusText'>" + resources.error_event_capture_offline[locale] + '</p>' + '</span>');
7304            console.info(resources.error_event_capture_offline[locale] + " Source Event Capture");
7305        }
7306        else {
7307            $('.connectionStatus').html("");
7308        }
7309
7310        if (!(e.EventType === 340 || e.EventType === 337)) {
7311            $('.sync-view').remove();
7312            $('.async-view').show();
7313        }
7314
7315        Hoc.InTheHouseContent();
7316        $('[data-toggle="tooltip"]').tooltip({ delay: { "show": 300, "hide": 50 }, placement: "top" });
7317
7318    },
7319    //Update Members ViewModel
7320    //Clear Members array
7321    UpdateMember: function (e) {
7322
7323        nith.resetMembers();
7324        for (var j = 0, length = e.Members.length; j < length; j++) {
7325            nith.addMembers(
7326                e.Members[j].AffId,
7327                e.Members[j].AffRolId,
7328                e.Members[j].ConId);
7329        }
7330    },
7331    //Update Decisions ViewModel
7332    //Clear Decision array
7333    UpdateDecision: function (e) {
7334
7335        nith.resetDecisions();
7336
7337        if (e.Decision) {
7338            nith.addDecision(
7339                e.Decision.BillId,
7340                e.Decision.DecisionDescriptionEn,
7341                e.Decision.DecisionDescriptionFr,
7342                e.Decision.DecisionIdentifierEn,
7343                e.Decision.DecisionIdentifierFr,
7344                e.Decision.DecisionQualifierEn,
7345                e.Decision.DecisionQualifierFr,
7346                e.Decision.SectionTitleEn,
7347                e.Decision.SectionTitleFr,
7348                e.Decision.VoteNays,
7349                e.Decision.VoteResultEn,
7350                e.Decision.VoteResultFr,
7351                e.Decision.VoteYeas,
7352                e.Decision.SpeakerCastingVoteEn,
7353                e.Decision.SpeakerCastingVoteFr
7354            );
7355        } else {
7356            //When there are no decisions, create the orbject key with no value
7357            nith.addDecision("", "", "", "", "", "", "", "", "", "", "", "", "");
7358        }
7359
7360    },
7361    //Update Item of Business ViewModel
7362    UpdateItemOfBusiness: function (e) {
7363
7364        nith.resetItemOfBusiness();
7365        nith.addItemOfBusiness(
7366            e.IoB.BillId,
7367            e.IoB.ItemDescriptionEn,
7368            e.IoB.ItemDescriptionFr,
7369            e.IoB.ItemId,
7370            e.IoB.ItemIdentifierEn,
7371            e.IoB.ItemIdentifierFr,
7372            e.IoB.ItemQualifierEn,
7373            e.IoB.ItemQualifierFr,
7374            e.IoB.MotionId
7375        );
7376
7377    },
7378    //Update Explanatory Context ViewModel
7379    UpdateExplanatoryContext: function (e) {
7380
7381        nith.resetExplanatoryContext();
7382        nith.addExplanatoryContext(
7383            e.ExplanatoryContext.ContextDescriptionEn,
7384            e.ExplanatoryContext.ContextDescriptionFr,
7385            e.ExplanatoryContext.ContextTitleEn,
7386            e.ExplanatoryContext.ContextTitleFr
7387        );
7388
7389    },
7390    //Update SubsequentDivsions ViewModel
7391    UpdateSubsequentDivisions: function (e) {
7392
7393        nith.resetSubsequentDivisions();
7394        if (e.SubsequentDivisions) {
7395            for (var k = 0, l = e.SubsequentDivisions.length; k < l; k++) {
7396                nith.addSubsequentDivisions(
7397                    e.SubsequentDivisions[k].BillId,
7398                    e.SubsequentDivisions[k].ItemDescriptionEn,
7399                    e.SubsequentDivisions[k].ItemDescriptionFr,
7400                    e.SubsequentDivisions[k].ItemId,
7401                    e.SubsequentDivisions[k].ItemIdentifierEn,
7402                    e.SubsequentDivisions[k].ItemIdentifierFr,
7403                    e.SubsequentDivisions[k].ItemQualifierEn,
7404                    e.SubsequentDivisions[k].ItemQualifierFr,
7405                    e.SubsequentDivisions[k].MotionId
7406                );
7407            }
7408        } else {
7409            //When there are no subsequentDivisions, create the orbject key with no value
7410            nith.addSubsequentDivisions("", "", "", "", "", "", "", "", "");
7411        }
7412
7413    },
7414    // Handle Show More/Show Less logic depending on content height
7415    InTheHouseContent: function () {
7416
7417        var inTheHouseContentHeight = 327;
7418
7419        if ($("#in-the-house-content").hasClass("in")) {
7420            //do nothing
7421        } else if ($(window).width() > 767) {
7422
7423            var inTheHouseHeaderHeight = $('#in-the-house-header').outerHeight(true);
7424            var widgetHeight = parseInt($('.tabbed-widget-content-wrapper').css('min-height'), 10);
7425            var inTheHouseFooterHeight = $('#in-the-house-footer').outerHeight();
7426
7427            inTheHouseContentHeight = widgetHeight - inTheHouseHeaderHeight - inTheHouseFooterHeight;
7428
7429            $('#in-the-house-content').css('min-height', inTheHouseContentHeight);
7430            $('#in-the-house-content').height(inTheHouseContentHeight);
7431        }
7432
7433        if ($("#in-the-house-content-list").height() > inTheHouseContentHeight) {
7434            $("#in-the-house-show-more").removeClass("invisible");
7435        } else {
7436            $("#in-the-house-show-more").addClass("invisible");
7437        }
7438    },
7439    // Init Bells Are Rining countdown timer
7440    InitBells: function (BellCountdown, BellReasonEn, BellReasonFr, BellStartDateTime) {
7441
7442        console.info('initilizing Bells');
7443        var dateToCountdownTo = moment(BellStartDateTime);
7444        var bellCountdown = parseInt(BellCountdown);
7445        dateToCountdownTo.add(bellCountdown, 'm');
7446
7447        //start countdown
7448        $('#clock').countdown(dateToCountdownTo.format('YYYY/MM/DD HH:mm:ss'), function (event) {
7449            var totalHours = event.offset.totalDays * 24 + event.offset.hours;
7450            $(this).html(event.strftime(totalHours + '%-H:%M:%S'));
7451        });
7452
7453    },
7454
7455    NowInTheHouseWidget: $(function () {
7456
7457        // set local variables
7458        locale = document.getElementById('culture').value;
7459        parldataWidgetUrl = document.getElementById('parldataWidgets-url').value;
7460        membersHomeUrl = document.getElementById('membersHome-url').value;
7461        isMeetingInProgress = document.getElementById('isMeetingInProgress').value === 'True';
7462
7463        // init DecisionViewModel and apply knockout bindings
7464        nith = new NowInTheHouseViewModel();
7465        ko.applyBindings(nith);
7466
7467        Hoc.InitSignalR();
7468
7469
7470        // Call InTheHouseContent on resize
7471        $(window).resize(function () {
7472            Hoc.InTheHouseContent();
7473        });
7474        // TODO change the data-toggle below.
7475        $('#tabbed-widget-in-the-house-tab[data-toggle="tab"]').on('shown.bs.tab', function () {
7476            Hoc.InTheHouseContent();
7477        });
7478
7479        $('#in-the-house-show-more-less-link').click(function () {
7480            if ($("#in-the-house-content").hasClass("in")) {
7481                $("#in-the-house-show-more-less-link").html(resources.show_more[locale] + "&nbsp;<i class='hoc-icons  icon-down'></i>");
7482            }
7483            else {
7484                $("#in-the-house-show-more-less-link").html(resources.show_less[locale] + "&nbsp;<i class='hoc-icons  icon-up'></i>");
7485            }
7486        });
7487
7488    })
7489};
7490
7491
7492var NowInTheHouseViewModel = function () {
7493    var self = this;
7494    self.eventId = ko.observable(0);
7495    self.eventType = ko.observable("");
7496    self.descriptionEn = ko.observable("");
7497    self.descriptionFr = ko.observable("");
7498    self.bellsAreRinging = ko.observable(false);
7499    self.bellCountdown = ko.observable("");
7500    self.bellReasonEn = ko.observable("");
7501    self.bellReasonFr = ko.observable("");
7502    self.isSilenced = ko.observable("");
7503    self.silenceReasonEn = ko.observable("");
7504    self.silenceReasonFr = ko.observable("");
7505    self.bellStartDateTime = ko.observable("");
7506    self.members = ko.observable("");
7507    self.meetingId = ko.observable("");
7508    self.isOffline = ko.observable("");
7509    self.eventId = ko.observable("");
7510    self.parlVuDesktopUrlEn = ko.observable("");
7511    self.parlVuDesktopUrlFr = ko.observable("");
7512    self.parlVuMobileUrlVideoEn = ko.observable("");
7513    self.parlVuMobileUrlVideoFr = ko.observable("");
7514    self.parlVuMobileUrlAudioEn = ko.observable("");
7515    self.parlVuMobileUrlAudioFr = ko.observable("");
7516    self.houseStatusMessage = ko.observable("");
7517
7518    //Decision observable
7519    self.decisions = ko.observableArray([]);
7520    self.addDecision = function (billId, decisionDescriptionEn, decisionDescriptionFr, decisionIdentifierEn, decisionIdentifierFr, decisionQualifierEn, decisionQualifierFr, sectionTitleEn, sectionTitleFr, voteNays, voteResultEn, voteResultFr, voteYeas, speakerCastingVoteEn, speakerCastingVoteFr) {
7521        self.decisions.push(
7522            {
7523                billId: billId,
7524                decisionDescriptionEn: decisionDescriptionEn,
7525                decisionDescriptionFr: decisionDescriptionFr,
7526                decisionIdentifierEn: decisionIdentifierEn,
7527                decisionIdentifierFr: decisionIdentifierFr,
7528                decisionQualifierEn: decisionQualifierEn ? "&nbsp;(" + decisionQualifierEn + ")" : "",
7529                decisionQualifierFr: decisionQualifierFr ? "&nbsp;(" + decisionQualifierFr + ")" : "",
7530                sectionTitleEn: sectionTitleEn,
7531                sectionTitleFr: sectionTitleFr,
7532                voteNays: voteNays,
7533                voteNaysEn: (voteYeas > 0 || voteNays > 0) ? " Nays: " + voteNays : "",
7534                voteNaysFr: (voteYeas > 0 || voteNays > 0) ? " Contre : " + voteNays : "",
7535                voteResultEn: voteResultEn + "&nbsp;&nbsp;",
7536                voteResultFr: voteResultFr + "&nbsp;&nbsp;",
7537                voteYeas: voteYeas,
7538                voteYeasEn: (voteYeas > 0 || voteNays > 0) ? "Yeas: " + voteYeas + "&nbsp;" : "",
7539                voteYeasFr: (voteYeas > 0 || voteNays > 0) ? "Pour : " + voteYeas + "&nbsp;" : "",
7540                speakerCastingVoteEn: speakerCastingVoteEn ? "Speaker's Casting Vote: " + speakerCastingVoteEn : "",
7541                speakerCastingVoteFr: speakerCastingVoteFr ? "Voix prépondérante du Président : " + speakerCastingVoteFr : ""
7542            });
7543    };
7544
7545    //interventions observable
7546    self.interventions = ko.observableArray([]);
7547    self.addInterventions = function (name, caucusEn, caucusFr, caucusWebsiteColor, personLongHonorificEn, personLongHonorificFr, provinceEn, provinceFr, regionEn, regionFr, photo, personGender, personId) {
7548        self.interventions.push(
7549            {
7550                name: name,
7551                caucusEn: caucusEn,
7552                caucusFr: caucusFr,
7553                caucusWebsiteColor: caucusWebsiteColor,
7554                personLongHonorificEn: personLongHonorificEn,
7555                personLongHonorificFr: personLongHonorificFr,
7556                provinceEn: provinceEn,
7557                provinceFr: provinceFr,
7558                regionEn: regionEn,
7559                regionFr: regionFr,
7560                photo: photo,
7561                personGender: ko.observable(personGender),
7562                urlEn: membersHomeUrl.replace("{0}", "en") + "/" + personId,
7563                urlFr: membersHomeUrl.replace("{0}", "fr") + "/" + personId
7564            });
7565    };
7566
7567    //members observable
7568    self.members = ko.observableArray([]);
7569    self.addMembers = function (affId, affRoleId, conId) {
7570        self.members.push(
7571            {
7572                affId: ko.observable(affId),
7573                affRoleId: ko.observable(affRoleId),
7574                conId: ko.observable(conId)
7575            });
7576    };
7577
7578    //itemOfBusiness observable
7579    self.itemOfBusiness = ko.observableArray([]);
7580    self.addItemOfBusiness = function (billId, itemDescriptionEn, itemDescriptionFr,itemId,itemIdentifierEn,itemIdentifierFr,itemQualifierEn,itemQualifierFr,motionId) {
7581
7582        self.itemOfBusiness.push(
7583        {
7584            billId: billId,
7585            itemDescriptionEn: itemDescriptionEn,
7586            itemDescriptionFr: itemDescriptionFr,
7587            itemId: itemId,
7588            itemIdentifierEn: itemIdentifierEn,
7589            itemIdentifierFr: itemIdentifierFr,
7590            itemQualifierEn: itemQualifierEn !== "" ? "&nbsp;(" + itemQualifierEn + ")" : "",
7591            itemQualifierFr: itemQualifierFr !== "" ? "&nbsp;(" + itemQualifierFr + ")" : "",
7592            motionId: motionId,
7593            urlEn: (billId !== "") ? parldataWidgetUrl + "en/bill/" + billId : ((motionId !== "") ? parldataWidgetUrl + "en/motion/" + motionId : ""),
7594            urlFr: (billId !== "") ? parldataWidgetUrl + "fr/bill/" + billId : ((motionId !== "") ? parldataWidgetUrl + "fr/motion/" + motionId : "")
7595        });
7596    };
7597
7598    //explanatoryContext observable
7599    self.explanatoryContext = ko.observableArray([]);
7600    self.addExplanatoryContext = function (contextDescriptionEn, c
vendor: 11,320 bytes, lines 7600-7884
7600ontextDescriptionFr, contextTitleEn, contextTitleFr) {
7601        self.explanatoryContext.push(
7602            {
7603                contextDescriptionEn: contextDescriptionEn,
7604                contextDescriptionFr: contextDescriptionFr,
7605                contextTitleEn: contextTitleEn,
7606                contextTitleFr: contextTitleFr
7607            });
7608    };
7609
7610    //subsequentDivisions observable
7611    self.subsequentDivisions = ko.observableArray([]);
7612    self.addSubsequentDivisions = function (billId, itemDescriptionEn, itemDescriptionFr, itemId, itemIdentifierEn, itemIdentifierFr, itemQualifierEn, itemQualifierFr, motionId) {
7613        self.subsequentDivisions.push(
7614            {
7615                titleEn: ko.observable("Votes"),
7616                titleFr: ko.observable("Votes"),
7617                billId: billId,
7618                itemDescriptionEn: itemDescriptionEn,
7619                itemDescriptionFr: itemDescriptionFr,
7620                itemId: itemId,
7621                itemIdentifierEn: itemIdentifierEn,
7622                itemIdentifierFr: itemIdentifierFr,
7623                itemQualifierEn: (itemQualifierEn !== "") ? "&nbsp;(" + itemQualifierEn + ")" : "",
7624                itemQualifierFr: (itemQualifierFr !== "") ? "&nbsp;(" + itemQualifierFr + ")" : "",
7625                motionId: motionId,
7626                urlEn: (billId !== "") ? parldataWidgetUrl + "en/bill/" + billId : parldataWidgetUrl + "en/motion/" + motionId,
7627                urlFr: (billId !== "") ? parldataWidgetUrl + "fr/bill/" + billId : parldataWidgetUrl + "fr/motion/" + motionId
7628            });
7629    };
7630
7631    self.resetInterventions = function () {
7632        self.interventions.removeAll();
7633    };
7634
7635    self.resetDecisions = function () {
7636        self.decisions.removeAll();
7637    };
7638
7639    self.resetItemOfBusiness = function () {
7640        self.itemOfBusiness.removeAll();
7641    };
7642
7643    self.resetExplanatoryContext = function () {
7644        self.explanatoryContext.removeAll();
7645    };
7646
7647    self.resetSubsequentDivisions = function () {
7648        self.subsequentDivisions.removeAll();
7649    };
7650
7651    self.resetMembers = function () {
7652        self.members.removeAll();
7653    };
7654};
7655
7656/*!
7657 * The Final Countdown for jQuery v2.2.0 (http://hilios.github.io/jQuery.countdown/)
7658 * Copyright (c) 2016 Edson Hilios
7659 * 
7660 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7661 * this software and associated documentation files (the "Software"), to deal in
7662 * the Software without restriction, including without limitation the rights to
7663 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7664 * the Software, and to permit persons to whom the Software is furnished to do so,
7665 * subject to the following conditions:
7666 * 
7667 * The above copyright notice and this permission notice shall be included in all
7668 * copies or substantial portions of the Software.
7669 * 
7670 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7671 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
7672 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
7673 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
7674 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7675 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7676 */
7677(function (factory) {
7678    "use strict";
7679    if (typeof define === "function" && define.amd) {
7680        define(["jquery"], factory);
7681    } else {
7682        factory(jQuery);
7683    }
7684})(function ($) {
7685    "use strict";
7686    var instances = [], matchers = [], defaultOptions = {
7687        precision: 100,
7688        elapse: false,
7689        defer: false
7690    };
7691    matchers.push(/^[0-9]*$/.source);
7692    matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
7693    matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
7694    matchers = new RegExp(matchers.join("|"));
7695    function parseDateString(dateString) {
7696        if (dateString instanceof Date) {
7697            return dateString;
7698        }
7699        if (String(dateString).match(matchers)) {
7700            if (String(dateString).match(/^[0-9]*$/)) {
7701                dateString = Number(dateString);
7702            }
7703            if (String(dateString).match(/\-/)) {
7704                dateString = String(dateString).replace(/\-/g, "/");
7705            }
7706            return new Date(dateString);
7707        } else {
7708            throw new Error("Couldn't cast `" + dateString + "` to a date object.");
7709        }
7710    }
7711    var DIRECTIVE_KEY_MAP = {
7712        Y: "years",
7713        m: "months",
7714        n: "daysToMonth",
7715        d: "daysToWeek",
7716        w: "weeks",
7717        W: "weeksToMonth",
7718        H: "hours",
7719        M: "minutes",
7720        S: "seconds",
7721        D: "totalDays",
7722        I: "totalHours",
7723        N: "totalMinutes",
7724        T: "totalSeconds"
7725    };
7726    function escapedRegExp(str) {
7727        var sanitize = str.toString().replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
7728        return new RegExp(sanitize);
7729    }
7730    function strftime(offsetObject) {
7731        return function (format) {
7732            var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
7733            if (directives) {
7734                for (var i = 0, len = directives.length; i < len; ++i) {
7735                    var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/), regexp = escapedRegExp(directive[0]), modifier = directive[1] || "", plural = directive[3] || "", value = null;
7736                    directive = directive[2];
7737                    if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
7738                        value = DIRECTIVE_KEY_MAP[directive];
7739                        value = Number(offsetObject[value]);
7740                    }
7741                    if (value !== null) {
7742                        if (modifier === "!") {
7743                            value = pluralize(plural, value);
7744                        }
7745                        if (modifier === "") {
7746                            if (value < 10) {
7747                                value = "0" + value.toString();
7748                            }
7749                        }
7750                        format = format.replace(regexp, value.toString());
7751                    }
7752                }
7753            }
7754            format = format.replace(/%%/, "%");
7755            return format;
7756        };
7757    }
7758    function pluralize(format, count) {
7759        var plural = "s", singular = "";
7760        if (format) {
7761            format = format.replace(/(:|;|\s)/gi, "").split(/\,/);
7762            if (format.length === 1) {
7763                plural = format[0];
7764            } else {
7765                singular = format[0];
7766                plural = format[1];
7767            }
7768        }
7769        if (Math.abs(count) > 1) {
7770            return plural;
7771        } else {
7772            return singular;
7773        }
7774    }
7775    var Countdown = function (el, finalDate, options) {
7776        this.el = el;
7777        this.$el = $(el);
7778        this.interval = null;
7779        this.offset = {};
7780        this.options = $.extend({}, defaultOptions);
7781        this.instanceNumber = instances.length;
7782        instances.push(this);
7783        this.$el.data("countdown-instance", this.instanceNumber);
7784        if (options) {
7785            if (typeof options === "function") {
7786                this.$el.on("update.countdown", options);
7787                this.$el.on("stoped.countdown", options);
7788                this.$el.on("finish.countdown", options);
7789            } else {
7790                this.options = $.extend({}, defaultOptions, options);
7791            }
7792        }
7793        this.setFinalDate(finalDate);
7794        if (this.options.defer === false) {
7795            this.start();
7796        }
7797    };
7798    $.extend(Countdown.prototype, {
7799        start: function () {
7800            if (this.interval !== null) {
7801                clearInterval(this.interval);
7802            }
7803            var self = this;
7804            this.update();
7805            this.interval = setInterval(function () {
7806                self.update.call(self);
7807            }, this.options.precision);
7808        },
7809        stop: function () {
7810            clearInterval(this.interval);
7811            this.interval = null;
7812            this.dispatchEvent("stoped");
7813        },
7814        toggle: function () {
7815            if (this.interval) {
7816                this.stop();
7817            } else {
7818                this.start();
7819            }
7820        },
7821        pause: function () {
7822            this.stop();
7823        },
7824        resume: function () {
7825            this.start();
7826        },
7827        remove: function () {
7828            this.stop.call(this);
7829            instances[this.instanceNumber] = null;
7830            delete this.$el.data().countdownInstance;
7831        },
7832        setFinalDate: function (value) {
7833            this.finalDate = parseDateString(value);
7834        },
7835        update: function () {
7836            if (this.$el.closest("html").length === 0) {
7837                this.remove();
7838                return;
7839            }
7840            var hasEventsAttached = $._data(this.el, "events") !== undefined, now = new Date(), newTotalSecsLeft;
7841            newTotalSecsLeft = this.finalDate.getTime() - now.getTime();
7842            newTotalSecsLeft = Math.ceil(newTotalSecsLeft / 1e3);
7843            newTotalSecsLeft = !this.options.elapse && newTotalSecsLeft < 0 ? 0 : Math.abs(newTotalSecsLeft);
7844            if (this.totalSecsLeft === newTotalSecsLeft || !hasEventsAttached) {
7845                return;
7846            } else {
7847                this.totalSecsLeft = newTotalSecsLeft;
7848            }
7849            this.elapsed = now >= this.finalDate;
7850            this.offset = {
7851                seconds: this.totalSecsLeft % 60,
7852                minutes: Math.floor(this.totalSecsLeft / 60) % 60,
7853                hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
7854                days: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
7855                daysToWeek: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
7856                daysToMonth: Math.floor(this.totalSecsLeft / 60 / 60 / 24 % 30.4368),
7857                weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
7858                weeksToMonth: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7) % 4,
7859                months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30.4368),
7860                years: Math.abs(this.finalDate.getFullYear() - now.getFullYear()),
7861                totalDays: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
7862                totalHours: Math.floor(this.totalSecsLeft / 60 / 60),
7863                totalMinutes: Math.floor(this.totalSecsLeft / 60),
7864                totalSeconds: this.totalSecsLeft
7865            };
7866            if (!this.options.elapse && this.totalSecsLeft === 0) {
7867                this.stop();
7868                this.dispatchEvent("finish");
7869            } else {
7870                this.dispatchEvent("update");
7871            }
7872        },
7873        dispatchEvent: function (eventName) {
7874            var event = $.Event(eventName + ".countdown");
7875            event.finalDate = this.finalDate;
7876            event.elapsed = this.elapsed;
7877            event.offset = $.extend({}, this.offset);
7878            event.strftime = strftime(this.offset);
7879            this.$el.trigger(event);
7880        }
7881    });
7882    $.fn.countdown = function () {
7883        var argumentsArray = Array.prototype.slice.call(arguments, 0);
7884        return this.each(function 
vendor: 819 bytes, lines 7884-7901
7884() {
7885            var instanceNumber = $(this).data("countdown-instance");
7886            if (instanceNumber !== undefined) {
7887                var instance = instances[instanceNumber], method = argumentsArray[0];
7888                if (Countdown.prototype.hasOwnProperty(method)) {
7889                    instance[method].apply(instance, argumentsArray.slice(1));
7890                } else if (String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
7891                    instance.setFinalDate.call(instance, method);
7892                    instance.start();
7893                } else {
7894                    $.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi, method));
7895                }
7896            } else {
7897                new Countdown(this, argumentsArray[0], argumentsArray[1]);
7898            }
7899        });
7900    };
7901});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.