epyce 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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);