epyce 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/Gemfile +4 -0
  2. data/LICENSE +3 -0
  3. data/README.md +59 -0
  4. data/Rakefile +1 -0
  5. data/VERSION +1 -0
  6. data/app/assets/stylesheets/html5S-libs/_core.sass +40 -0
  7. data/app/assets/stylesheets/html5S-libs/_normal.sass +193 -0
  8. data/app/assets/stylesheets/html5S-libs/_reset.sass +217 -0
  9. data/epyce.gemspec +23 -0
  10. data/lib/epyce.rb +6 -0
  11. data/lib/epyce/railtie.rb +12 -0
  12. data/lib/epyce/version.rb +5 -0
  13. data/lib/generators/epyce/install/install_generator.rb +24 -0
  14. data/lib/generators/epyce/template/app/views/layouts/_flashes.html.haml +4 -0
  15. data/lib/generators/epyce/template/app/views/layouts/_footer.html.haml +2 -0
  16. data/lib/generators/epyce/template/app/views/layouts/_header.html.haml +1 -0
  17. data/lib/generators/epyce/template/app/views/layouts/application.html.haml +57 -0
  18. data/lib/generators/epyce/template/public/apple-touch-icon-114x114-precomposed.png +0 -0
  19. data/lib/generators/epyce/template/public/apple-touch-icon-57x57-precomposed.png +0 -0
  20. data/lib/generators/epyce/template/public/apple-touch-icon-72x72-precomposed.png +0 -0
  21. data/lib/generators/epyce/template/public/apple-touch-icon-precomposed.png +0 -0
  22. data/lib/generators/epyce/template/public/apple-touch-icon.png +0 -0
  23. data/lib/generators/epyce/template/public/crossdomain.xml +14 -0
  24. data/lib/generators/epyce/template/public/favicon.png +0 -0
  25. data/vendor/assets/javascripts/epyce-debug.js.coffee +4 -0
  26. data/vendor/assets/javascripts/epyce-debug/dd_belatedpng.min.js +13 -0
  27. data/vendor/assets/javascripts/epyce-debug/head.js +681 -0
  28. data/vendor/assets/javascripts/epyce-debug/knockout-1.2.1.debug.js +2219 -0
  29. data/vendor/assets/javascripts/epyce-debug/respond.min.js +7 -0
  30. data/vendor/assets/javascripts/epyce-debug/underscore.js +839 -0
  31. data/vendor/assets/javascripts/epyce.js.coffee +4 -0
  32. data/vendor/assets/javascripts/epyce/dd_belatedpng.min.js +13 -0
  33. data/vendor/assets/javascripts/epyce/head.min.js +8 -0
  34. data/vendor/assets/javascripts/epyce/knockout-1.2.1.js +76 -0
  35. data/vendor/assets/javascripts/epyce/respond.min.js +7 -0
  36. data/vendor/assets/javascripts/epyce/underscore-min.js +27 -0
  37. data/vendor/assets/stylesheets/epyce.css.sass +4 -0
  38. data/vendor/assets/stylesheets/epyce/.gitkeep +0 -0
  39. data/vendor/assets/stylesheets/skeleton/base.css +336 -0
  40. data/vendor/assets/stylesheets/skeleton/layout.css +64 -0
  41. data/vendor/assets/stylesheets/skeleton/skeleton.css +237 -0
  42. metadata +111 -0
