/assets/vendor/ko/knockout.js

https://bitbucket.org/grid13/yii-knockout-jsplum · JavaScript · 3564 lines · 2969 code · 303 blank · 292 comment · 608 complexity · 366ab33cc62e55a46125a00436919756 MD5 · raw file

Large files are truncated click here to view the full file

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