/Scripts/knockout-2.2.0.debug.js

https://bitbucket.org/parivedasolutions/attributeroutingtest · JavaScript · 3577 lines · 2979 code · 303 blank · 295 comment · 610 complexity · 53ace77b7c95fc1ec9887939788244b0 MD5 · raw file

Large files are truncated click here to view the full file

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