@@ -0,0 +1,2219 @@
1
+ // Knockout JavaScript library v1.2.1
2
+ // (c) Steven Sanderson - http://knockoutjs.com/
3
+ // License: MIT (http://www.opensource.org/licenses/mit-license.php)
4
+
5
+ (function(window,undefined){
6
+ var ko = window["ko"] = {};
7
+ // Google Closure Compiler helpers (used only to make the minified file smaller)
8
+ ko.exportSymbol = function(publicPath, object) {
9
+ var tokens = publicPath.split(".");
10
+ var target = window;
11
+ for (var i = 0; i < tokens.length - 1; i++)
12
+ target = target[tokens[i]];
13
+ target[tokens[tokens.length - 1]] = object;
14
+ };
15
+ ko.exportProperty = function(owner, publicName, object) {
16
+ owner[publicName] = object;
17
+ };
18
+ ko.utils = new (function () {
19
+ var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
20
+ var isIe6 = /MSIE 6/i.test(navigator.userAgent);
21
+ var isIe7 = /MSIE 7/i.test(navigator.userAgent);
22
+
23
+ // 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)
24
+ var knownEvents = {}, knownEventTypesByEventName = {};
25
+ var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
26
+ knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
27
+ knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
28
+ for (var eventType in knownEvents) {
29
+ var knownEventsForType = knownEvents[eventType];
30
+ if (knownEventsForType.length) {
31
+ for (var i = 0, j = knownEventsForType.length; i < j; i++)
32
+ knownEventTypesByEventName[knownEventsForType[i]] = eventType;
33
+ }
34
+ }
35
+
36
+ function isClickOnCheckableElement(element, eventType) {
37
+ if ((element.tagName != "INPUT") || !element.type) return false;
38
+ if (eventType.toLowerCase() != "click") return false;
39
+ var inputType = element.type.toLowerCase();
40
+ return (inputType == "checkbox") || (inputType == "radio");
41
+ }
42
+
43
+ return {
44
+ fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
45
+
46
+ arrayForEach: function (array, action) {
47
+ for (var i = 0, j = array.length; i < j; i++)
48
+ action(array[i]);
49
+ },
50
+
51
+ arrayIndexOf: function (array, item) {
52
+ if (typeof array.indexOf == "function")
53
+ return array.indexOf(item);
54
+ for (var i = 0, j = array.length; i < j; i++)
55
+ if (array[i] === item)
56
+ return i;
57
+ return -1;
58
+ },
59
+
60
+ arrayFirst: function (array, predicate, predicateOwner) {
61
+ for (var i = 0, j = array.length; i < j; i++)
62
+ if (predicate.call(predicateOwner, array[i]))
63
+ return array[i];
64
+ return null;
65
+ },
66
+
67
+ arrayRemoveItem: function (array, itemToRemove) {
68
+ var index = ko.utils.arrayIndexOf(array, itemToRemove);
69
+ if (index >= 0)
70
+ array.splice(index, 1);
71
+ },
72
+
73
+ arrayGetDistinctValues: function (array) {
74
+ array = array || [];
75
+ var result = [];
76
+ for (var i = 0, j = array.length; i < j; i++) {
77
+ if (ko.utils.arrayIndexOf(result, array[i]) < 0)
78
+ result.push(array[i]);
79
+ }
80
+ return result;
81
+ },
82
+
83
+ arrayMap: function (array, mapping) {
84
+ array = array || [];
85
+ var result = [];
86
+ for (var i = 0, j = array.length; i < j; i++)
87
+ result.push(mapping(array[i]));
88
+ return result;
89
+ },
90
+
91
+ arrayFilter: function (array, predicate) {
92
+ array = array || [];
93
+ var result = [];
94
+ for (var i = 0, j = array.length; i < j; i++)
95
+ if (predicate(array[i]))
96
+ result.push(array[i]);
97
+ return result;
98
+ },
99
+
100
+ arrayPushAll: function (array, valuesToPush) {
101
+ for (var i = 0, j = valuesToPush.length; i < j; i++)
102
+ array.push(valuesToPush[i]);
103
+ },
104
+
105
+ emptyDomNode: function (domNode) {
106
+ while (domNode.firstChild) {
107
+ ko.removeNode(domNode.firstChild);
108
+ }
109
+ },
110
+
111
+ setDomNodeChildren: function (domNode, childNodes) {
112
+ ko.utils.emptyDomNode(domNode);
113
+ if (childNodes) {
114
+ ko.utils.arrayForEach(childNodes, function (childNode) {
115
+ domNode.appendChild(childNode);
116
+ });
117
+ }
118
+ },
119
+
120
+ replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
121
+ var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
122
+ if (nodesToReplaceArray.length > 0) {
123
+ var insertionPoint = nodesToReplaceArray[0];
124
+ var parent = insertionPoint.parentNode;
125
+ for (var i = 0, j = newNodesArray.length; i < j; i++)
126
+ parent.insertBefore(newNodesArray[i], insertionPoint);
127
+ for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
128
+ ko.removeNode(nodesToReplaceArray[i]);
129
+ }
130
+ }
131
+ },
132
+
133
+ setOptionNodeSelectionState: function (optionNode, isSelected) {
134
+ // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
135
+ if (navigator.userAgent.indexOf("MSIE 6") >= 0)
136
+ optionNode.setAttribute("selected", isSelected);
137
+ else
138
+ optionNode.selected = isSelected;
139
+ },
140
+
141
+ getElementsHavingAttribute: function (rootNode, attributeName) {
142
+ if ((!rootNode) || (rootNode.nodeType != 1)) return [];
143
+ var results = [];
144
+ if (rootNode.getAttribute(attributeName) !== null)
145
+ results.push(rootNode);
146
+ var descendants = rootNode.getElementsByTagName("*");
147
+ for (var i = 0, j = descendants.length; i < j; i++)
148
+ if (descendants[i].getAttribute(attributeName) !== null)
149
+ results.push(descendants[i]);
150
+ return results;
151
+ },
152
+
153
+ stringTrim: function (string) {
154
+ return (string || "").replace(stringTrimRegex, "");
155
+ },
156
+
157
+ stringTokenize: function (string, delimiter) {
158
+ var result = [];
159
+ var tokens = (string || "").split(delimiter);
160
+ for (var i = 0, j = tokens.length; i < j; i++) {
161
+ var trimmed = ko.utils.stringTrim(tokens[i]);
162
+ if (trimmed !== "")
163
+ result.push(trimmed);
164
+ }
165
+ return result;
166
+ },
167
+
168
+ stringStartsWith: function (string, startsWith) {
169
+ string = string || "";
170
+ if (startsWith.length > string.length)
171
+ return false;
172
+ return string.substring(0, startsWith.length) === startsWith;
173
+ },
174
+
175
+ evalWithinScope: function (expression, scope) {
176
+ // Always do the evaling within a "new Function" to block access to parent scope
177
+ if (scope === undefined)
178
+ return (new Function("return " + expression))();
179
+
180
+ // Ensure "expression" is flattened into a source code string *before* it runs, otherwise
181
+ // the variable name "expression" itself will clash with a subproperty called "expression"
182
+ return (new Function("sc", "with(sc) { return (" + expression + ") }"))(scope);
183
+ },
184
+
185
+ domNodeIsContainedBy: function (node, containedByNode) {
186
+ if (containedByNode.compareDocumentPosition)
187
+ return (containedByNode.compareDocumentPosition(node) & 16) == 16;
188
+ while (node != null) {
189
+ if (node == containedByNode)
190
+ return true;
191
+ node = node.parentNode;
192
+ }
193
+ return false;
194
+ },
195
+
196
+ domNodeIsAttachedToDocument: function (node) {
197
+ return ko.utils.domNodeIsContainedBy(node, document);
198
+ },
199
+
200
+ registerEventHandler: function (element, eventType, handler) {
201
+ if (typeof jQuery != "undefined") {
202
+ if (isClickOnCheckableElement(element, eventType)) {
203
+ // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
204
+ // it toggles the element checked state *after* the click event handlers run, whereas native
205
+ // click events toggle the checked state *before* the event handler.
206
+ // Fix this by intecepting the handler and applying the correct checkedness before it runs.
207
+ var originalHandler = handler;
208
+ handler = function(event, eventData) {
209
+ var jQuerySuppliedCheckedState = this.checked;
210
+ if (eventData)
211
+ this.checked = eventData.checkedStateBeforeEvent !== true;
212
+ originalHandler.call(this, event);
213
+ this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
214
+ };
215
+ }
216
+ jQuery(element)['bind'](eventType, handler);
217
+ } else if (typeof element.addEventListener == "function")
218
+ element.addEventListener(eventType, handler, false);
219
+ else if (typeof element.attachEvent != "undefined")
220
+ element.attachEvent("on" + eventType, function (event) {
221
+ handler.call(element, event);
222
+ });
223
+ else
224
+ throw new Error("Browser doesn't support addEventListener or attachEvent");
225
+ },
226
+
227
+ triggerEvent: function (element, eventType) {
228
+ if (!(element && element.nodeType))
229
+ throw new Error("element must be a DOM node when calling triggerEvent");
230
+
231
+ if (typeof jQuery != "undefined") {
232
+ var eventData = [];
233
+ if (isClickOnCheckableElement(element, eventType)) {
234
+ // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
235
+ eventData.push({ checkedStateBeforeEvent: element.checked });
236
+ }
237
+ jQuery(element)['trigger'](eventType, eventData);
238
+ } else if (typeof document.createEvent == "function") {
239
+ if (typeof element.dispatchEvent == "function") {
240
+ var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
241
+ var event = document.createEvent(eventCategory);
242
+ event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
243
+ element.dispatchEvent(event);
244
+ }
245
+ else
246
+ throw new Error("The supplied element doesn't support dispatchEvent");
247
+ } else if (typeof element.fireEvent != "undefined") {
248
+ // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
249
+ // so to make it consistent, we'll do it manually here
250
+ if (eventType == "click") {
251
+ if ((element.tagName == "INPUT") && ((element.type.toLowerCase() == "checkbox") || (element.type.toLowerCase() == "radio")))
252
+ element.checked = element.checked !== true;
253
+ }
254
+ element.fireEvent("on" + eventType);
255
+ }
256
+ else
257
+ throw new Error("Browser doesn't support triggering events");
258
+ },
259
+
260
+ unwrapObservable: function (value) {
261
+ return ko.isObservable(value) ? value() : value;
262
+ },
263
+
264
+ domNodeHasCssClass: function (node, className) {
265
+ var currentClassNames = (node.className || "").split(/\s+/);
266
+ return ko.utils.arrayIndexOf(currentClassNames, className) >= 0;
267
+ },
268
+
269
+ toggleDomNodeCssClass: function (node, className, shouldHaveClass) {
270
+ var hasClass = ko.utils.domNodeHasCssClass(node, className);
271
+ if (shouldHaveClass && !hasClass) {
272
+ node.className = (node.className || "") + " " + className;
273
+ } else if (hasClass && !shouldHaveClass) {
274
+ var currentClassNames = (node.className || "").split(/\s+/);
275
+ var newClassName = "";
276
+ for (var i = 0; i < currentClassNames.length; i++)
277
+ if (currentClassNames[i] != className)
278
+ newClassName += currentClassNames[i] + " ";
279
+ node.className = ko.utils.stringTrim(newClassName);
280
+ }
281
+ },
282
+
283
+ range: function (min, max) {
284
+ min = ko.utils.unwrapObservable(min);
285
+ max = ko.utils.unwrapObservable(max);
286
+ var result = [];
287
+ for (var i = min; i <= max; i++)
288
+ result.push(i);
289
+ return result;
290
+ },
291
+
292
+ makeArray: function(arrayLikeObject) {
293
+ var result = [];
294
+ for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
295
+ result.push(arrayLikeObject[i]);
296
+ };
297
+ return result;
298
+ },
299
+
300
+ isIe6 : isIe6,
301
+ isIe7 : isIe7,
302
+
303
+ getFormFields: function(form, fieldName) {
304
+ var fields = ko.utils.makeArray(form.getElementsByTagName("INPUT")).concat(ko.utils.makeArray(form.getElementsByTagName("TEXTAREA")));
305
+ var isMatchingField = (typeof fieldName == 'string')
306
+ ? function(field) { return field.name === fieldName }
307
+ : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
308
+ var matches = [];
309
+ for (var i = fields.length - 1; i >= 0; i--) {
310
+ if (isMatchingField(fields[i]))
311
+ matches.push(fields[i]);
312
+ };
313
+ return matches;
314
+ },
315
+
316
+ parseJson: function (jsonString) {
317
+ if (typeof jsonString == "string") {
318
+ jsonString = ko.utils.stringTrim(jsonString);
319
+ if (jsonString) {
320
+ if (window.JSON && window.JSON.parse) // Use native parsing where available
321
+ return window.JSON.parse(jsonString);
322
+ return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
323
+ }
324
+ }
325
+ return null;
326
+ },
327
+
328
+ stringifyJson: function (data) {
329
+ if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
330
+ 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");
331
+ return JSON.stringify(ko.utils.unwrapObservable(data));
332
+ },
333
+
334
+ postJson: function (urlOrForm, data, options) {
335
+ options = options || {};
336
+ var params = options['params'] || {};
337
+ var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
338
+ var url = urlOrForm;
339
+
340
+ // If we were given a form, use its 'action' URL and pick out any requested field values
341
+ if((typeof urlOrForm == 'object') && (urlOrForm.tagName == "FORM")) {
342
+ var originalForm = urlOrForm;
343
+ url = originalForm.action;
344
+ for (var i = includeFields.length - 1; i >= 0; i--) {
345
+ var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
346
+ for (var j = fields.length - 1; j >= 0; j--)
347
+ params[fields[j].name] = fields[j].value;
348
+ }
349
+ }
350
+
351
+ data = ko.utils.unwrapObservable(data);
352
+ var form = document.createElement("FORM");
353
+ form.style.display = "none";
354
+ form.action = url;
355
+ form.method = "post";
356
+ for (var key in data) {
357
+ var input = document.createElement("INPUT");
358
+ input.name = key;
359
+ input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
360
+ form.appendChild(input);
361
+ }
362
+ for (var key in params) {
363
+ var input = document.createElement("INPUT");
364
+ input.name = key;
365
+ input.value = params[key];
366
+ form.appendChild(input);
367
+ }
368
+ document.body.appendChild(form);
369
+ options['submitter'] ? options['submitter'](form) : form.submit();
370
+ setTimeout(function () { form.parentNode.removeChild(form); }, 0);
371
+ }
372
+ }
373
+ })();
374
+
375
+ ko.exportSymbol('ko.utils', ko.utils);
376
+ ko.exportSymbol('ko.utils.arrayForEach', ko.utils.arrayForEach);
377
+ ko.exportSymbol('ko.utils.arrayFirst', ko.utils.arrayFirst);
378
+ ko.exportSymbol('ko.utils.arrayFilter', ko.utils.arrayFilter);
379
+ ko.exportSymbol('ko.utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
380
+ ko.exportSymbol('ko.utils.arrayIndexOf', ko.utils.arrayIndexOf);
381
+ ko.exportSymbol('ko.utils.arrayMap', ko.utils.arrayMap);
382
+ ko.exportSymbol('ko.utils.arrayPushAll', ko.utils.arrayPushAll);
383
+ ko.exportSymbol('ko.utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
384
+ ko.exportSymbol('ko.utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
385
+ ko.exportSymbol('ko.utils.getElementsHavingAttribute', ko.utils.getElementsHavingAttribute);
386
+ ko.exportSymbol('ko.utils.getFormFields', ko.utils.getFormFields);
387
+ ko.exportSymbol('ko.utils.postJson', ko.utils.postJson);
388
+ ko.exportSymbol('ko.utils.parseJson', ko.utils.parseJson);
389
+ ko.exportSymbol('ko.utils.registerEventHandler', ko.utils.registerEventHandler);
390
+ ko.exportSymbol('ko.utils.stringifyJson', ko.utils.stringifyJson);
391
+ ko.exportSymbol('ko.utils.range', ko.utils.range);
392
+ ko.exportSymbol('ko.utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
393
+ ko.exportSymbol('ko.utils.triggerEvent', ko.utils.triggerEvent);
394
+ ko.exportSymbol('ko.utils.unwrapObservable', ko.utils.unwrapObservable);
395
+
396
+ if (!Function.prototype['bind']) {
397
+ // 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)
398
+ // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
399
+ Function.prototype['bind'] = function (object) {
400
+ var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
401
+ return function () {
402
+ return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
403
+ };
404
+ };
405
+ }
406
+ ko.utils.domData = new (function () {
407
+ var uniqueId = 0;
408
+ var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
409
+ var dataStore = {};
410
+ return {
411
+ get: function (node, key) {
412
+ var allDataForNode = ko.utils.domData.getAll(node, false);
413
+ return allDataForNode === undefined ? undefined : allDataForNode[key];
414
+ },
415
+ set: function (node, key, value) {
416
+ if (value === undefined) {
417
+ // Make sure we don't actually create a new domData key if we are actually deleting a value
418
+ if (ko.utils.domData.getAll(node, false) === undefined)
419
+ return;
420
+ }
421
+ var allDataForNode = ko.utils.domData.getAll(node, true);
422
+ allDataForNode[key] = value;
423
+ },
424
+ getAll: function (node, createIfNotFound) {
425
+ var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
426
+ if (!dataStoreKey) {
427
+ if (!createIfNotFound)
428
+ return undefined;
429
+ dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
430
+ dataStore[dataStoreKey] = {};
431
+ }
432
+ return dataStore[dataStoreKey];
433
+ },
434
+ clear: function (node) {
435
+ var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
436
+ if (dataStoreKey) {
437
+ delete dataStore[dataStoreKey];
438
+ node[dataStoreKeyExpandoPropertyName] = null;
439
+ }
440
+ }
441
+ }
442
+ })();
443
+ ko.utils.domNodeDisposal = new (function () {
444
+ var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
445
+
446
+ function getDisposeCallbacksCollection(node, createIfNotFound) {
447
+ var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
448
+ if ((allDisposeCallbacks === undefined) && createIfNotFound) {
449
+ allDisposeCallbacks = [];
450
+ ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
451
+ }
452
+ return allDisposeCallbacks;
453
+ }
454
+ function destroyCallbacksCollection(node) {
455
+ ko.utils.domData.set(node, domDataKey, undefined);
456
+ }
457
+
458
+ function cleanSingleNode(node) {
459
+ // Run all the dispose callbacks
460
+ var callbacks = getDisposeCallbacksCollection(node, false);
461
+ if (callbacks) {
462
+ callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
463
+ for (var i = 0; i < callbacks.length; i++)
464
+ callbacks[i](node);
465
+ }
466
+
467
+ // Also erase the DOM data
468
+ ko.utils.domData.clear(node);
469
+
470
+ // Special support for jQuery here because it's so commonly used.
471
+ // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
472
+ // so notify it to tear down any resources associated with the node & descendants here.
473
+ if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
474
+ jQuery['cleanData']([node]);
475
+ }
476
+
477
+ return {
478
+ addDisposeCallback : function(node, callback) {
479
+ if (typeof callback != "function")
480
+ throw new Error("Callback must be a function");
481
+ getDisposeCallbacksCollection(node, true).push(callback);
482
+ },
483
+
484
+ removeDisposeCallback : function(node, callback) {
485
+ var callbacksCollection = getDisposeCallbacksCollection(node, false);
486
+ if (callbacksCollection) {
487
+ ko.utils.arrayRemoveItem(callbacksCollection, callback);
488
+ if (callbacksCollection.length == 0)
489
+ destroyCallbacksCollection(node);
490
+ }
491
+ },
492
+
493
+ cleanNode : function(node) {
494
+ if ((node.nodeType != 1) && (node.nodeType != 9))
495
+ return;
496
+ cleanSingleNode(node);
497
+
498
+ // Clone the descendants list in case it changes during iteration
499
+ var descendants = [];
500
+ ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
501
+ for (var i = 0, j = descendants.length; i < j; i++)
502
+ cleanSingleNode(descendants[i]);
503
+ },
504
+
505
+ removeNode : function(node) {
506
+ ko.cleanNode(node);
507
+ if (node.parentNode)
508
+ node.parentNode.removeChild(node);
509
+ }
510
+ }
511
+ })();
512
+ ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
513
+ ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
514
+ ko.exportSymbol('ko.cleanNode', ko.cleanNode);
515
+ ko.exportSymbol('ko.removeNode', ko.removeNode);
516
+ ko.exportSymbol('ko.utils.domNodeDisposal', ko.utils.domNodeDisposal);
517
+ ko.exportSymbol('ko.utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
518
+ ko.exportSymbol('ko.utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);(function () {
519
+ function simpleHtmlParse(html) {
520
+ // Based on jQuery's "clean" function, but only accounting for table-related elements.
521
+ // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
522
+
523
+ // Trim whitespace, otherwise indexOf won't work as expected
524
+ var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
525
+
526
+ // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
527
+ var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
528
+ !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
529
+ (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
530
+ /* anything else */ [0, "", ""];
531
+
532
+ // Go to html and back, then peel off extra wrappers
533
+ div.innerHTML = wrap[1] + html + wrap[2];
534
+
535
+ // Move to the right depth
536
+ while (wrap[0]--)
537
+ div = div.lastChild;
538
+
539
+ return ko.utils.makeArray(div.childNodes);
540
+ }
541
+
542
+ ko.utils.parseHtmlFragment = function(html) {
543
+ return typeof jQuery != 'undefined' ? jQuery['clean']([html]) // As below, benefit from jQuery's optimisations where possible
544
+ : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
545
+ };
546
+
547
+ ko.utils.setHtml = function(node, html) {
548
+ ko.utils.emptyDomNode(node);
549
+
550
+ if ((html !== null) && (html !== undefined)) {
551
+ if (typeof html != 'string')
552
+ html = html.toString();
553
+
554
+ // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
555
+ // for example <tr> elements which are not normally allowed to exist on their own.
556
+ // If you've referenced jQuery we'll use that rather than duplicating its code.
557
+ if (typeof jQuery != 'undefined') {
558
+ jQuery(node)['html'](html);
559
+ } else {
560
+ // ... otherwise, use KO's own parsing logic.
561
+ var parsedNodes = ko.utils.parseHtmlFragment(html);
562
+ for (var i = 0; i < parsedNodes.length; i++)
563
+ node.appendChild(parsedNodes[i]);
564
+ }
565
+ }
566
+ };
567
+ })();
568
+ ko.memoization = (function () {
569
+ var memos = {};
570
+
571
+ function randomMax8HexChars() {
572
+ return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
573
+ }
574
+ function generateRandomId() {
575
+ return randomMax8HexChars() + randomMax8HexChars();
576
+ }
577
+ function findMemoNodes(rootNode, appendToArray) {
578
+ if (!rootNode)
579
+ return;
580
+ if (rootNode.nodeType == 8) {
581
+ var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
582
+ if (memoId != null)
583
+ appendToArray.push({ domNode: rootNode, memoId: memoId });
584
+ } else if (rootNode.nodeType == 1) {
585
+ for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
586
+ findMemoNodes(childNodes[i], appendToArray);
587
+ }
588
+ }
589
+
590
+ return {
591
+ memoize: function (callback) {
592
+ if (typeof callback != "function")
593
+ throw new Error("You can only pass a function to ko.memoization.memoize()");
594
+ var memoId = generateRandomId();
595
+ memos[memoId] = callback;
596
+ return "<!--[ko_memo:" + memoId + "]-->";
597
+ },
598
+
599
+ unmemoize: function (memoId, callbackParams) {
600
+ var callback = memos[memoId];
601
+ if (callback === undefined)
602
+ throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
603
+ try {
604
+ callback.apply(null, callbackParams || []);
605
+ return true;
606
+ }
607
+ finally { delete memos[memoId]; }
608
+ },
609
+
610
+ unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
611
+ var memos = [];
612
+ findMemoNodes(domNode, memos);
613
+ for (var i = 0, j = memos.length; i < j; i++) {
614
+ var node = memos[i].domNode;
615
+ var combinedParams = [node];
616
+ if (extraCallbackParamsArray)
617
+ ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
618
+ ko.memoization.unmemoize(memos[i].memoId, combinedParams);
619
+ node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
620
+ if (node.parentNode)
621
+ 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)
622
+ }
623
+ },
624
+
625
+ parseMemoText: function (memoText) {
626
+ var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
627
+ return match ? match[1] : null;
628
+ }
629
+ };
630
+ })();
631
+
632
+ ko.exportSymbol('ko.memoization', ko.memoization);
633
+ ko.exportSymbol('ko.memoization.memoize', ko.memoization.memoize);
634
+ ko.exportSymbol('ko.memoization.unmemoize', ko.memoization.unmemoize);
635
+ ko.exportSymbol('ko.memoization.parseMemoText', ko.memoization.parseMemoText);
636
+ ko.exportSymbol('ko.memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
637
+
638
+ ko.subscription = function (callback, disposeCallback) {
639
+ this.callback = callback;
640
+ this.dispose = function () {
641
+ this.isDisposed = true;
642
+ disposeCallback();
643
+ }['bind'](this);
644
+
645
+ ko.exportProperty(this, 'dispose', this.dispose);
646
+ };
647
+
648
+ ko.subscribable = function () {
649
+ var _subscriptions = [];
650
+
651
+ this.subscribe = function (callback, callbackTarget) {
652
+ var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
653
+
654
+ var subscription = new ko.subscription(boundCallback, function () {
655
+ ko.utils.arrayRemoveItem(_subscriptions, subscription);
656
+ });
657
+ _subscriptions.push(subscription);
658
+ return subscription;
659
+ };
660
+
661
+ this.notifySubscribers = function (valueToNotify) {
662
+ ko.utils.arrayForEach(_subscriptions.slice(0), function (subscription) {
663
+ // In case a subscription was disposed during the arrayForEach cycle, check
664
+ // for isDisposed on each subscription before invoking its callback
665
+ if (subscription && (subscription.isDisposed !== true))
666
+ subscription.callback(valueToNotify);
667
+ });
668
+ };
669
+
670
+ this.getSubscriptionsCount = function () {
671
+ return _subscriptions.length;
672
+ };
673
+
674
+ ko.exportProperty(this, 'subscribe', this.subscribe);
675
+ ko.exportProperty(this, 'notifySubscribers', this.notifySubscribers);
676
+ ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
677
+ }
678
+
679
+ ko.isSubscribable = function (instance) {
680
+ return typeof instance.subscribe == "function" && typeof instance.notifySubscribers == "function";
681
+ };
682
+
683
+ ko.exportSymbol('ko.subscribable', ko.subscribable);
684
+ ko.exportSymbol('ko.isSubscribable', ko.isSubscribable);
685
+
686
+ ko.dependencyDetection = (function () {
687
+ var _detectedDependencies = [];
688
+
689
+ return {
690
+ begin: function () {
691
+ _detectedDependencies.push([]);
692
+ },
693
+
694
+ end: function () {
695
+ return _detectedDependencies.pop();
696
+ },
697
+
698
+ registerDependency: function (subscribable) {
699
+ if (!ko.isSubscribable(subscribable))
700
+ throw "Only subscribable things can act as dependencies";
701
+ if (_detectedDependencies.length > 0) {
702
+ _detectedDependencies[_detectedDependencies.length - 1].push(subscribable);
703
+ }
704
+ }
705
+ };
706
+ })();var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
707
+
708
+ function valuesArePrimitiveAndEqual(a, b) {
709
+ var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
710
+ return oldValueIsPrimitive ? (a === b) : false;
711
+ }
712
+
713
+ ko.observable = function (initialValue) {
714
+ var _latestValue = initialValue;
715
+
716
+ function observable() {
717
+ if (arguments.length > 0) {
718
+ // Write
719
+
720
+ // Ignore writes if the value hasn't changed
721
+ if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
722
+ _latestValue = arguments[0];
723
+ observable.notifySubscribers(_latestValue);
724
+ }
725
+ return this; // Permits chained assignments
726
+ }
727
+ else {
728
+ // Read
729
+ ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
730
+ return _latestValue;
731
+ }
732
+ }
733
+ observable.__ko_proto__ = ko.observable;
734
+ observable.valueHasMutated = function () { observable.notifySubscribers(_latestValue); }
735
+ observable['equalityComparer'] = valuesArePrimitiveAndEqual;
736
+
737
+ ko.subscribable.call(observable);
738
+
739
+ ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
740
+
741
+ return observable;
742
+ }
743
+ ko.isObservable = function (instance) {
744
+ if ((instance === null) || (instance === undefined) || (instance.__ko_proto__ === undefined)) return false;
745
+ if (instance.__ko_proto__ === ko.observable) return true;
746
+ return ko.isObservable(instance.__ko_proto__); // Walk the prototype chain
747
+ }
748
+ ko.isWriteableObservable = function (instance) {
749
+ // Observable
750
+ if ((typeof instance == "function") && instance.__ko_proto__ === ko.observable)
751
+ return true;
752
+ // Writeable dependent observable
753
+ if ((typeof instance == "function") && (instance.__ko_proto__ === ko.dependentObservable) && (instance.hasWriteFunction))
754
+ return true;
755
+ // Anything else
756
+ return false;
757
+ }
758
+
759
+
760
+ ko.exportSymbol('ko.observable', ko.observable);
761
+ ko.exportSymbol('ko.isObservable', ko.isObservable);
762
+ ko.exportSymbol('ko.isWriteableObservable', ko.isWriteableObservable);
763
+ ko.observableArray = function (initialValues) {
764
+ if (arguments.length == 0) {
765
+ // Zero-parameter constructor initializes to empty array
766
+ initialValues = [];
767
+ }
768
+ if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
769
+ throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
770
+ var result = new ko.observable(initialValues);
771
+
772
+ ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
773
+ result[methodName] = function () {
774
+ var underlyingArray = result();
775
+ var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
776
+ result.valueHasMutated();
777
+ return methodCallResult;
778
+ };
779
+ });
780
+
781
+ ko.utils.arrayForEach(["slice"], function (methodName) {
782
+ result[methodName] = function () {
783
+ var underlyingArray = result();
784
+ return underlyingArray[methodName].apply(underlyingArray, arguments);
785
+ };
786
+ });
787
+
788
+ result.remove = function (valueOrPredicate) {
789
+ var underlyingArray = result();
790
+ var remainingValues = [];
791
+ var removedValues = [];
792
+ var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
793
+ for (var i = 0, j = underlyingArray.length; i < j; i++) {
794
+ var value = underlyingArray[i];
795
+ if (!predicate(value))
796
+ remainingValues.push(value);
797
+ else
798
+ removedValues.push(value);
799
+ }
800
+ result(remainingValues);
801
+ return removedValues;
802
+ };
803
+
804
+ result.removeAll = function (arrayOfValues) {
805
+ // If you passed zero args, we remove everything
806
+ if (arrayOfValues === undefined) {
807
+ var allValues = result();
808
+ result([]);
809
+ return allValues;
810
+ }
811
+
812
+ // If you passed an arg, we interpret it as an array of entries to remove
813
+ if (!arrayOfValues)
814
+ return [];
815
+ return result.remove(function (value) {
816
+ return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
817
+ });
818
+ };
819
+
820
+ result.destroy = function (valueOrPredicate) {
821
+ var underlyingArray = result();
822
+ var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
823
+ for (var i = underlyingArray.length - 1; i >= 0; i--) {
824
+ var value = underlyingArray[i];
825
+ if (predicate(value))
826
+ underlyingArray[i]["_destroy"] = true;
827
+ }
828
+ result.valueHasMutated();
829
+ };
830
+
831
+ result.destroyAll = function (arrayOfValues) {
832
+ // If you passed zero args, we destroy everything
833
+ if (arrayOfValues === undefined)
834
+ return result.destroy(function() { return true });
835
+
836
+ // If you passed an arg, we interpret it as an array of entries to destroy
837
+ if (!arrayOfValues)
838
+ return [];
839
+ return result.destroy(function (value) {
840
+ return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
841
+ });
842
+ };
843
+
844
+ result.indexOf = function (item) {
845
+ var underlyingArray = result();
846
+ return ko.utils.arrayIndexOf(underlyingArray, item);
847
+ };
848
+
849
+ result.replace = function(oldItem, newItem) {
850
+ var index = result.indexOf(oldItem);
851
+ if (index >= 0) {
852
+ result()[index] = newItem;
853
+ result.valueHasMutated();
854
+ }
855
+ };
856
+
857
+ ko.exportProperty(result, "remove", result.remove);
858
+ ko.exportProperty(result, "removeAll", result.removeAll);
859
+ ko.exportProperty(result, "destroy", result.destroy);
860
+ ko.exportProperty(result, "destroyAll", result.destroyAll);
861
+ ko.exportProperty(result, "indexOf", result.indexOf);
862
+
863
+ return result;
864
+ }
865
+
866
+ ko.exportSymbol('ko.observableArray', ko.observableArray);
867
+ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
868
+ var _latestValue, _hasBeenEvaluated = false;
869
+
870
+ if (evaluatorFunctionOrOptions && typeof evaluatorFunctionOrOptions == "object") {
871
+ // Single-parameter syntax - everything is on this "options" param
872
+ options = evaluatorFunctionOrOptions;
873
+ } else {
874
+ // Multi-parameter syntax - construct the options according to the params passed
875
+ options = options || {};
876
+ options["read"] = evaluatorFunctionOrOptions || options["read"];
877
+ options["owner"] = evaluatorFunctionTarget || options["owner"];
878
+ }
879
+ // By here, "options" is always non-null
880
+
881
+ if (typeof options["read"] != "function")
882
+ throw "Pass a function that returns the value of the dependentObservable";
883
+
884
+ // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values
885
+ // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
886
+ // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
887
+ var disposeWhenNodeIsRemoved = (typeof options["disposeWhenNodeIsRemoved"] == "object") ? options["disposeWhenNodeIsRemoved"] : null;
888
+ var disposeWhenNodeIsRemovedCallback = null;
889
+ if (disposeWhenNodeIsRemoved) {
890
+ disposeWhenNodeIsRemovedCallback = function() { dependentObservable.dispose() };
891
+ ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, disposeWhenNodeIsRemovedCallback);
892
+ var existingDisposeWhenFunction = options["disposeWhen"];
893
+ options["disposeWhen"] = function () {
894
+ return (!ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved))
895
+ || ((typeof existingDisposeWhenFunction == "function") && existingDisposeWhenFunction());
896
+ }
897
+ }
898
+
899
+ var _subscriptionsToDependencies = [];
900
+ function disposeAllSubscriptionsToDependencies() {
901
+ ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
902
+ subscription.dispose();
903
+ });
904
+ _subscriptionsToDependencies = [];
905
+ }
906
+
907
+ function replaceSubscriptionsToDependencies(newDependencies) {
908
+ disposeAllSubscriptionsToDependencies();
909
+ ko.utils.arrayForEach(newDependencies, function (dependency) {
910
+ _subscriptionsToDependencies.push(dependency.subscribe(evaluate));
911
+ });
912
+ };
913
+
914
+ function evaluate() {
915
+ // Don't dispose on first evaluation, because the "disposeWhen" callback might
916
+ // e.g., dispose when the associated DOM element isn't in the doc, and it's not
917
+ // going to be in the doc until *after* the first evaluation
918
+ if ((_hasBeenEvaluated) && typeof options["disposeWhen"] == "function") {
919
+ if (options["disposeWhen"]()) {
920
+ dependentObservable.dispose();
921
+ return;
922
+ }
923
+ }
924
+
925
+ try {
926
+ ko.dependencyDetection.begin();
927
+ _latestValue = options["owner"] ? options["read"].call(options["owner"]) : options["read"]();
928
+ } finally {
929
+ var distinctDependencies = ko.utils.arrayGetDistinctValues(ko.dependencyDetection.end());
930
+ replaceSubscriptionsToDependencies(distinctDependencies);
931
+ }
932
+
933
+ dependentObservable.notifySubscribers(_latestValue);
934
+ _hasBeenEvaluated = true;
935
+ }
936
+
937
+ function dependentObservable() {
938
+ if (arguments.length > 0) {
939
+ if (typeof options["write"] === "function") {
940
+ // Writing a value
941
+ var valueToWrite = arguments[0];
942
+ options["owner"] ? options["write"].call(options["owner"], valueToWrite) : options["write"](valueToWrite);
943
+ } else {
944
+ throw "Cannot write a value to a dependentObservable unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.";
945
+ }
946
+ } else {
947
+ // Reading the value
948
+ if (!_hasBeenEvaluated)
949
+ evaluate();
950
+ ko.dependencyDetection.registerDependency(dependentObservable);
951
+ return _latestValue;
952
+ }
953
+ }
954
+ dependentObservable.__ko_proto__ = ko.dependentObservable;
955
+ dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; }
956
+ dependentObservable.hasWriteFunction = typeof options["write"] === "function";
957
+ dependentObservable.dispose = function () {
958
+ if (disposeWhenNodeIsRemoved)
959
+ ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, disposeWhenNodeIsRemovedCallback);
960
+ disposeAllSubscriptionsToDependencies();
961
+ };
962
+
963
+ ko.subscribable.call(dependentObservable);
964
+ if (options['deferEvaluation'] !== true)
965
+ evaluate();
966
+
967
+ ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
968
+ ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
969
+
970
+ return dependentObservable;
971
+ };
972
+ ko.dependentObservable.__ko_proto__ = ko.observable;
973
+
974
+ ko.exportSymbol('ko.dependentObservable', ko.dependentObservable);
975
+
976
+ (function() {
977
+ var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
978
+
979
+ ko.toJS = function(rootObject) {
980
+ if (arguments.length == 0)
981
+ throw new Error("When calling ko.toJS, pass the object you want to convert.");
982
+
983
+ // We just unwrap everything at every level in the object graph
984
+ return mapJsObjectGraph(rootObject, function(valueToMap) {
985
+ // Loop because an observable's value might in turn be another observable wrapper
986
+ for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
987
+ valueToMap = valueToMap();
988
+ return valueToMap;
989
+ });
990
+ };
991
+
992
+ ko.toJSON = function(rootObject) {
993
+ var plainJavaScriptObject = ko.toJS(rootObject);
994
+ return ko.utils.stringifyJson(plainJavaScriptObject);
995
+ };
996
+
997
+ function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
998
+ visitedObjects = visitedObjects || new objectLookup();
999
+
1000
+ rootObject = mapInputCallback(rootObject);
1001
+ var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined);
1002
+ if (!canHaveProperties)
1003
+ return rootObject;
1004
+
1005
+ var outputProperties = rootObject instanceof Array ? [] : {};
1006
+ visitedObjects.save(rootObject, outputProperties);
1007
+
1008
+ visitPropertiesOrArrayEntries(rootObject, function(indexer) {
1009
+ var propertyValue = mapInputCallback(rootObject[indexer]);
1010
+
1011
+ switch (typeof propertyValue) {
1012
+ case "boolean":
1013
+ case "number":
1014
+ case "string":
1015
+ case "function":
1016
+ outputProperties[indexer] = propertyValue;
1017
+ break;
1018
+ case "object":
1019
+ case "undefined":
1020
+ var previouslyMappedValue = visitedObjects.get(propertyValue);
1021
+ outputProperties[indexer] = (previouslyMappedValue !== undefined)
1022
+ ? previouslyMappedValue
1023
+ : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
1024
+ break;
1025
+ }
1026
+ });
1027
+
1028
+ return outputProperties;
1029
+ }
1030
+
1031
+ function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
1032
+ if (rootObject instanceof Array) {
1033
+ for (var i = 0; i < rootObject.length; i++)
1034
+ visitorCallback(i);
1035
+ } else {
1036
+ for (var propertyName in rootObject)
1037
+ visitorCallback(propertyName);
1038
+ }
1039
+ };
1040
+
1041
+ function objectLookup() {
1042
+ var keys = [];
1043
+ var values = [];
1044
+ this.save = function(key, value) {
1045
+ var existingIndex = ko.utils.arrayIndexOf(keys, key);
1046
+ if (existingIndex >= 0)
1047
+ values[existingIndex] = value;
1048
+ else {
1049
+ keys.push(key);
1050
+ values.push(value);
1051
+ }
1052
+ };
1053
+ this.get = function(key) {
1054
+ var existingIndex = ko.utils.arrayIndexOf(keys, key);
1055
+ return (existingIndex >= 0) ? values[existingIndex] : undefined;
1056
+ };
1057
+ };
1058
+ })();
1059
+
1060
+ ko.exportSymbol('ko.toJS', ko.toJS);
1061
+ ko.exportSymbol('ko.toJSON', ko.toJSON);(function () {
1062
+ // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
1063
+ // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
1064
+ // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
1065
+ ko.selectExtensions = {
1066
+ readValue : function(element) {
1067
+ if (element.tagName == 'OPTION') {
1068
+ if (element['__ko__hasDomDataOptionValue__'] === true)
1069
+ return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
1070
+ return element.getAttribute("value");
1071
+ } else if (element.tagName == 'SELECT')
1072
+ return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
1073
+ else
1074
+ return element.value;
1075
+ },
1076
+
1077
+ writeValue: function(element, value) {
1078
+ if (element.tagName == 'OPTION') {
1079
+ switch(typeof value) {
1080
+ case "string":
1081
+ case "number":
1082
+ ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
1083
+ if ('__ko__hasDomDataOptionValue__' in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
1084
+ delete element['__ko__hasDomDataOptionValue__'];
1085
+ }
1086
+ element.value = value;
1087
+ break;
1088
+ default:
1089
+ // Store arbitrary object using DomData
1090
+ ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
1091
+ element['__ko__hasDomDataOptionValue__'] = true;
1092
+ element.value = "";
1093
+ break;
1094
+ }
1095
+ } else if (element.tagName == 'SELECT') {
1096
+ for (var i = element.options.length - 1; i >= 0; i--) {
1097
+ if (ko.selectExtensions.readValue(element.options[i]) == value) {
1098
+ element.selectedIndex = i;
1099
+ break;
1100
+ }
1101
+ }
1102
+ } else {
1103
+ if ((value === null) || (value === undefined))
1104
+ value = "";
1105
+ element.value = value;
1106
+ }
1107
+ }
1108
+ };
1109
+ })();
1110
+
1111
+ ko.exportSymbol('ko.selectExtensions', ko.selectExtensions);
1112
+ ko.exportSymbol('ko.selectExtensions.readValue', ko.selectExtensions.readValue);
1113
+ ko.exportSymbol('ko.selectExtensions.writeValue', ko.selectExtensions.writeValue);
1114
+
1115
+ ko.jsonExpressionRewriting = (function () {
1116
+ var restoreCapturedTokensRegex = /\[ko_token_(\d+)\]/g;
1117
+ var javaScriptAssignmentTarget = /^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i;
1118
+ var javaScriptReservedWords = ["true", "false"];
1119
+
1120
+ function restoreTokens(string, tokens) {
1121
+ return string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
1122
+ return tokens[tokenIndex];
1123
+ });
1124
+ }
1125
+
1126
+ function isWriteableValue(expression) {
1127
+ if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
1128
+ return false;
1129
+ return expression.match(javaScriptAssignmentTarget) !== null;
1130
+ }
1131
+
1132
+ return {
1133
+ parseJson: function (jsonString) {
1134
+ jsonString = ko.utils.stringTrim(jsonString);
1135
+ if (jsonString.length < 3)
1136
+ return {};
1137
+
1138
+ // We're going to split on commas, so first extract any blocks that may contain commas other than those at the top level
1139
+ var tokens = [];
1140
+ var tokenStart = null, tokenEndChar;
1141
+ for (var position = jsonString.charAt(0) == "{" ? 1 : 0; position < jsonString.length; position++) {
1142
+ var c = jsonString.charAt(position);
1143
+ if (tokenStart === null) {
1144
+ switch (c) {
1145
+ case '"':
1146
+ case "'":
1147
+ case "/":
1148
+ tokenStart = position;
1149
+ tokenEndChar = c;
1150
+ break;
1151
+ case "{":
1152
+ tokenStart = position;
1153
+ tokenEndChar = "}";
1154
+ break;
1155
+ case "[":
1156
+ tokenStart = position;
1157
+ tokenEndChar = "]";
1158
+ break;
1159
+ }
1160
+ } else if (c == tokenEndChar) {
1161
+ var token = jsonString.substring(tokenStart, position + 1);
1162
+ tokens.push(token);
1163
+ var replacement = "[ko_token_" + (tokens.length - 1) + "]";
1164
+ jsonString = jsonString.substring(0, tokenStart) + replacement + jsonString.substring(position + 1);
1165
+ position -= (token.length - replacement.length);
1166
+ tokenStart = null;
1167
+ }
1168
+ }
1169
+
1170
+ // Now we can safely split on commas to get the key/value pairs
1171
+ var result = {};
1172
+ var keyValuePairs = jsonString.split(",");
1173
+ for (var i = 0, j = keyValuePairs.length; i < j; i++) {
1174
+ var pair = keyValuePairs[i];
1175
+ var colonPos = pair.indexOf(":");
1176
+ if ((colonPos > 0) && (colonPos < pair.length - 1)) {
1177
+ var key = ko.utils.stringTrim(pair.substring(0, colonPos));
1178
+ var value = ko.utils.stringTrim(pair.substring(colonPos + 1));
1179
+ if (key.charAt(0) == "{")
1180
+ key = key.substring(1);
1181
+ if (value.charAt(value.length - 1) == "}")
1182
+ value = value.substring(0, value.length - 1);
1183
+ key = ko.utils.stringTrim(restoreTokens(key, tokens));
1184
+ value = ko.utils.stringTrim(restoreTokens(value, tokens));
1185
+ result[key] = value;
1186
+ }
1187
+ }
1188
+ return result;
1189
+ },
1190
+
1191
+ insertPropertyAccessorsIntoJson: function (jsonString) {
1192
+ var parsed = ko.jsonExpressionRewriting.parseJson(jsonString);
1193
+ var propertyAccessorTokens = [];
1194
+ for (var key in parsed) {
1195
+ var value = parsed[key];
1196
+ if (isWriteableValue(value)) {
1197
+ if (propertyAccessorTokens.length > 0)
1198
+ propertyAccessorTokens.push(", ");
1199
+ propertyAccessorTokens.push(key + " : function(__ko_value) { " + value + " = __ko_value; }");
1200
+ }
1201
+ }
1202
+
1203
+ if (propertyAccessorTokens.length > 0) {
1204
+ var allPropertyAccessors = propertyAccessorTokens.join("");
1205
+ jsonString = jsonString + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
1206
+ }
1207
+
1208
+ return jsonString;
1209
+ }
1210
+ };
1211
+ })();
1212
+
1213
+ ko.exportSymbol('ko.jsonExpressionRewriting', ko.jsonExpressionRewriting);
1214
+ ko.exportSymbol('ko.jsonExpressionRewriting.parseJson', ko.jsonExpressionRewriting.parseJson);
1215
+ ko.exportSymbol('ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson);
1216
+
1217
+ (function () {
1218
+ var defaultBindingAttributeName = "data-bind";
1219
+ ko.bindingHandlers = {};
1220
+
1221
+ function parseBindingAttribute(attributeText, viewModel) {
1222
+ try {
1223
+ var json = " { " + ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson(attributeText) + " } ";
1224
+ return ko.utils.evalWithinScope(json, viewModel === null ? window : viewModel);
1225
+ } catch (ex) {
1226
+ throw new Error("Unable to parse binding attribute.\nMessage: " + ex + ";\nAttribute value: " + attributeText);
1227
+ }
1228
+ }
1229
+
1230
+ function invokeBindingHandler(handler, element, dataValue, allBindings, viewModel) {
1231
+ handler(element, dataValue, allBindings, viewModel);
1232
+ }
1233
+
1234
+ ko.applyBindingsToNode = function (node, bindings, viewModel, bindingAttributeName) {
1235
+ var isFirstEvaluation = true;
1236
+ bindingAttributeName = bindingAttributeName || defaultBindingAttributeName;
1237
+
1238
+ // Each time the dependentObservable is evaluated (after data changes),
1239
+ // the binding attribute is reparsed so that it can pick out the correct
1240
+ // model properties in the context of the changed data.
1241
+ // DOM event callbacks need to be able to access this changed data,
1242
+ // so we need a single parsedBindings variable (shared by all callbacks
1243
+ // associated with this node's bindings) that all the closures can access.
1244
+ var parsedBindings;
1245
+ function makeValueAccessor(bindingKey) {
1246
+ return function () { return parsedBindings[bindingKey] }
1247
+ }
1248
+ function parsedBindingsAccessor() {
1249
+ return parsedBindings;
1250
+ }
1251
+
1252
+ new ko.dependentObservable(
1253
+ function () {
1254
+ var evaluatedBindings = (typeof bindings == "function") ? bindings() : bindings;
1255
+ parsedBindings = evaluatedBindings || parseBindingAttribute(node.getAttribute(bindingAttributeName), viewModel);
1256
+
1257
+ // First run all the inits, so bindings can register for notification on changes
1258
+ if (isFirstEvaluation) {
1259
+ for (var bindingKey in parsedBindings) {
1260
+ if (ko.bindingHandlers[bindingKey] && typeof ko.bindingHandlers[bindingKey]["init"] == "function")
1261
+ invokeBindingHandler(ko.bindingHandlers[bindingKey]["init"], node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel);
1262
+ }
1263
+ }
1264
+
1265
+ // ... then run all the updates, which might trigger changes even on the first evaluation
1266
+ for (var bindingKey in parsedBindings) {
1267
+ if (ko.bindingHandlers[bindingKey] && typeof ko.bindingHandlers[bindingKey]["update"] == "function")
1268
+ invokeBindingHandler(ko.bindingHandlers[bindingKey]["update"], node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel);
1269
+ }
1270
+ },
1271
+ null,
1272
+ { 'disposeWhenNodeIsRemoved' : node }
1273
+ );
1274
+ isFirstEvaluation = false;
1275
+ };
1276
+
1277
+ ko.applyBindings = function (viewModel, rootNode) {
1278
+ if (rootNode && (rootNode.nodeType == undefined))
1279
+ throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node (note: this is a breaking change since KO version 1.05)");
1280
+ rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
1281
+
1282
+ var elemsWithBindingAttribute = ko.utils.getElementsHavingAttribute(rootNode, defaultBindingAttributeName);
1283
+ ko.utils.arrayForEach(elemsWithBindingAttribute, function (element) {
1284
+ ko.applyBindingsToNode(element, null, viewModel);
1285
+ });
1286
+ };
1287
+
1288
+ ko.exportSymbol('ko.bindingHandlers', ko.bindingHandlers);
1289
+ ko.exportSymbol('ko.applyBindings', ko.applyBindings);
1290
+ ko.exportSymbol('ko.applyBindingsToNode', ko.applyBindingsToNode);
1291
+ })();// For certain common events (currently just 'click'), allow a simplified data-binding syntax
1292
+ // e.g. click:handler instead of the usual full-length event:{click:handler}
1293
+ var eventHandlersWithShortcuts = ['click'];
1294
+ ko.utils.arrayForEach(eventHandlersWithShortcuts, function(eventName) {
1295
+ ko.bindingHandlers[eventName] = {
1296
+ 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
1297
+ var newValueAccessor = function () {
1298
+ var result = {};
1299
+ result[eventName] = valueAccessor();
1300
+ return result;
1301
+ };
1302
+ return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
1303
+ }
1304
+ }
1305
+ });
1306
+
1307
+
1308
+ ko.bindingHandlers['event'] = {
1309
+ 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
1310
+ var eventsToHandle = valueAccessor() || {};
1311
+ for(var eventNameOutsideClosure in eventsToHandle) {
1312
+ (function() {
1313
+ var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
1314
+ if (typeof eventName == "string") {
1315
+ ko.utils.registerEventHandler(element, eventName, function (event) {
1316
+ var handlerReturnValue;
1317
+ var handlerFunction = valueAccessor()[eventName];
1318
+ if (!handlerFunction)
1319
+ return;
1320
+ var allBindings = allBindingsAccessor();
1321
+
1322
+ try {
1323
+ handlerReturnValue = handlerFunction.apply(viewModel, arguments);
1324
+ } finally {
1325
+ if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
1326
+ if (event.preventDefault)
1327
+ event.preventDefault();
1328
+ else
1329
+ event.returnValue = false;
1330
+ }
1331
+ }
1332
+
1333
+ var bubble = allBindings[eventName + 'Bubble'] !== false;
1334
+ if (!bubble) {
1335
+ event.cancelBubble = true;
1336
+ if (event.stopPropagation)
1337
+ event.stopPropagation();
1338
+ }
1339
+ });
1340
+ }
1341
+ })();
1342
+ }
1343
+ }
1344
+ };
1345
+
1346
+ ko.bindingHandlers['submit'] = {
1347
+ 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
1348
+ if (typeof valueAccessor() != "function")
1349
+ throw new Error("The value for a submit binding must be a function to invoke on submit");
1350
+ ko.utils.registerEventHandler(element, "submit", function (event) {
1351
+ var handlerReturnValue;
1352
+ var value = valueAccessor();
1353
+ try { handlerReturnValue = value.call(viewModel, element); }
1354
+ finally {
1355
+ if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
1356
+ if (event.preventDefault)
1357
+ event.preventDefault();
1358
+ else
1359
+ event.returnValue = false;
1360
+ }
1361
+ }
1362
+ });
1363
+ }
1364
+ };
1365
+
1366
+ ko.bindingHandlers['visible'] = {
1367
+ 'update': function (element, valueAccessor) {
1368
+ var value = ko.utils.unwrapObservable(valueAccessor());
1369
+ var isCurrentlyVisible = !(element.style.display == "none");
1370
+ if (value && !isCurrentlyVisible)
1371
+ element.style.display = "";
1372
+ else if ((!value) && isCurrentlyVisible)
1373
+ element.style.display = "none";
1374
+ }
1375
+ }
1376
+
1377
+ ko.bindingHandlers['enable'] = {
1378
+ 'update': function (element, valueAccessor) {
1379
+ var value = ko.utils.unwrapObservable(valueAccessor());
1380
+ if (value && element.disabled)
1381
+ element.removeAttribute("disabled");
1382
+ else if ((!value) && (!element.disabled))
1383
+ element.disabled = true;
1384
+ }
1385
+ };
1386
+
1387
+ ko.bindingHandlers['disable'] = {
1388
+ 'update': function (element, valueAccessor) {
1389
+ ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
1390
+ }
1391
+ };
1392
+
1393
+ ko.bindingHandlers['value'] = {
1394
+ 'init': function (element, valueAccessor, allBindingsAccessor) {
1395
+ // Always catch "change" event; possibly other events too if asked
1396
+ var eventsToCatch = ["change"];
1397
+ var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
1398
+ if (requestedEventsToCatch) {
1399
+ if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
1400
+ requestedEventsToCatch = [requestedEventsToCatch];
1401
+ ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
1402
+ eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
1403
+ }
1404
+
1405
+ ko.utils.arrayForEach(eventsToCatch, function(eventName) {
1406
+ // The syntax "after<eventname>" means "run the handler asynchronously after the event"
1407
+ // This is useful, for example, to catch "keydown" events after the browser has updated the control
1408
+ // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
1409
+ var handleEventAsynchronously = false;
1410
+ if (ko.utils.stringStartsWith(eventName, "after")) {
1411
+ handleEventAsynchronously = true;
1412
+ eventName = eventName.substring("after".length);
1413
+ }
1414
+ var runEventHandler = handleEventAsynchronously ? function(handler) { setTimeout(handler, 0) }
1415
+ : function(handler) { handler() };
1416
+
1417
+ ko.utils.registerEventHandler(element, eventName, function () {
1418
+ runEventHandler(function() {
1419
+ var modelValue = valueAccessor();
1420
+ var elementValue = ko.selectExtensions.readValue(element);
1421
+ if (ko.isWriteableObservable(modelValue))
1422
+ modelValue(elementValue);
1423
+ else {
1424
+ var allBindings = allBindingsAccessor();
1425
+ if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['value'])
1426
+ allBindings['_ko_property_writers']['value'](elementValue);
1427
+ }
1428
+ });
1429
+ });
1430
+ });
1431
+ },
1432
+ 'update': function (element, valueAccessor) {
1433
+ var newValue = ko.utils.unwrapObservable(valueAccessor());
1434
+ var elementValue = ko.selectExtensions.readValue(element);
1435
+ var valueHasChanged = (newValue != elementValue);
1436
+
1437
+ // JavaScript's 0 == "" behavious is unfortunate here as it prevents writing 0 to an empty text box (loose equality suggests the values are the same).
1438
+ // We don't want to do a strict equality comparison as that is more confusing for developers in certain cases, so we specifically special case 0 != "" here.
1439
+ if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
1440
+ valueHasChanged = true;
1441
+
1442
+ if (valueHasChanged) {
1443
+ var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
1444
+ applyValueAction();
1445
+
1446
+ // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
1447
+ // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
1448
+ // to apply the value as well.
1449
+ var alsoApplyAsynchronously = element.tagName == "SELECT";
1450
+ if (alsoApplyAsynchronously)
1451
+ setTimeout(applyValueAction, 0);
1452
+ }
1453
+
1454
+ // For SELECT nodes, you're not allowed to have a model value that disagrees with the UI selection, so if there is a
1455
+ // difference, treat it as a change that should be written back to the model
1456
+ if (element.tagName == "SELECT") {
1457
+ elementValue = ko.selectExtensions.readValue(element);
1458
+ if(elementValue !== newValue)
1459
+ ko.utils.triggerEvent(element, "change");
1460
+ }
1461
+ }
1462
+ };
1463
+
1464
+ ko.bindingHandlers['options'] = {
1465
+ 'update': function (element, valueAccessor, allBindingsAccessor) {
1466
+ if (element.tagName != "SELECT")
1467
+ throw new Error("options binding applies only to SELECT elements");
1468
+
1469
+ var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
1470
+ return node.tagName && node.tagName == "OPTION" && node.selected;
1471
+ }), function (node) {
1472
+ return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
1473
+ });
1474
+ var previousScrollTop = element.scrollTop;
1475
+
1476
+ var value = ko.utils.unwrapObservable(valueAccessor());
1477
+ var selectedValue = element.value;
1478
+ ko.utils.emptyDomNode(element);
1479
+
1480
+ if (value) {
1481
+ var allBindings = allBindingsAccessor();
1482
+ if (typeof value.length != "number")
1483
+ value = [value];
1484
+ if (allBindings['optionsCaption']) {
1485
+ var option = document.createElement("OPTION");
1486
+ option.innerHTML = allBindings['optionsCaption'];
1487
+ ko.selectExtensions.writeValue(option, undefined);
1488
+ element.appendChild(option);
1489
+ }
1490
+ for (var i = 0, j = value.length; i < j; i++) {
1491
+ var option = document.createElement("OPTION");
1492
+
1493
+ // Apply a value to the option element
1494
+ var optionValue = typeof allBindings['optionsValue'] == "string" ? value[i][allBindings['optionsValue']] : value[i];
1495
+ optionValue = ko.utils.unwrapObservable(optionValue);
1496
+ ko.selectExtensions.writeValue(option, optionValue);
1497
+
1498
+ // Apply some text to the option element
1499
+ var optionsTextValue = allBindings['optionsText'];
1500
+ if (typeof optionsTextValue == "function")
1501
+ optionText = optionsTextValue(value[i]); // Given a function; run it against the data value
1502
+ else if (typeof optionsTextValue == "string")
1503
+ optionText = value[i][optionsTextValue]; // Given a string; treat it as a property name on the data value
1504
+ else
1505
+ optionText = optionValue; // Given no optionsText arg; use the data value itself
1506
+ if ((optionText === null) || (optionText === undefined))
1507
+ optionText = "";
1508
+ optionText = ko.utils.unwrapObservable(optionText).toString();
1509
+ typeof option.innerText == "string" ? option.innerText = optionText
1510
+ : option.textContent = optionText;
1511
+
1512
+ element.appendChild(option);
1513
+ }
1514
+
1515
+ // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
1516
+ // That's why we first added them without selection. Now it's time to set the selection.
1517
+ var newOptions = element.getElementsByTagName("OPTION");
1518
+ var countSelectionsRetained = 0;
1519
+ for (var i = 0, j = newOptions.length; i < j; i++) {
1520
+ if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
1521
+ ko.utils.setOptionNodeSelectionState(newOptions[i], true);
1522
+ countSelectionsRetained++;
1523
+ }
1524
+ }
1525
+
1526
+ if (previousScrollTop)
1527
+ element.scrollTop = previousScrollTop;
1528
+ }
1529
+ }
1530
+ };
1531
+ ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.bindingHandlers.options.optionValueDomData__';
1532
+
1533
+ ko.bindingHandlers['selectedOptions'] = {
1534
+ getSelectedValuesFromSelectNode: function (selectNode) {
1535
+ var result = [];
1536
+ var nodes = selectNode.childNodes;
1537
+ for (var i = 0, j = nodes.length; i < j; i++) {
1538
+ var node = nodes[i];
1539
+ if ((node.tagName == "OPTION") && node.selected)
1540
+ result.push(ko.selectExtensions.readValue(node));
1541
+ }
1542
+ return result;
1543
+ },
1544
+ 'init': function (element, valueAccessor, allBindingsAccessor) {
1545
+ ko.utils.registerEventHandler(element, "change", function () {
1546
+ var value = valueAccessor();
1547
+ if (ko.isWriteableObservable(value))
1548
+ value(ko.bindingHandlers['selectedOptions'].getSelectedValuesFromSelectNode(this));
1549
+ else {
1550
+ var allBindings = allBindingsAccessor();
1551
+ if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['value'])
1552
+ allBindings['_ko_property_writers']['value'](ko.bindingHandlers['selectedOptions'].getSelectedValuesFromSelectNode(this));
1553
+ }
1554
+ });
1555
+ },
1556
+ 'update': function (element, valueAccessor) {
1557
+ if (element.tagName != "SELECT")
1558
+ throw new Error("values binding applies only to SELECT elements");
1559
+
1560
+ var newValue = ko.utils.unwrapObservable(valueAccessor());
1561
+ if (newValue && typeof newValue.length == "number") {
1562
+ var nodes = element.childNodes;
1563
+ for (var i = 0, j = nodes.length; i < j; i++) {
1564
+ var node = nodes[i];
1565
+ if (node.tagName == "OPTION")
1566
+ ko.utils.setOptionNodeSelectionState(node, ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0);
1567
+ }
1568
+ }
1569
+ }
1570
+ };
1571
+
1572
+ ko.bindingHandlers['text'] = {
1573
+ 'update': function (element, valueAccessor) {
1574
+ var value = ko.utils.unwrapObservable(valueAccessor());
1575
+ if ((value === null) || (value === undefined))
1576
+ value = "";
1577
+ typeof element.innerText == "string" ? element.innerText = value
1578
+ : element.textContent = value;
1579
+ }
1580
+ };
1581
+
1582
+ ko.bindingHandlers['html'] = {
1583
+ 'update': function (element, valueAccessor) {
1584
+ var value = ko.utils.unwrapObservable(valueAccessor());
1585
+ ko.utils.setHtml(element, value);
1586
+ }
1587
+ };
1588
+
1589
+ ko.bindingHandlers['css'] = {
1590
+ 'update': function (element, valueAccessor) {
1591
+ var value = ko.utils.unwrapObservable(valueAccessor() || {});
1592
+ for (var className in value) {
1593
+ if (typeof className == "string") {
1594
+ var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
1595
+ ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
1596
+ }
1597
+ }
1598
+ }
1599
+ };
1600
+
1601
+ ko.bindingHandlers['style'] = {
1602
+ 'update': function (element, valueAccessor) {
1603
+ var value = ko.utils.unwrapObservable(valueAccessor() || {});
1604
+ for (var styleName in value) {
1605
+ if (typeof styleName == "string") {
1606
+ var styleValue = ko.utils.unwrapObservable(value[styleName]);
1607
+ element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
1608
+ }
1609
+ }
1610
+ }
1611
+ };
1612
+
1613
+ ko.bindingHandlers['uniqueName'] = {
1614
+ 'init': function (element, valueAccessor) {
1615
+ if (valueAccessor()) {
1616
+ element.name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
1617
+
1618
+ // Workaround IE 6 issue - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
1619
+ if (ko.utils.isIe6)
1620
+ element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
1621
+ }
1622
+ }
1623
+ };
1624
+ ko.bindingHandlers['uniqueName'].currentIndex = 0;
1625
+
1626
+ ko.bindingHandlers['checked'] = {
1627
+ 'init': function (element, valueAccessor, allBindingsAccessor) {
1628
+ var updateHandler = function() {
1629
+ var valueToWrite;
1630
+ if (element.type == "checkbox") {
1631
+ valueToWrite = element.checked;
1632
+ } else if ((element.type == "radio") && (element.checked)) {
1633
+ valueToWrite = element.value;
1634
+ } else {
1635
+ return; // "checked" binding only responds to checkboxes and selected radio buttons
1636
+ }
1637
+
1638
+ var modelValue = valueAccessor();
1639
+ if ((element.type == "checkbox") && (ko.utils.unwrapObservable(modelValue) instanceof Array)) {
1640
+ // For checkboxes bound to an array, we add/remove the checkbox value to that array
1641
+ // This works for both observable and non-observable arrays
1642
+ var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.unwrapObservable(modelValue), element.value);
1643
+ if (element.checked && (existingEntryIndex < 0))
1644
+ modelValue.push(element.value);
1645
+ else if ((!element.checked) && (existingEntryIndex >= 0))
1646
+ modelValue.splice(existingEntryIndex, 1);
1647
+ } else if (ko.isWriteableObservable(modelValue)) {
1648
+ if (modelValue() !== valueToWrite) { // Suppress repeated events when there's nothing new to notify (some browsers raise them)
1649
+ modelValue(valueToWrite);
1650
+ }
1651
+ } else {
1652
+ var allBindings = allBindingsAccessor();
1653
+ if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['checked']) {
1654
+ allBindings['_ko_property_writers']['checked'](valueToWrite);
1655
+ }
1656
+ }
1657
+ };
1658
+ ko.utils.registerEventHandler(element, "click", updateHandler);
1659
+
1660
+ // IE 6 won't allow radio buttons to be selected unless they have a name
1661
+ if ((element.type == "radio") && !element.name)
1662
+ ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
1663
+ },
1664
+ 'update': function (element, valueAccessor) {
1665
+ var value = ko.utils.unwrapObservable(valueAccessor());
1666
+
1667
+ if (element.type == "checkbox") {
1668
+ if (value instanceof Array) {
1669
+ // When bound to an array, the checkbox being checked represents its value being present in that array
1670
+ element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
1671
+ } else {
1672
+ // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
1673
+ element.checked = value;
1674
+ }
1675
+
1676
+ // Workaround for IE 6 bug - it fails to apply checked state to dynamically-created checkboxes if you merely say "element.checked = true"
1677
+ if (value && ko.utils.isIe6)
1678
+ element.mergeAttributes(document.createElement("<input type='checkbox' checked='checked' />"), false);
1679
+ } else if (element.type == "radio") {
1680
+ element.checked = (element.value == value);
1681
+
1682
+ // Workaround for IE 6/7 bug - it fails to apply checked state to dynamically-created radio buttons if you merely say "element.checked = true"
1683
+ if ((element.value == value) && (ko.utils.isIe6 || ko.utils.isIe7))
1684
+ element.mergeAttributes(document.createElement("<input type='radio' checked='checked' />"), false);
1685
+ }
1686
+ }
1687
+ };
1688
+
1689
+ ko.bindingHandlers['attr'] = {
1690
+ update: function(element, valueAccessor, allBindingsAccessor) {
1691
+ var value = ko.utils.unwrapObservable(valueAccessor()) || {};
1692
+ for (var attrName in value) {
1693
+ if (typeof attrName == "string") {
1694
+ var attrValue = ko.utils.unwrapObservable(value[attrName]);
1695
+
1696
+ // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
1697
+ // when someProp is a "no value"-like value (strictly null, false, or undefined)
1698
+ // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
1699
+ if ((attrValue === false) || (attrValue === null) || (attrValue === undefined))
1700
+ element.removeAttribute(attrName);
1701
+ else
1702
+ element.setAttribute(attrName, attrValue.toString());
1703
+ }
1704
+ }
1705
+ }
1706
+ };
1707
+ ko.templateEngine = function () {
1708
+ this['renderTemplate'] = function (templateName, data, options) {
1709
+ throw "Override renderTemplate in your ko.templateEngine subclass";
1710
+ },
1711
+ this['isTemplateRewritten'] = function (templateName) {
1712
+ throw "Override isTemplateRewritten in your ko.templateEngine subclass";
1713
+ },
1714
+ this['rewriteTemplate'] = function (templateName, rewriterCallback) {
1715
+ throw "Override rewriteTemplate in your ko.templateEngine subclass";
1716
+ },
1717
+ this['createJavaScriptEvaluatorBlock'] = function (script) {
1718
+ throw "Override createJavaScriptEvaluatorBlock in your ko.templateEngine subclass";
1719
+ }
1720
+ };
1721
+
1722
+ ko.exportSymbol('ko.templateEngine', ko.templateEngine);
1723
+
1724
+ ko.templateRewriting = (function () {
1725
+ var memoizeBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
1726
+
1727
+ return {
1728
+ ensureTemplateIsRewritten: function (template, templateEngine) {
1729
+ if (!templateEngine['isTemplateRewritten'](template))
1730
+ templateEngine['rewriteTemplate'](template, function (htmlString) {
1731
+ return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
1732
+ });
1733
+ },
1734
+
1735
+ memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
1736
+ return htmlString.replace(memoizeBindingAttributeSyntaxRegex, function () {
1737
+ var tagToRetain = arguments[1];
1738
+ var dataBindAttributeValue = arguments[6];
1739
+
1740
+ dataBindAttributeValue = ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson(dataBindAttributeValue);
1741
+
1742
+ // For no obvious reason, Opera fails to evaluate dataBindAttributeValue unless it's wrapped in an additional anonymous function,
1743
+ // even though Opera's built-in debugger can evaluate it anyway. No other browser requires this extra indirection.
1744
+ var applyBindingsToNextSiblingScript = "ko.templateRewriting.applyMemoizedBindingsToNextSibling(function() { \
1745
+ return (function() { return { " + dataBindAttributeValue + " } })() \
1746
+ })";
1747
+ return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
1748
+ });
1749
+ },
1750
+
1751
+ applyMemoizedBindingsToNextSibling: function (bindings) {
1752
+ return ko.memoization.memoize(function (domNode, viewModel) {
1753
+ if (domNode.nextSibling)
1754
+ ko.applyBindingsToNode(domNode.nextSibling, bindings, viewModel);
1755
+ });
1756
+ }
1757
+ }
1758
+ })();
1759
+
1760
+ ko.exportSymbol('ko.templateRewriting', ko.templateRewriting);
1761
+ ko.exportSymbol('ko.templateRewriting.applyMemoizedBindingsToNextSibling', ko.templateRewriting.applyMemoizedBindingsToNextSibling); // Exported only because it has to be referenced by string lookup from within rewritten template
1762
+
1763
+ (function () {
1764
+ var _templateEngine;
1765
+ ko.setTemplateEngine = function (templateEngine) {
1766
+ if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
1767
+ throw "templateEngine must inherit from ko.templateEngine";
1768
+ _templateEngine = templateEngine;
1769
+ }
1770
+
1771
+ function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
1772
+ return nodeOrNodeArray.nodeType ? nodeOrNodeArray
1773
+ : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
1774
+ : null;
1775
+ }
1776
+
1777
+ function executeTemplate(targetNodeOrNodeArray, renderMode, template, data, options) {
1778
+ var dataForTemplate = ko.utils.unwrapObservable(data);
1779
+
1780
+ options = options || {};
1781
+ var templateEngineToUse = (options['templateEngine'] || _templateEngine);
1782
+ ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse);
1783
+ var renderedNodesArray = templateEngineToUse['renderTemplate'](template, dataForTemplate, options);
1784
+
1785
+ // Loosely check result is an array of DOM nodes
1786
+ if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
1787
+ throw "Template engine must return an array of DOM nodes";
1788
+
1789
+ if (renderedNodesArray)
1790
+ ko.utils.arrayForEach(renderedNodesArray, function (renderedNode) {
1791
+ ko.memoization.unmemoizeDomNodeAndDescendants(renderedNode, [data]);
1792
+ });
1793
+
1794
+ switch (renderMode) {
1795
+ case "replaceChildren": ko.utils.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray); break;
1796
+ case "replaceNode": ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray); break;
1797
+ case "ignoreTargetNode": break;
1798
+ default: throw new Error("Unknown renderMode: " + renderMode);
1799
+ }
1800
+
1801
+ if (options['afterRender'])
1802
+ options['afterRender'](renderedNodesArray, data);
1803
+
1804
+ return renderedNodesArray;
1805
+ }
1806
+
1807
+ ko.renderTemplate = function (template, data, options, targetNodeOrNodeArray, renderMode) {
1808
+ options = options || {};
1809
+ if ((options['templateEngine'] || _templateEngine) == undefined)
1810
+ throw "Set a template engine before calling renderTemplate";
1811
+ renderMode = renderMode || "replaceChildren";
1812
+
1813
+ if (targetNodeOrNodeArray) {
1814
+ var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
1815
+
1816
+ var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
1817
+ var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
1818
+
1819
+ return new ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
1820
+ function () {
1821
+ // Support selecting template as a function of the data being rendered
1822
+ var templateName = typeof(template) == 'function' ? template(data) : template;
1823
+
1824
+ var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, data, options);
1825
+ if (renderMode == "replaceNode") {
1826
+ targetNodeOrNodeArray = renderedNodesArray;
1827
+ firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
1828
+ }
1829
+ },
1830
+ null,
1831
+ { 'disposeWhen': whenToDispose, 'disposeWhenNodeIsRemoved': activelyDisposeWhenNodeIsRemoved }
1832
+ );
1833
+ } else {
1834
+ // 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
1835
+ return ko.memoization.memoize(function (domNode) {
1836
+ ko.renderTemplate(template, data, options, domNode, "replaceNode");
1837
+ });
1838
+ }
1839
+ };
1840
+
1841
+ ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode) {
1842
+ return new ko.dependentObservable(function () {
1843
+ var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
1844
+ if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
1845
+ unwrappedArray = [unwrappedArray];
1846
+
1847
+ // Filter out any entries marked as destroyed
1848
+ var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
1849
+ return options['includeDestroyed'] || !item['_destroy'];
1850
+ });
1851
+
1852
+ ko.utils.setDomNodeChildrenFromArrayMapping(targetNode, filteredArray, function (arrayValue) {
1853
+ // Support selecting template as a function of the data being rendered
1854
+ var templateName = typeof(template) == 'function' ? template(arrayValue) : template;
1855
+
1856
+ return executeTemplate(null, "ignoreTargetNode", templateName, arrayValue, options);
1857
+ }, options);
1858
+ }, null, { 'disposeWhenNodeIsRemoved': targetNode });
1859
+ };
1860
+
1861
+ var templateSubscriptionDomDataKey = '__ko__templateSubscriptionDomDataKey__';
1862
+ function disposeOldSubscriptionAndStoreNewOne(element, newSubscription) {
1863
+ var oldSubscription = ko.utils.domData.get(element, templateSubscriptionDomDataKey);
1864
+ if (oldSubscription && (typeof(oldSubscription.dispose) == 'function'))
1865
+ oldSubscription.dispose();
1866
+ ko.utils.domData.set(element, templateSubscriptionDomDataKey, newSubscription);
1867
+ }
1868
+
1869
+ ko.bindingHandlers['template'] = {
1870
+ 'update': function (element, valueAccessor, allBindingsAccessor, viewModel) {
1871
+ var bindingValue = ko.utils.unwrapObservable(valueAccessor());
1872
+ var templateName = typeof bindingValue == "string" ? bindingValue : bindingValue.name;
1873
+
1874
+ var templateSubscription;
1875
+ if (typeof bindingValue['foreach'] != "undefined") {
1876
+ // Render once for each data point
1877
+ templateSubscription = ko.renderTemplateForEach(templateName, bindingValue['foreach'] || [], { 'templateOptions': bindingValue['templateOptions'], 'afterAdd': bindingValue['afterAdd'], 'beforeRemove': bindingValue['beforeRemove'], 'includeDestroyed': bindingValue['includeDestroyed'], 'afterRender': bindingValue['afterRender'] }, element);
1878
+ }
1879
+ else {
1880
+ // Render once for this single data point (or use the viewModel if no data was provided)
1881
+ var templateData = bindingValue['data'];
1882
+ templateSubscription = ko.renderTemplate(templateName, typeof templateData == "undefined" ? viewModel : templateData, { 'templateOptions': bindingValue['templateOptions'], 'afterRender': bindingValue['afterRender'] }, element);
1883
+ }
1884
+
1885
+ // It only makes sense to have a single template subscription per element (otherwise which one should have its output displayed?)
1886
+ disposeOldSubscriptionAndStoreNewOne(element, templateSubscription);
1887
+ }
1888
+ };
1889
+ })();
1890
+
1891
+ ko.exportSymbol('ko.setTemplateEngine', ko.setTemplateEngine);
1892
+ ko.exportSymbol('ko.renderTemplate', ko.renderTemplate);
1893
+
1894
+ (function () {
1895
+ // Simple calculation based on Levenshtein distance.
1896
+ function calculateEditDistanceMatrix(oldArray, newArray, maxAllowedDistance) {
1897
+ var distances = [];
1898
+ for (var i = 0; i <= newArray.length; i++)
1899
+ distances[i] = [];
1900
+
1901
+ // Top row - transform old array into empty array via deletions
1902
+ for (var i = 0, j = Math.min(oldArray.length, maxAllowedDistance); i <= j; i++)
1903
+ distances[0][i] = i;
1904
+
1905
+ // Left row - transform empty array into new array via additions
1906
+ for (var i = 1, j = Math.min(newArray.length, maxAllowedDistance); i <= j; i++) {
1907
+ distances[i][0] = i;
1908
+ }
1909
+
1910
+ // Fill out the body of the array
1911
+ var oldIndex, oldIndexMax = oldArray.length, newIndex, newIndexMax = newArray.length;
1912
+ var distanceViaAddition, distanceViaDeletion;
1913
+ for (oldIndex = 1; oldIndex <= oldIndexMax; oldIndex++) {
1914
+ var newIndexMinForRow = Math.max(1, oldIndex - maxAllowedDistance);
1915
+ var newIndexMaxForRow = Math.min(newIndexMax, oldIndex + maxAllowedDistance);
1916
+ for (newIndex = newIndexMinForRow; newIndex <= newIndexMaxForRow; newIndex++) {
1917
+ if (oldArray[oldIndex - 1] === newArray[newIndex - 1])
1918
+ distances[newIndex][oldIndex] = distances[newIndex - 1][oldIndex - 1];
1919
+ else {
1920
+ var northDistance = distances[newIndex - 1][oldIndex] === undefined ? Number.MAX_VALUE : distances[newIndex - 1][oldIndex] + 1;
1921
+ var westDistance = distances[newIndex][oldIndex - 1] === undefined ? Number.MAX_VALUE : distances[newIndex][oldIndex - 1] + 1;
1922
+ distances[newIndex][oldIndex] = Math.min(northDistance, westDistance);
1923
+ }
1924
+ }
1925
+ }
1926
+
1927
+ return distances;
1928
+ }
1929
+
1930
+ function findEditScriptFromEditDistanceMatrix(editDistanceMatrix, oldArray, newArray) {
1931
+ var oldIndex = oldArray.length;
1932
+ var newIndex = newArray.length;
1933
+ var editScript = [];
1934
+ var maxDistance = editDistanceMatrix[newIndex][oldIndex];
1935
+ if (maxDistance === undefined)
1936
+ return null; // maxAllowedDistance must be too small
1937
+ while ((oldIndex > 0) || (newIndex > 0)) {
1938
+ var me = editDistanceMatrix[newIndex][oldIndex];
1939
+ var distanceViaAdd = (newIndex > 0) ? editDistanceMatrix[newIndex - 1][oldIndex] : maxDistance + 1;
1940
+ var distanceViaDelete = (oldIndex > 0) ? editDistanceMatrix[newIndex][oldIndex - 1] : maxDistance + 1;
1941
+ var distanceViaRetain = (newIndex > 0) && (oldIndex > 0) ? editDistanceMatrix[newIndex - 1][oldIndex - 1] : maxDistance + 1;
1942
+ if ((distanceViaAdd === undefined) || (distanceViaAdd < me - 1)) distanceViaAdd = maxDistance + 1;
1943
+ if ((distanceViaDelete === undefined) || (distanceViaDelete < me - 1)) distanceViaDelete = maxDistance + 1;
1944
+ if (distanceViaRetain < me - 1) distanceViaRetain = maxDistance + 1;
1945
+
1946
+ if ((distanceViaAdd <= distanceViaDelete) && (distanceViaAdd < distanceViaRetain)) {
1947
+ editScript.push({ status: "added", value: newArray[newIndex - 1] });
1948
+ newIndex--;
1949
+ } else if ((distanceViaDelete < distanceViaAdd) && (distanceViaDelete < distanceViaRetain)) {
1950
+ editScript.push({ status: "deleted", value: oldArray[oldIndex - 1] });
1951
+ oldIndex--;
1952
+ } else {
1953
+ editScript.push({ status: "retained", value: oldArray[oldIndex - 1] });
1954
+ newIndex--;
1955
+ oldIndex--;
1956
+ }
1957
+ }
1958
+ return editScript.reverse();
1959
+ }
1960
+
1961
+ ko.utils.compareArrays = function (oldArray, newArray, maxEditsToConsider) {
1962
+ if (maxEditsToConsider === undefined) {
1963
+ return ko.utils.compareArrays(oldArray, newArray, 1) // First consider likely case where there is at most one edit (very fast)
1964
+ || ko.utils.compareArrays(oldArray, newArray, 10) // If that fails, account for a fair number of changes while still being fast
1965
+ || ko.utils.compareArrays(oldArray, newArray, Number.MAX_VALUE); // Ultimately give the right answer, even though it may take a long time
1966
+ } else {
1967
+ oldArray = oldArray || [];
1968
+ newArray = newArray || [];
1969
+ var editDistanceMatrix = calculateEditDistanceMatrix(oldArray, newArray, maxEditsToConsider);
1970
+ return findEditScriptFromEditDistanceMatrix(editDistanceMatrix, oldArray, newArray);
1971
+ }
1972
+ };
1973
+ })();
1974
+
1975
+ ko.exportSymbol('ko.utils.compareArrays', ko.utils.compareArrays);
1976
+
1977
+ (function () {
1978
+ // Objective:
1979
+ // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
1980
+ // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
1981
+ // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
1982
+ // 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
1983
+ // previously mapped - retain those nodes, and just insert/delete other ones
1984
+
1985
+ function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap) {
1986
+ // Map this array value inside a dependentObservable so we re-map when any dependency changes
1987
+ var mappedNodes = [];
1988
+ var dependentObservable = ko.dependentObservable(function() {
1989
+ var newMappedNodes = mapping(valueToMap) || [];
1990
+
1991
+ // On subsequent evaluations, just replace the previously-inserted DOM nodes
1992
+ if (mappedNodes.length > 0)
1993
+ ko.utils.replaceDomNodes(mappedNodes, newMappedNodes);
1994
+
1995
+ // Replace the contents of the mappedNodes array, thereby updating the record
1996
+ // of which nodes would be deleted if valueToMap was itself later removed
1997
+ mappedNodes.splice(0, mappedNodes.length);
1998
+ ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
1999
+ }, null, { 'disposeWhenNodeIsRemoved': containerNode, 'disposeWhen': function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
2000
+ return { mappedNodes : mappedNodes, dependentObservable : dependentObservable };
2001
+ }
2002
+
2003
+ ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options) {
2004
+ // Compare the provided array against the previous one
2005
+ array = array || [];
2006
+ options = options || {};
2007
+ var isFirstExecution = ko.utils.domData.get(domNode, "setDomNodeChildrenFromArrayMapping_lastMappingResult") === undefined;
2008
+ var lastMappingResult = ko.utils.domData.get(domNode, "setDomNodeChildrenFromArrayMapping_lastMappingResult") || [];
2009
+ var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
2010
+ var editScript = ko.utils.compareArrays(lastArray, array);
2011
+
2012
+ // Build the new mapping result
2013
+ var newMappingResult = [];
2014
+ var lastMappingResultIndex = 0;
2015
+ var nodesToDelete = [];
2016
+ var nodesAdded = [];
2017
+ var insertAfterNode = null;
2018
+ for (var i = 0, j = editScript.length; i < j; i++) {
2019
+ switch (editScript[i].status) {
2020
+ case "retained":
2021
+ // Just keep the information - don't touch the nodes
2022
+ var dataToRetain = lastMappingResult[lastMappingResultIndex];
2023
+ newMappingResult.push(dataToRetain);
2024
+ if (dataToRetain.domNodes.length > 0)
2025
+ insertAfterNode = dataToRetain.domNodes[dataToRetain.domNodes.length - 1];
2026
+ lastMappingResultIndex++;
2027
+ break;
2028
+
2029
+ case "deleted":
2030
+ // Stop tracking changes to the mapping for these nodes
2031
+ lastMappingResult[lastMappingResultIndex].dependentObservable.dispose();
2032
+
2033
+ // Queue these nodes for later removal
2034
+ ko.utils.arrayForEach(lastMappingResult[lastMappingResultIndex].domNodes, function (node) {
2035
+ nodesToDelete.push({
2036
+ element: node,
2037
+ index: i,
2038
+ value: editScript[i].value
2039
+ });
2040
+ insertAfterNode = node;
2041
+ });
2042
+ lastMappingResultIndex++;
2043
+ break;
2044
+
2045
+ case "added":
2046
+ var mapData = mapNodeAndRefreshWhenChanged(domNode, mapping, editScript[i].value);
2047
+ var mappedNodes = mapData.mappedNodes;
2048
+
2049
+ // On the first evaluation, insert the nodes at the current insertion point
2050
+ newMappingResult.push({ arrayEntry: editScript[i].value, domNodes: mappedNodes, dependentObservable: mapData.dependentObservable });
2051
+ for (var nodeIndex = 0, nodeIndexMax = mappedNodes.length; nodeIndex < nodeIndexMax; nodeIndex++) {
2052
+ var node = mappedNodes[nodeIndex];
2053
+ nodesAdded.push({
2054
+ element: node,
2055
+ index: i,
2056
+ value: editScript[i].value
2057
+ });
2058
+ if (insertAfterNode == null) {
2059
+ // Insert at beginning
2060
+ if (domNode.firstChild)
2061
+ domNode.insertBefore(node, domNode.firstChild);
2062
+ else
2063
+ domNode.appendChild(node);
2064
+ } else {
2065
+ // Insert after insertion point
2066
+ if (insertAfterNode.nextSibling)
2067
+ domNode.insertBefore(node, insertAfterNode.nextSibling);
2068
+ else
2069
+ domNode.appendChild(node);
2070
+ }
2071
+ insertAfterNode = node;
2072
+ }
2073
+ break;
2074
+ }
2075
+ }
2076
+
2077
+ ko.utils.arrayForEach(nodesToDelete, function (node) { ko.cleanNode(node.element) });
2078
+
2079
+ var invokedBeforeRemoveCallback = false;
2080
+ if (!isFirstExecution) {
2081
+ if (options['afterAdd']) {
2082
+ for (var i = 0; i < nodesAdded.length; i++)
2083
+ options['afterAdd'](nodesAdded[i].element, nodesAdded[i].index, nodesAdded[i].value);
2084
+ }
2085
+ if (options['beforeRemove']) {
2086
+ for (var i = 0; i < nodesToDelete.length; i++)
2087
+ options['beforeRemove'](nodesToDelete[i].element, nodesToDelete[i].index, nodesToDelete[i].value);
2088
+ invokedBeforeRemoveCallback = true;
2089
+ }
2090
+ }
2091
+ if (!invokedBeforeRemoveCallback)
2092
+ ko.utils.arrayForEach(nodesToDelete, function (node) {
2093
+ if (node.element.parentNode)
2094
+ node.element.parentNode.removeChild(node.element);
2095
+ });
2096
+
2097
+ // Store a copy of the array items we just considered so we can difference it next time
2098
+ ko.utils.domData.set(domNode, "setDomNodeChildrenFromArrayMapping_lastMappingResult", newMappingResult);
2099
+ }
2100
+ })();
2101
+
2102
+ ko.exportSymbol('ko.utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
2103
+
2104
+ ko.jqueryTmplTemplateEngine = function () {
2105
+ // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
2106
+ // doesn't expose a version number, so we have to infer it.
2107
+ this.jQueryTmplVersion = (function() {
2108
+ if ((typeof(jQuery) == "undefined") || !jQuery['tmpl'])
2109
+ return 0;
2110
+ // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
2111
+ if (jQuery['tmpl']['tag']) {
2112
+ if (jQuery['tmpl']['tag']['tmpl'] && jQuery['tmpl']['tag']['tmpl']['open']) {
2113
+ if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
2114
+ return 3; // Since 1.0.0pre, custom tags should append markup to an array called "__"
2115
+ }
2116
+ }
2117
+ return 2; // Prior to 1.0.0pre, custom tags should append markup to an array called "_"
2118
+ }
2119
+ return 1; // Very old version doesn't have an extensible tag system
2120
+ })();
2121
+
2122
+ this['getTemplateNode'] = function (template) {
2123
+ var templateNode = document.getElementById(template);
2124
+ if (templateNode == null)
2125
+ throw new Error("Cannot find template with ID=" + template);
2126
+ return templateNode;
2127
+ }
2128
+
2129
+ // These two only needed for jquery-tmpl v1
2130
+ var aposMarker = "__ko_apos__";
2131
+ var aposRegex = new RegExp(aposMarker, "g");
2132
+
2133
+ this['renderTemplate'] = function (templateId, data, options) {
2134
+ options = options || {};
2135
+ if (this.jQueryTmplVersion == 0)
2136
+ throw new Error("jquery.tmpl not detected.\nTo use KO's default template engine, reference jQuery and jquery.tmpl. See Knockout installation documentation for more details.");
2137
+
2138
+ if (this.jQueryTmplVersion == 1) {
2139
+ // jquery.tmpl v1 doesn't like it if the template returns just text content or nothing - it only likes you to return DOM nodes.
2140
+ // To make things more flexible, we can wrap the whole template in a <script> node so that jquery.tmpl just processes it as
2141
+ // text and doesn't try to parse the output. Then, since jquery.tmpl has jQuery as a dependency anyway, we can use jQuery to
2142
+ // parse that text into a document fragment using jQuery.clean().
2143
+ var templateTextInWrapper = "<script type=\"text/html\">" + this['getTemplateNode'](templateId).text + "</script>";
2144
+ var renderedMarkupInWrapper = jQuery['tmpl'](templateTextInWrapper, data);
2145
+ var renderedMarkup = renderedMarkupInWrapper[0].text.replace(aposRegex, "'");;
2146
+ return jQuery['clean']([renderedMarkup], document);
2147
+ }
2148
+
2149
+ // It's easier with jquery.tmpl v2 and later - it handles any DOM structure
2150
+ if (!(templateId in jQuery['template'])) {
2151
+ // Precache a precompiled version of this template (don't want to reparse on every render)
2152
+ var templateText = this['getTemplateNode'](templateId).text;
2153
+ jQuery['template'](templateId, templateText);
2154
+ }
2155
+ data = [data]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
2156
+
2157
+ var resultNodes = jQuery['tmpl'](templateId, data, options['templateOptions']);
2158
+ resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
2159
+ jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
2160
+ return resultNodes;
2161
+ },
2162
+
2163
+ this['isTemplateRewritten'] = function (templateId) {
2164
+ // It must already be rewritten if we've already got a cached version of it
2165
+ // (this optimisation helps on IE < 9, because it greatly reduces the number of getElementById calls)
2166
+ if (templateId in jQuery['template'])
2167
+ return true;
2168
+
2169
+ return this['getTemplateNode'](templateId).isRewritten === true;
2170
+ },
2171
+
2172
+ this['rewriteTemplate'] = function (template, rewriterCallback) {
2173
+ var templateNode = this['getTemplateNode'](template);
2174
+ var rewritten = rewriterCallback(templateNode.text);
2175
+
2176
+ if (this.jQueryTmplVersion == 1) {
2177
+ // jquery.tmpl v1 falls over if you use single-quotes, so replace these with a temporary marker for template rendering,
2178
+ // and then replace back after the template was rendered. This is slightly complicated by the fact that we must not interfere
2179
+ // with any code blocks - only replace apos characters outside code blocks.
2180
+ rewritten = ko.utils.stringTrim(rewritten);
2181
+ rewritten = rewritten.replace(/([\s\S]*?)(\${[\s\S]*?}|{{[\=a-z][\s\S]*?}}|$)/g, function(match) {
2182
+ // Called for each non-code-block followed by a code block (or end of template)
2183
+ var nonCodeSnippet = arguments[1];
2184
+ var codeSnippet = arguments[2];
2185
+ return nonCodeSnippet.replace(/\'/g, aposMarker) + codeSnippet;
2186
+ });
2187
+ }
2188
+
2189
+ templateNode.text = rewritten;
2190
+ templateNode.isRewritten = true;
2191
+ },
2192
+
2193
+ this['createJavaScriptEvaluatorBlock'] = function (script) {
2194
+ if (this.jQueryTmplVersion == 1)
2195
+ return "{{= " + script + "}}";
2196
+
2197
+ // From v2, jquery-tmpl does some parameter parsing that fails on nontrivial expressions.
2198
+ // Prevent it from messing with the code by wrapping it in a further function.
2199
+ return "{{ko_code ((function() { return " + script + " })()) }}";
2200
+ },
2201
+
2202
+ this.addTemplate = function (templateName, templateMarkup) {
2203
+ document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
2204
+ }
2205
+ ko.exportProperty(this, 'addTemplate', this.addTemplate);
2206
+
2207
+ if (this.jQueryTmplVersion > 1) {
2208
+ jQuery['tmpl']['tag']['ko_code'] = {
2209
+ open: (this.jQueryTmplVersion < 3 ? "_" : "__") + ".push($1 || '');"
2210
+ };
2211
+ }
2212
+ };
2213
+
2214
+ ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
2215
+
2216
+ // Use this one by default
2217
+ ko.setTemplateEngine(new ko.jqueryTmplTemplateEngine());
2218
+
2219
+ ko.exportSymbol('ko.jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);})(window);