PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Backboned/Scripts/knockback.js

https://bitbucket.org/mbinot/backboned
JavaScript | 1561 lines | 1351 code | 143 blank | 67 comment | 405 complexity | 50bdc146248973ce1f12c0610d4b1738 MD5 | raw file
  1. /*
  2. knockback.js 0.16.6 (full version)
  3. (c) 2011, 2012 Kevin Malakoff - http://kmalakoff.github.com/knockback/
  4. License: MIT (http://www.opensource.org/licenses/mit-license.php)
  5. Dependencies: Knockout.js, Backbone.js, and Underscore.js.
  6. */
  7. (function() {
  8. return (function(factory) {
  9. // AMD
  10. if (typeof define === 'function' && define.amd) {
  11. return define('knockback', ['underscore', 'backbone', 'knockout'], factory);
  12. }
  13. // CommonJS/NodeJS or No Loader
  14. else {
  15. return factory.call(this);
  16. }
  17. })(function() {// Generated by CoffeeScript 1.3.3
  18. /*
  19. knockback-core.js 0.16.6
  20. (c) 2011, 2012 Kevin Malakoff.
  21. Knockback.js is freely distributable under the MIT license.
  22. See the following for full license details:
  23. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  24. Dependencies: Knockout.js, Backbone.js, and Underscore.js.
  25. */
  26. var Backbone, EMAIL_REGEXP, KB_TYPE_ARRAY, KB_TYPE_COLLECTION, KB_TYPE_MODEL, KB_TYPE_SIMPLE, KB_TYPE_UNKNOWN, NUMBER_REGEXP, URL_REGEXP, addStatisticsEvent, arraySlice, arraySplice, buildEvalWithinScopeFunction, collapseOptions, kb, ko, legacyWarning, onReady, throwMissing, throwUnexpected, _, _argumentsAddKey, _unwrapModels, _wrappedKey,
  27. __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
  28. kb = (function() {
  29. function kb() {}
  30. kb.VERSION = '0.16.6';
  31. kb.TYPE_UNKNOWN = 0;
  32. kb.TYPE_SIMPLE = 1;
  33. kb.TYPE_ARRAY = 2;
  34. kb.TYPE_MODEL = 3;
  35. kb.TYPE_COLLECTION = 4;
  36. kb.release = function(obj, pre_release_fn) {
  37. var array, item, view_model, view_models, _i, _j, _len, _len1;
  38. if ((!obj || (obj !== Object(obj))) || ((typeof obj === 'function') && !ko.isObservable(obj)) || obj.__kb_destroyed || ((obj instanceof Backbone.Model) || (obj instanceof Backbone.Collection))) {
  39. return this;
  40. }
  41. if (_.isArray(obj)) {
  42. array = obj.splice(0, obj.length);
  43. for (_i = 0, _len = array.length; _i < _len; _i++) {
  44. item = array[_i];
  45. kb.release(item);
  46. }
  47. return this;
  48. }
  49. obj.__kb_destroyed = true;
  50. !pre_release_fn || pre_release_fn();
  51. if (ko.isObservable(obj) || (typeof obj.dispose === 'function') || (typeof obj.destroy === 'function') || (typeof obj.release === 'function')) {
  52. if (ko.isObservable(obj) && _.isArray(array = obj())) {
  53. if (obj.__kb_is_co || (obj.__kb_is_o && (obj.valueType() === KB_TYPE_COLLECTION))) {
  54. if (obj.destroy) {
  55. obj.destroy();
  56. } else if (obj.dispose) {
  57. obj.dispose();
  58. }
  59. } else if (array.length) {
  60. view_models = array.slice(0);
  61. array.splice(0, array.length);
  62. for (_j = 0, _len1 = view_models.length; _j < _len1; _j++) {
  63. view_model = view_models[_j];
  64. kb.release(view_model);
  65. }
  66. }
  67. } else if (obj.release) {
  68. obj.release();
  69. } else if (obj.destroy) {
  70. obj.destroy();
  71. } else if (obj.dispose) {
  72. obj.dispose();
  73. }
  74. } else {
  75. this.releaseKeys(obj);
  76. }
  77. return this;
  78. };
  79. kb.releaseKeys = function(obj) {
  80. var key, value;
  81. for (key in obj) {
  82. value = obj[key];
  83. (key === '__kb') || kb.release(value, (function() {
  84. return obj[key] = null;
  85. }));
  86. }
  87. return this;
  88. };
  89. kb.releaseOnNodeRemove = function(view_model, node) {
  90. view_model || throwUnexpected(this, 'missing view model');
  91. node || throwUnexpected(this, 'missing node');
  92. return ko.utils.domNodeDisposal.addDisposeCallback(node, function() {
  93. return kb.release(view_model);
  94. });
  95. };
  96. kb.renderTemplate = function(template, view_model, options) {
  97. var el, observable;
  98. if (options == null) {
  99. options = {};
  100. }
  101. el = document.createElement('div');
  102. observable = ko.renderTemplate(template, view_model, options, el, 'replaceChildren');
  103. if (el.children.length === 1) {
  104. el = el.children[0];
  105. }
  106. kb.releaseOnNodeRemove(view_model, el);
  107. observable.dispose();
  108. return el;
  109. };
  110. kb.renderAutoReleasedTemplate = function(template, view_model, options) {
  111. if (options == null) {
  112. options = {};
  113. }
  114. legacyWarning('kb.renderAutoReleasedTemplate', '0.16.3', 'Please use kb.renderTemplate instead');
  115. return this.renderTemplate(template, view_model, options = {});
  116. };
  117. kb.applyBindings = function(view_model, node) {
  118. ko.applyBindings(view_model, node);
  119. return kb.releaseOnNodeRemove(view_model, node);
  120. };
  121. return kb;
  122. })();
  123. this.Knockback = this.kb = kb;
  124. if (typeof exports !== 'undefined') {
  125. module.exports = kb;
  126. }
  127. if (!this._ && (typeof require !== 'undefined')) {
  128. try {
  129. _ = require('lodash');
  130. } catch (e) {
  131. _ = require('underscore');
  132. }
  133. } else {
  134. _ = this._;
  135. }
  136. kb._ = _ = _.hasOwnProperty('_') ? _._ : _;
  137. kb.Backbone = Backbone = !this.Backbone && (typeof require !== 'undefined') ? require('backbone') : this.Backbone;
  138. kb.ko = ko = !this.ko && (typeof require !== 'undefined') ? require('knockout') : this.ko;
  139. throwMissing = function(instance, message) {
  140. throw "" + (_.isString(instance) ? instance : instance.constructor.name) + ": " + message + " is missing";
  141. };
  142. throwUnexpected = function(instance, message) {
  143. throw "" + (_.isString(instance) ? instance : instance.constructor.name) + ": " + message + " is unexpected";
  144. };
  145. legacyWarning = function(identifier, last_version, message) {
  146. var _base;
  147. this._legacy_warnings || (this._legacy_warnings = {});
  148. (_base = this._legacy_warnings)[identifier] || (_base[identifier] = 0);
  149. this._legacy_warnings[identifier]++;
  150. return console.warn("warning: '" + identifier + "' has been deprecated (will be removed in Knockback after " + last_version + "). " + message + ".");
  151. };
  152. arraySplice = Array.prototype.splice;
  153. collapseOptions = function(options) {
  154. var result;
  155. result = _.clone(options);
  156. while (options.options) {
  157. _.defaults(result, options.options);
  158. options = options.options;
  159. }
  160. delete result.options;
  161. return result;
  162. };
  163. KB_TYPE_UNKNOWN = kb.TYPE_UNKNOWN;
  164. KB_TYPE_SIMPLE = kb.TYPE_SIMPLE;
  165. KB_TYPE_ARRAY = kb.TYPE_ARRAY;
  166. KB_TYPE_MODEL = kb.TYPE_MODEL;
  167. KB_TYPE_COLLECTION = kb.TYPE_COLLECTION;
  168. /*
  169. knockback-utils.js
  170. (c) 2011, 2012 Kevin Malakoff.
  171. Knockback.js is freely distributable under the MIT license.
  172. See the following for full license details:
  173. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  174. Dependencies: Knockout.js, Backbone.js, and Underscore.js.
  175. Optional dependency: Backbone.ModelRef.js.
  176. */
  177. _wrappedKey = function(obj, key, value) {
  178. if (arguments.length === 2) {
  179. if (obj && obj.__kb && obj.__kb.hasOwnProperty(key)) {
  180. return obj.__kb[key];
  181. } else {
  182. return void 0;
  183. }
  184. }
  185. obj || throwUnexpected(this, "no obj for wrapping " + key);
  186. obj.__kb || (obj.__kb = {});
  187. obj.__kb[key] = value;
  188. return value;
  189. };
  190. _argumentsAddKey = function(args, key) {
  191. arraySplice.call(args, 1, 0, key);
  192. return args;
  193. };
  194. _unwrapModels = function(obj) {
  195. var key, result, value;
  196. if (!obj) {
  197. return obj;
  198. } else if (obj.__kb) {
  199. if ('object' in obj.__kb) {
  200. return obj.__kb.object;
  201. } else {
  202. return obj;
  203. }
  204. } else if (_.isArray(obj)) {
  205. return _.map(obj, function(test) {
  206. return _unwrapModels(test);
  207. });
  208. } else if (_.isObject(obj) && !ko.isObservable(obj) && !_.isDate(obj) && !_.isString(obj)) {
  209. result = {};
  210. for (key in obj) {
  211. value = obj[key];
  212. result[key] = _unwrapModels(value);
  213. }
  214. return result;
  215. } else {
  216. return obj;
  217. }
  218. };
  219. kb.utils = (function() {
  220. function utils() {}
  221. utils.wrappedObservable = function(obj, value) {
  222. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'observable'));
  223. };
  224. utils.wrappedObject = function(obj, value) {
  225. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'object'));
  226. };
  227. utils.wrappedModel = function(obj, value) {
  228. if (arguments.length === 1) {
  229. value = _wrappedKey(obj, 'object');
  230. if (_.isUndefined(value)) {
  231. return obj;
  232. } else {
  233. return value;
  234. }
  235. } else {
  236. return _wrappedKey(obj, 'object', value);
  237. }
  238. };
  239. utils.wrappedStore = function(obj, value) {
  240. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'store'));
  241. };
  242. utils.wrappedStoreIsOwned = function(obj, value) {
  243. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'store_is_owned'));
  244. };
  245. utils.wrappedFactory = function(obj, value) {
  246. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'factory'));
  247. };
  248. utils.wrappedModelWatcher = function(obj, value) {
  249. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'model_watcher'));
  250. };
  251. utils.wrappedModelWatcherIsOwned = function(obj, value) {
  252. return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'model_watcher_is_owned'));
  253. };
  254. utils.wrappedDestroy = function(obj) {
  255. var __kb;
  256. if (!obj.__kb) {
  257. return;
  258. }
  259. if (obj.__kb.model_watcher) {
  260. obj.__kb.model_watcher.releaseCallbacks(obj);
  261. }
  262. __kb = obj.__kb;
  263. obj.__kb = null;
  264. if (__kb.observable) {
  265. __kb.observable.destroy = __kb.observable.release = null;
  266. this.wrappedDestroy(__kb.observable);
  267. __kb.observable = null;
  268. }
  269. __kb.factory = null;
  270. if (__kb.model_watcher_is_owned) {
  271. __kb.model_watcher.destroy();
  272. }
  273. __kb.model_watcher = null;
  274. if (__kb.store_is_owned) {
  275. __kb.store.destroy();
  276. }
  277. return __kb.store = null;
  278. };
  279. utils.valueType = function(observable) {
  280. if (!observable) {
  281. return KB_TYPE_UNKNOWN;
  282. }
  283. if (observable.__kb_is_o) {
  284. return observable.valueType();
  285. }
  286. if (observable.__kb_is_co || (observable instanceof Backbone.Collection)) {
  287. return KB_TYPE_COLLECTION;
  288. }
  289. if ((observable instanceof kb.ViewModel) || (observable instanceof Backbone.Model)) {
  290. return KB_TYPE_MODEL;
  291. }
  292. if (_.isArray(observable)) {
  293. return KB_TYPE_ARRAY;
  294. }
  295. return KB_TYPE_SIMPLE;
  296. };
  297. utils.pathJoin = function(path1, path2) {
  298. return (path1 ? (path1[path1.length - 1] !== '.' ? "" + path1 + "." : path1) : '') + path2;
  299. };
  300. utils.optionsPathJoin = function(options, path) {
  301. return _.defaults({
  302. path: this.pathJoin(options.path, path)
  303. }, options);
  304. };
  305. utils.inferCreator = function(value, factory, path, owner, key) {
  306. var creator, relation;
  307. if (factory) {
  308. creator = factory.creatorForPath(value, path);
  309. }
  310. if (creator) {
  311. return creator;
  312. }
  313. if (owner && Backbone.RelationalModel && (owner instanceof Backbone.RelationalModel)) {
  314. key = ko.utils.unwrapObservable(key);
  315. relation = _.find(owner.getRelations(), function(test) {
  316. return test.key === key;
  317. });
  318. if (relation) {
  319. if (relation.collectionType || _.isArray(relation.keyContents)) {
  320. return kb.CollectionObservable;
  321. } else {
  322. return kb.ViewModel;
  323. }
  324. }
  325. }
  326. if (!value) {
  327. return null;
  328. }
  329. if (value instanceof Backbone.Model) {
  330. return kb.ViewModel;
  331. }
  332. if (value instanceof Backbone.Collection) {
  333. return kb.CollectionObservable;
  334. }
  335. return null;
  336. };
  337. utils.createFromDefaultCreator = function(obj, options) {
  338. if (obj instanceof Backbone.Model) {
  339. return kb.viewModel(obj, options);
  340. }
  341. if (obj instanceof Backbone.Collection) {
  342. return kb.collectionObservable(obj, options);
  343. }
  344. if (_.isArray(obj)) {
  345. return ko.observableArray(obj);
  346. }
  347. return ko.observable(obj);
  348. };
  349. utils.release = function(obj) {
  350. legacyWarning('kb.utils.release', '0.16.0', 'Please use kb.release instead');
  351. return kb.release(obj);
  352. };
  353. return utils;
  354. })();
  355. /*
  356. knockback_factory.js
  357. (c) 2011, 2012 Kevin Malakoff.
  358. Knockback.Factory is freely distributable under the MIT license.
  359. See the following for full license details:
  360. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  361. */
  362. kb.Factory = (function() {
  363. Factory.useOptionsOrCreate = function(options, obj, owner_path) {
  364. var factory;
  365. if (options.factory && (!options.factories || (options.factories && options.factory.hasPathMappings(options.factories, owner_path)))) {
  366. return kb.utils.wrappedFactory(obj, options.factory);
  367. }
  368. factory = kb.utils.wrappedFactory(obj, new kb.Factory(options.factory));
  369. if (options.factories) {
  370. factory.addPathMappings(options.factories, owner_path);
  371. }
  372. return factory;
  373. };
  374. function Factory(parent_factory) {
  375. this.parent_factory = parent_factory;
  376. this.paths = {};
  377. }
  378. Factory.prototype.hasPath = function(path) {
  379. return this.paths.hasOwnProperty(path) || (this.parent_factory && this.parent_factory.hasPath(path));
  380. };
  381. Factory.prototype.addPathMapping = function(path, create_info) {
  382. return this.paths[path] = create_info;
  383. };
  384. Factory.prototype.addPathMappings = function(factories, owner_path) {
  385. var create_info, path;
  386. for (path in factories) {
  387. create_info = factories[path];
  388. this.paths[kb.utils.pathJoin(owner_path, path)] = create_info;
  389. }
  390. };
  391. Factory.prototype.hasPathMappings = function(factories, owner_path) {
  392. var all_exist, creator, existing_creator, path;
  393. all_exist = true;
  394. for (path in factories) {
  395. creator = factories[path];
  396. all_exist &= (existing_creator = this.creatorForPath(null, kb.utils.pathJoin(owner_path, path))) && (creator === existing_creator);
  397. }
  398. return all_exist;
  399. };
  400. Factory.prototype.creatorForPath = function(obj, path) {
  401. var creator;
  402. if ((creator = this.paths[path])) {
  403. if (creator.view_model) {
  404. return creator.view_model;
  405. } else {
  406. return creator;
  407. }
  408. }
  409. if (this.parent_factory) {
  410. if ((creator = this.parent_factory.creatorForPath(obj, path))) {
  411. return creator;
  412. }
  413. }
  414. return null;
  415. };
  416. return Factory;
  417. })();
  418. /*
  419. knockback_store.js
  420. (c) 2012 Kevin Malakoff.
  421. Knockback.Store is freely distributable under the MIT license.
  422. See the following for full license details:
  423. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  424. */
  425. kb.Store = (function() {
  426. Store.useOptionsOrCreate = function(options, obj, observable) {
  427. if (options.store) {
  428. options.store.register(obj, observable, options);
  429. return kb.utils.wrappedStore(observable, options.store);
  430. } else {
  431. kb.utils.wrappedStoreIsOwned(observable, true);
  432. return kb.utils.wrappedStore(observable, new kb.Store());
  433. }
  434. };
  435. function Store() {
  436. this.observable_records = [];
  437. this.replaced_observables = [];
  438. }
  439. Store.prototype.destroy = function() {
  440. var observable, record, _i, _j, _len, _len1, _ref, _ref1;
  441. _ref = this.observable_records;
  442. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  443. record = _ref[_i];
  444. kb.release(record.observable);
  445. }
  446. _ref1 = this.replaced_observables;
  447. for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
  448. observable = _ref1[_j];
  449. kb.release(observable);
  450. }
  451. this.observable_records = null;
  452. return this.replaced_observables = null;
  453. };
  454. Store.prototype.register = function(obj, observable, options) {
  455. var creator;
  456. if (!observable) {
  457. return;
  458. }
  459. if (ko.isObservable(observable) || observable.__kb_is_co) {
  460. return;
  461. }
  462. kb.utils.wrappedObject(observable, obj);
  463. if (!obj) {
  464. observable.__kb_null = true;
  465. }
  466. creator = options.creator ? options.creator : (options.path && options.factory ? options.factory.creatorForPath(obj, options.path) : null);
  467. if (!creator) {
  468. creator = observable.constructor;
  469. }
  470. this.observable_records.push({
  471. obj: obj,
  472. observable: observable,
  473. creator: creator
  474. });
  475. return observable;
  476. };
  477. Store.prototype.findIndex = function(obj, creator) {
  478. var index, record, _ref;
  479. if (!obj || (obj instanceof Backbone.Model)) {
  480. _ref = this.observable_records;
  481. for (index in _ref) {
  482. record = _ref[index];
  483. if (!record.observable) {
  484. continue;
  485. }
  486. if (record.observable.__kb_destroyed) {
  487. record.obj = null;
  488. record.observable = null;
  489. continue;
  490. }
  491. if ((!obj && !record.observable.__kb_null) || (obj && (record.observable.__kb_null || (record.obj !== obj)))) {
  492. continue;
  493. } else if ((record.creator === creator) || (record.creator.create && (record.creator.create === creator.create))) {
  494. return index;
  495. }
  496. }
  497. }
  498. return -1;
  499. };
  500. Store.prototype.find = function(obj, creator) {
  501. var index;
  502. if ((index = this.findIndex(obj, creator)) < 0) {
  503. return null;
  504. } else {
  505. return this.observable_records[index].observable;
  506. }
  507. };
  508. Store.prototype.isRegistered = function(observable) {
  509. var record, _i, _len, _ref;
  510. _ref = this.observable_records;
  511. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  512. record = _ref[_i];
  513. if (record.observable === observable) {
  514. return true;
  515. }
  516. }
  517. return false;
  518. };
  519. Store.prototype.findOrCreate = function(obj, options) {
  520. var creator, observable;
  521. options.store = this;
  522. options.creator || (options.creator = kb.utils.inferCreator(obj, options.factory, options.path));
  523. if (!options.creator && (obj instanceof Backbone.Model)) {
  524. options.creator = kv.ViewModel;
  525. }
  526. creator = options.creator;
  527. if (!creator) {
  528. return kb.utils.createFromDefaultCreator(obj, options);
  529. } else if (creator.models_only) {
  530. return obj;
  531. }
  532. if (creator) {
  533. observable = this.find(obj, creator);
  534. }
  535. if (observable) {
  536. return observable;
  537. }
  538. if (creator.create) {
  539. observable = creator.create(obj, options);
  540. } else {
  541. observable = new creator(obj, options);
  542. }
  543. observable || (observable = ko.observable(null));
  544. if (!ko.isObservable(observable)) {
  545. this.isRegistered(observable) || this.register(obj, observable, options);
  546. }
  547. return observable;
  548. };
  549. Store.prototype.findOrReplace = function(obj, creator, observable) {
  550. var index, record;
  551. obj || raiseUnexpected('obj missing');
  552. if ((index = this.findIndex(obj, creator)) < 0) {
  553. return this.register(obj, observable, {
  554. creator: creator
  555. });
  556. } else {
  557. record = this.observable_records[index];
  558. (kb.utils.wrappedObject(record.observable) === obj) || throwUnexpected(this, 'different object');
  559. if (record.observable !== observable) {
  560. (record.observable.constructor === observable.constructor) || throwUnexpected(this, 'replacing different type');
  561. this.replaced_observables.push(record.observable);
  562. record.observable = observable;
  563. }
  564. return observable;
  565. }
  566. };
  567. return Store;
  568. })();
  569. /*
  570. knockback_model_watcher.js
  571. (c) 2011, 2012 Kevin Malakoff.
  572. Knockback.Observable is freely distributable under the MIT license.
  573. See the following for full license details:
  574. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  575. */
  576. addStatisticsEvent = function(model, event_name, info) {
  577. return !kb.statistics || kb.statistics.addModelEvent({
  578. name: event_name,
  579. model: model,
  580. key: info.key,
  581. path: info.path
  582. });
  583. };
  584. kb.ModelWatcher = (function() {
  585. ModelWatcher.useOptionsOrCreate = function(options, model, obj, callback_options) {
  586. if (options.model_watcher) {
  587. if (!(options.model_watcher.model() === model || (options.model_watcher.model_ref === model))) {
  588. throwUnexpected(this, 'model not matching');
  589. }
  590. return kb.utils.wrappedModelWatcher(obj, options.model_watcher).registerCallbacks(obj, callback_options);
  591. } else {
  592. kb.utils.wrappedModelWatcherIsOwned(obj, true);
  593. return kb.utils.wrappedModelWatcher(obj, new kb.ModelWatcher(model)).registerCallbacks(obj, callback_options);
  594. }
  595. };
  596. function ModelWatcher(model, obj, callback_options) {
  597. this._onModelUnloaded = __bind(this._onModelUnloaded, this);
  598. this._onModelLoaded = __bind(this._onModelLoaded, this);
  599. this.__kb || (this.__kb = {});
  600. this.__kb.callbacks = {};
  601. this.__kb._onModelLoaded = _.bind(this._onModelLoaded, this);
  602. this.__kb._onModelUnloaded = _.bind(this._onModelUnloaded, this);
  603. if (callback_options) {
  604. this.registerCallbacks(obj, callback_options);
  605. }
  606. if (model) {
  607. this.model(model);
  608. } else {
  609. this.m = null;
  610. }
  611. }
  612. ModelWatcher.prototype.destroy = function() {
  613. this.model(null);
  614. this.__kb.callbacks = null;
  615. return kb.utils.wrappedDestroy(this);
  616. };
  617. ModelWatcher.prototype.model = function(new_model) {
  618. var callbacks, event_name, info, list, previous_model, _i, _len, _ref;
  619. if ((arguments.length === 0) || (this.m === new_model)) {
  620. return this.m;
  621. }
  622. if (this.model_ref) {
  623. this.model_ref.unbind('loaded', this.__kb._onModelLoaded);
  624. this.model_ref.unbind('unloaded', this.__kb._onModelUnloaded);
  625. this.model_ref.release();
  626. this.model_ref = null;
  627. }
  628. if (Backbone.ModelRef && (new_model instanceof Backbone.ModelRef)) {
  629. this.model_ref = new_model;
  630. this.model_ref.retain();
  631. this.model_ref.bind('loaded', this.__kb._onModelLoaded);
  632. this.model_ref.bind('unloaded', this.__kb._onModelUnloaded);
  633. new_model = this.model_ref.model();
  634. } else {
  635. delete this.model_ref;
  636. }
  637. previous_model = this.m;
  638. this.m = new_model;
  639. _ref = this.__kb.callbacks;
  640. for (event_name in _ref) {
  641. callbacks = _ref[event_name];
  642. if (previous_model) {
  643. previous_model.unbind(event_name, callbacks.fn);
  644. }
  645. if (new_model) {
  646. new_model.bind(event_name, callbacks.fn);
  647. }
  648. list = callbacks.list;
  649. for (_i = 0, _len = list.length; _i < _len; _i++) {
  650. info = list[_i];
  651. if (info.model) {
  652. info.model(new_model);
  653. }
  654. }
  655. }
  656. return new_model;
  657. };
  658. ModelWatcher.prototype.registerCallbacks = function(obj, callback_info) {
  659. var callbacks, event_name, info, list;
  660. obj || throwMissing(this, 'obj');
  661. callback_info || throwMissing(this, 'info');
  662. event_name = callback_info.event_name ? callback_info.event_name : 'change';
  663. callbacks = this.__kb.callbacks[event_name];
  664. if (!callbacks) {
  665. list = [];
  666. callbacks = {
  667. list: list,
  668. fn: function(model) {
  669. var info, _i, _len;
  670. for (_i = 0, _len = list.length; _i < _len; _i++) {
  671. info = list[_i];
  672. if (info.update && !info.rel_fn) {
  673. if (model && info.key && (model.hasChanged && !model.hasChanged(ko.utils.unwrapObservable(info.key)))) {
  674. continue;
  675. }
  676. !kb.statistics || addStatisticsEvent(model, event_name, info);
  677. info.update();
  678. }
  679. }
  680. return null;
  681. }
  682. };
  683. this.__kb.callbacks[event_name] = callbacks;
  684. if (this.m) {
  685. this.m.bind(event_name, callbacks.fn);
  686. }
  687. }
  688. info = _.defaults({
  689. obj: obj
  690. }, callback_info);
  691. callbacks.list.push(info);
  692. if (this.m) {
  693. if (Backbone.RelationalModel && (this.m instanceof Backbone.RelationalModel)) {
  694. this._modelBindRelatationalInfo(event_name, info);
  695. }
  696. info.model(this.m) && info.model;
  697. }
  698. };
  699. ModelWatcher.prototype.releaseCallbacks = function(obj) {
  700. var callbacks, event_name, index, info, _ref, _ref1;
  701. if (!this.__kb.callbacks) {
  702. return;
  703. }
  704. _ref = this.__kb.callbacks;
  705. for (event_name in _ref) {
  706. callbacks = _ref[event_name];
  707. _ref1 = callbacks.list;
  708. for (index in _ref1) {
  709. info = _ref1[index];
  710. if (info.obj === obj) {
  711. callbacks.list.splice(index, 1);
  712. if (info.rel_fn) {
  713. this._modelUnbindRelatationalInfo(event_name, info);
  714. }
  715. if (info.model) {
  716. info.model(null);
  717. }
  718. return;
  719. }
  720. }
  721. }
  722. };
  723. ModelWatcher.prototype._onModelLoaded = function(model) {
  724. var callbacks, event_name, info, is_relational, list, _i, _len, _ref;
  725. is_relational = Backbone.RelationalModel && (model instanceof Backbone.RelationalModel);
  726. this.m = model;
  727. _ref = this.__kb.callbacks;
  728. for (event_name in _ref) {
  729. callbacks = _ref[event_name];
  730. model.bind(event_name, callbacks.fn);
  731. list = callbacks.list;
  732. for (_i = 0, _len = list.length; _i < _len; _i++) {
  733. info = list[_i];
  734. if (is_relational) {
  735. this._modelBindRelatationalInfo(event_name, info);
  736. }
  737. if (info.model) {
  738. info.model(model);
  739. }
  740. }
  741. }
  742. };
  743. ModelWatcher.prototype._onModelUnloaded = function(model) {
  744. var callbacks, event_name, info, list, _i, _len, _ref;
  745. this.m = null;
  746. _ref = this.__kb.callbacks;
  747. for (event_name in _ref) {
  748. callbacks = _ref[event_name];
  749. model.unbind(event_name, callbacks.fn);
  750. list = callbacks.list;
  751. for (_i = 0, _len = list.length; _i < _len; _i++) {
  752. info = list[_i];
  753. if (info.rel_fn) {
  754. this._modelUnbindRelatationalInfo(event_name, info);
  755. }
  756. if (info.model) {
  757. info.model(null);
  758. }
  759. }
  760. }
  761. };
  762. ModelWatcher.prototype._modelBindRelatationalInfo = function(event_name, info) {
  763. var key, relation;
  764. if ((event_name === 'change') && info.key && info.update) {
  765. key = ko.utils.unwrapObservable(info.key);
  766. relation = _.find(this.m.getRelations(), function(test) {
  767. return test.key === key;
  768. });
  769. if (!relation) {
  770. return;
  771. }
  772. info.rel_fn = function(model) {
  773. !kb.statistics || addStatisticsEvent(model, "" + event_name + " (relational)", info);
  774. return info.update();
  775. };
  776. if (relation.collectionType || _.isArray(relation.keyContents)) {
  777. info.is_collection = true;
  778. this.m.bind("add:" + info.key, info.rel_fn);
  779. this.m.bind("remove:" + info.key, info.rel_fn);
  780. } else {
  781. this.m.bind("update:" + info.key, info.rel_fn);
  782. }
  783. }
  784. };
  785. ModelWatcher.prototype._modelUnbindRelatationalInfo = function(event_name, info) {
  786. if (!info.rel_fn) {
  787. return;
  788. }
  789. if (info.is_collection) {
  790. this.m.unbind("add:" + info.key, info.rel_fn);
  791. this.m.unbind("remove:" + info.key, info.rel_fn);
  792. } else {
  793. this.m.unbind("update:" + info.key, info.rel_fn);
  794. }
  795. info.rel_fn = null;
  796. };
  797. return ModelWatcher;
  798. })();
  799. kb.modelObservable = function(model, observable) {
  800. return new kb.ModelWatcher(model, observable);
  801. };
  802. /*
  803. knockback-observable.js
  804. (c) 2012 Kevin Malakoff.
  805. Knockback.Observable is freely distributable under the MIT license.
  806. See the following for full license details:
  807. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  808. */
  809. kb.Observable = (function() {
  810. function Observable(model, options, vm) {
  811. var create_options, model_watcher, observable,
  812. _this = this;
  813. this.vm = vm;
  814. options || throwMissing(this, 'options');
  815. this.vm || (this.vm = {});
  816. if (_.isString(options) || ko.isObservable(options)) {
  817. create_options = this.create_options = {
  818. key: options
  819. };
  820. } else {
  821. create_options = this.create_options = collapseOptions(options);
  822. }
  823. this.key = create_options.key;
  824. delete create_options.key;
  825. this.key || throwMissing(this, 'key');
  826. !create_options.args || (this.args = create_options.args, delete create_options.args);
  827. !create_options.read || (this.read = create_options.read, delete create_options.read);
  828. !create_options.write || (this.write = create_options.write, delete create_options.write);
  829. model_watcher = create_options.model_watcher;
  830. delete create_options.model_watcher;
  831. this.vo = ko.observable(null);
  832. observable = kb.utils.wrappedObservable(this, ko.dependentObservable({
  833. read: function() {
  834. var arg, args, new_value, _i, _len, _ref;
  835. args = [ko.utils.unwrapObservable(_this.key)];
  836. if (_this.args) {
  837. if (_.isArray(_this.args)) {
  838. _ref = _this.args;
  839. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  840. arg = _ref[_i];
  841. args.push(ko.utils.unwrapObservable(arg));
  842. }
  843. } else {
  844. args.push(ko.utils.unwrapObservable(_this.args));
  845. }
  846. }
  847. if (_this.m) {
  848. new_value = _this.read ? _this.read.apply(_this.vm, args) : _this.m.get.apply(_this.m, args);
  849. _this.update(new_value);
  850. }
  851. return ko.utils.unwrapObservable(_this.vo());
  852. },
  853. write: function(new_value) {
  854. var arg, args, set_info, unwrapped_new_value, _i, _len, _ref;
  855. unwrapped_new_value = _unwrapModels(new_value);
  856. set_info = {};
  857. set_info[ko.utils.unwrapObservable(_this.key)] = unwrapped_new_value;
  858. args = _this.write ? [unwrapped_new_value] : [set_info];
  859. if (_this.args) {
  860. if (_.isArray(_this.args)) {
  861. _ref = _this.args;
  862. for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  863. arg = _ref[_i];
  864. args.push(ko.utils.unwrapObservable(arg));
  865. }
  866. } else {
  867. args.push(ko.utils.unwrapObservable(_this.args));
  868. }
  869. }
  870. if (_this.m) {
  871. if (_this.write) {
  872. _this.write.apply(_this.vm, args);
  873. } else {
  874. _this.m.set.apply(_this.m, args);
  875. }
  876. }
  877. return _this.update(new_value);
  878. },
  879. owner: this.vm
  880. }));
  881. observable.__kb_is_o = true;
  882. create_options.store = kb.utils.wrappedStore(observable, create_options.store);
  883. create_options.path = kb.utils.pathJoin(create_options.path, this.key);
  884. if (create_options.factories && ((typeof create_options.factories === 'function') || create_options.factories.create)) {
  885. create_options.factory = kb.utils.wrappedFactory(observable, new kb.Factory(create_options.factory));
  886. create_options.factory.addPathMapping(create_options.path, create_options.factories);
  887. } else {
  888. create_options.factory = kb.Factory.useOptionsOrCreate(create_options, observable, create_options.path);
  889. }
  890. delete create_options.factories;
  891. observable.value = _.bind(this.value, this);
  892. observable.valueType = _.bind(this.valueType, this);
  893. observable.destroy = _.bind(this.destroy, this);
  894. kb.ModelWatcher.useOptionsOrCreate({
  895. model_watcher: model_watcher
  896. }, model, this, {
  897. model: _.bind(this.model, this),
  898. update: _.bind(this.update, this),
  899. key: this.key,
  900. path: create_options.path
  901. });
  902. this.__kb_value || this.update();
  903. if (kb.LocalizedObservable && create_options.localizer) {
  904. observable = new create_options.localizer(observable);
  905. delete create_options.localizer;
  906. }
  907. if (kb.DefaultObservable && create_options.hasOwnProperty('default')) {
  908. observable = kb.defaultObservable(observable, create_options["default"]);
  909. delete create_options["default"];
  910. }
  911. return observable;
  912. }
  913. Observable.prototype.destroy = function() {
  914. this.__kb_destroyed = true;
  915. kb.release(this.__kb_value);
  916. this.__kb_value = null;
  917. this.vm = null;
  918. this.create_options = null;
  919. return kb.utils.wrappedDestroy(this);
  920. };
  921. Observable.prototype.value = function() {
  922. return this.__kb_value;
  923. };
  924. Observable.prototype.valueType = function() {
  925. var new_value;
  926. new_value = this.m ? this.m.get(this.key) : null;
  927. this.value_type || this._updateValueObservable(new_value);
  928. return this.value_type;
  929. };
  930. Observable.prototype.model = function(new_model) {
  931. if (this.__kb_destroyed) {
  932. return;
  933. }
  934. if ((arguments.length === 0) || (this.m === new_model)) {
  935. return this.m;
  936. }
  937. this.m = new_model;
  938. return this.update();
  939. };
  940. Observable.prototype.update = function(new_value) {
  941. var new_type, value;
  942. if (this.__kb_destroyed) {
  943. return;
  944. }
  945. if (this.m && !arguments.length) {
  946. new_value = this.m.get(ko.utils.unwrapObservable(this.key));
  947. }
  948. (new_value !== void 0) || (new_value = null);
  949. new_type = kb.utils.valueType(new_value);
  950. if (!this.__kb_value || (this.__kb_value.__kb_destroyed || (this.__kb_value.__kb_null && new_value))) {
  951. this.__kb_value = void 0;
  952. this.value_type = void 0;
  953. }
  954. value = this.__kb_value;
  955. if (_.isUndefined(this.value_type) || (this.value_type !== new_type && new_type !== KB_TYPE_UNKNOWN)) {
  956. if ((this.value_type === KB_TYPE_COLLECTION) && (new_type === KB_TYPE_ARRAY)) {
  957. return value(new_value);
  958. } else {
  959. return this._updateValueObservable(new_value);
  960. }
  961. } else if (this.value_type === KB_TYPE_MODEL) {
  962. if (typeof value.model === 'function') {
  963. if (value.model() !== new_value) {
  964. return value.model(new_value);
  965. }
  966. } else if (kb.utils.wrappedObject(value) !== new_value) {
  967. return this._updateValueObservable(new_value);
  968. }
  969. } else if (this.value_type === KB_TYPE_COLLECTION) {
  970. if (value.collection() !== new_value) {
  971. return value.collection(new_value);
  972. }
  973. } else {
  974. if (value() !== new_value) {
  975. return value(new_value);
  976. }
  977. }
  978. };
  979. Observable.prototype._updateValueObservable = function(new_value) {
  980. var create_options, creator, previous_value, value;
  981. create_options = this.create_options;
  982. create_options.creator = kb.utils.inferCreator(new_value, create_options.factory, create_options.path, this.m, this.key);
  983. this.value_type = KB_TYPE_UNKNOWN;
  984. creator = create_options.creator;
  985. previous_value = this.__kb_value;
  986. this.__kb_value = void 0;
  987. if (previous_value) {
  988. kb.release(previous_value);
  989. }
  990. if (creator) {
  991. if (create_options.store) {
  992. value = create_options.store.findOrCreate(new_value, create_options);
  993. } else {
  994. if (creator.models_only) {
  995. value = new_value;
  996. this.value_type = KB_TYPE_SIMPLE;
  997. } else if (creator.create) {
  998. value = creator.create(new_value, create_options);
  999. } else {
  1000. value = new creator(new_value, create_options);
  1001. }
  1002. }
  1003. } else {
  1004. if (_.isArray(new_value)) {
  1005. this.value_type = KB_TYPE_ARRAY;
  1006. value = ko.observableArray(new_value);
  1007. } else {
  1008. this.value_type = KB_TYPE_SIMPLE;
  1009. value = ko.observable(new_value);
  1010. }
  1011. }
  1012. if (this.value_type === KB_TYPE_UNKNOWN) {
  1013. if (!ko.isObservable(value)) {
  1014. this.value_type = KB_TYPE_MODEL;
  1015. if (typeof value.model !== 'function') {
  1016. kb.utils.wrappedObject(value, new_value);
  1017. }
  1018. } else if (value.__kb_is_co) {
  1019. this.value_type = KB_TYPE_COLLECTION;
  1020. } else {
  1021. this.value_type = KB_TYPE_SIMPLE;
  1022. }
  1023. }
  1024. this.__kb_value = value;
  1025. return this.vo(value);
  1026. };
  1027. return Observable;
  1028. })();
  1029. kb.observable = function(model, options, view_model) {
  1030. return new kb.Observable(model, options, view_model);
  1031. };
  1032. /*
  1033. knockback-view-model.js
  1034. (c) 2011, 2012 Kevin Malakoff.
  1035. Knockback.Observable is freely distributable under the MIT license.
  1036. See the following for full license details:
  1037. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  1038. */
  1039. kb.ViewModel = (function() {
  1040. ViewModel.extend = Backbone.Model.extend;
  1041. function ViewModel(model, options, view_model) {
  1042. var attribute_keys, bb_model, keys, mapped_keys, mapping_info, model_watcher, vm_key, _ref;
  1043. !model || (model instanceof Backbone.Model) || ((typeof model.get === 'function') && (typeof model.bind === 'function')) || throwUnexpected(this, 'not a model');
  1044. options || (options = {});
  1045. view_model || (view_model = {});
  1046. if (_.isArray(options)) {
  1047. options = {
  1048. keys: options
  1049. };
  1050. } else {
  1051. options = collapseOptions(options);
  1052. }
  1053. this.__kb || (this.__kb = {});
  1054. this.__kb.vm_keys = {};
  1055. this.__kb.model_keys = {};
  1056. this.__kb.view_model = _.isUndefined(view_model) ? this : view_model;
  1057. !options.internals || (this.__kb.internals = options.internals);
  1058. !options.excludes || (this.__kb.excludes = options.excludes);
  1059. kb.Store.useOptionsOrCreate(options, model, this);
  1060. this.__kb.path = options.path;
  1061. kb.Factory.useOptionsOrCreate(options, this, options.path);
  1062. model_watcher = kb.utils.wrappedModelWatcher(this, new kb.ModelWatcher(model, this, {
  1063. model: _.bind(this.model, this)
  1064. }));
  1065. if (options.requires && _.isArray(options.requires)) {
  1066. keys = _.clone(options.requires);
  1067. }
  1068. if (this.__kb.internals) {
  1069. keys = keys ? _.union(keys, this.__kb.internals) : _.clone(this.__kb.internals);
  1070. }
  1071. if (options.keys) {
  1072. if (_.isArray(options.keys)) {
  1073. this.__kb.keys = options.keys;
  1074. keys = keys ? _.union(keys, options.keys) : _.clone(options.keys);
  1075. } else {
  1076. mapped_keys = {};
  1077. _ref = options.keys;
  1078. for (vm_key in _ref) {
  1079. mapping_info = _ref[vm_key];
  1080. mapped_keys[_.isString(mapping_info) ? mapping_info : (mapping_info.key ? mapping_info.key : vm_key)] = true;
  1081. }
  1082. this.__kb.keys = _.keys(mapped_keys);
  1083. }
  1084. } else {
  1085. bb_model = model_watcher.model();
  1086. if (bb_model && bb_model.attributes) {
  1087. attribute_keys = _.keys(bb_model.attributes);
  1088. keys = keys ? _.union(keys, attribute_keys) : attribute_keys;
  1089. }
  1090. }
  1091. if (keys && this.__kb.excludes) {
  1092. keys = _.difference(keys, this.__kb.excludes);
  1093. }
  1094. if (_.isObject(options.keys) && !_.isArray(options.keys)) {
  1095. this._mapObservables(model, options.keys);
  1096. }
  1097. if (_.isObject(options.requires) && !_.isArray(options.requires)) {
  1098. this._mapObservables(model, options.requires);
  1099. }
  1100. !options.mappings || this._mapObservables(model, options.mappings);
  1101. !keys || this._createObservables(model, keys);
  1102. !kb.statistics || kb.statistics.register('ViewModel', this);
  1103. }
  1104. ViewModel.prototype.destroy = function() {
  1105. var vm_key;
  1106. if (this.__kb.view_model !== this) {
  1107. for (vm_key in this.__kb.vm_keys) {
  1108. this.__kb.view_model[vm_key] = null;
  1109. }
  1110. }
  1111. this.__kb.view_model = null;
  1112. kb.releaseKeys(this);
  1113. kb.utils.wrappedDestroy(this);
  1114. return !kb.statistics || kb.statistics.unregister('ViewModel', this);
  1115. };
  1116. ViewModel.prototype.shareOptions = function() {
  1117. return {
  1118. store: kb.utils.wrappedStore(this),
  1119. factory: kb.utils.wrappedFactory(this)
  1120. };
  1121. };
  1122. ViewModel.prototype.model = function(new_model) {
  1123. var missing, model, model_watcher;
  1124. model = kb.utils.wrappedObject(this);
  1125. if ((arguments.length === 0) || (model === new_model)) {
  1126. return model;
  1127. }
  1128. if (this.__kb_null) {
  1129. !new_model || throwUnexpected(this, 'model set on shared null');
  1130. return;
  1131. }
  1132. kb.utils.wrappedObject(this, new_model);
  1133. model_watcher = kb.utils.wrappedModelWatcher(this);
  1134. if (!model_watcher) {
  1135. return;
  1136. }
  1137. model_watcher.model(new_model);
  1138. if (this.__kb.keys || !new_model || !new_model.attributes) {
  1139. return;
  1140. }
  1141. missing = _.difference(_.keys(new_model.attributes), _.keys(this.__kb.model_keys));
  1142. if (missing) {
  1143. return this._createObservables(new_model, missing);
  1144. }
  1145. };
  1146. ViewModel.prototype._createObservables = function(model, keys) {
  1147. var create_options, key, vm_key, _i, _len;
  1148. create_options = {
  1149. store: kb.utils.wrappedStore(this),
  1150. factory: kb.utils.wrappedFactory(this),
  1151. path: this.__kb.path,
  1152. model_watcher: kb.utils.wrappedModelWatcher(this)
  1153. };
  1154. for (_i = 0, _len = keys.length; _i < _len; _i++) {
  1155. key = keys[_i];
  1156. vm_key = this.__kb.internals && _.contains(this.__kb.internals, key) ? "_" + key : key;
  1157. if (this[vm_key]) {
  1158. continue;
  1159. }
  1160. this.__kb.vm_keys[vm_key] = true;
  1161. this.__kb.model_keys[key] = true;
  1162. create_options.key = key;
  1163. this[vm_key] = this.__kb.view_model[vm_key] = kb.observable(model, create_options, this);
  1164. }
  1165. };
  1166. ViewModel.prototype._mapObservables = function(model, mappings) {
  1167. var create_options, mapping_info, vm_key;
  1168. create_options = {
  1169. store: kb.utils.wrappedStore(this),
  1170. factory: kb.utils.wrappedFactory(this),
  1171. path: this.__kb.path,
  1172. model_watcher: kb.utils.wrappedModelWatcher(this)
  1173. };
  1174. for (vm_key in mappings) {
  1175. mapping_info = mappings[vm_key];
  1176. if (this[vm_key]) {
  1177. continue;
  1178. }
  1179. mapping_info = _.isString(mapping_info) ? {
  1180. key: mapping_info
  1181. } : _.clone(mapping_info);
  1182. mapping_info.key || (mapping_info.key = vm_key);
  1183. this.__kb.vm_keys[vm_key] = true;
  1184. this.__kb.model_keys[mapping_info.key] = true;
  1185. this[vm_key] = this.__kb.view_model[vm_key] = kb.observable(model, _.defaults(mapping_info, create_options), this);
  1186. }
  1187. };
  1188. return ViewModel;
  1189. })();
  1190. kb.viewModel = function(model, options, view_model) {
  1191. return new kb.ViewModel(model, options, view_model);
  1192. };
  1193. kb.observables = function(model, binding_info, view_model) {
  1194. legacyWarning('kb.observables', '0.16.0', 'Please use kb.viewModel instead');
  1195. return new kb.ViewModel(model, binding_info, view_model);
  1196. };
  1197. /*
  1198. knockback-collection-observable.js
  1199. (c) 2011, 2012 Kevin Malakoff.
  1200. Knockback.CollectionObservable is freely distributable under the MIT license.
  1201. See the following for full license details:
  1202. https://github.com/kmalakoff/knockback/blob/master/LICENSE
  1203. */
  1204. kb.CollectionObservable = (function() {
  1205. CollectionObservable.extend = Backbone.Model.extend;
  1206. function CollectionObservable(collection, options) {
  1207. var create_options, observable,
  1208. _this = this;
  1209. !collection || (collection instanceof Backbone.Collection) || throwUnexpected(this, 'not a collection');
  1210. options || (options = {});
  1211. observable = kb.utils.wrappedObservable(this, ko.observableArray([]));
  1212. observable.__kb_is_co = true;
  1213. this.in_edit = 0;
  1214. this.__kb || (this.__kb = {});
  1215. this.__kb._onCollectionChange = _.bind(this._onCollectionChange, this);
  1216. options = collapseOptions(options);
  1217. if (options.sort_attribute) {
  1218. this.sorted_index_fn = ko.observable(this._sortAttributeFn(options.sort_attribute));
  1219. } else {
  1220. if (options.sorted_index) {
  1221. legacyWarning(this, '0.16.3', 'use sorted_index_fn instead');
  1222. options.sorted_index_fn = options.sorted_index;
  1223. }
  1224. this.sorted_index_fn = ko.observable(options.sorted_index_fn);
  1225. }
  1226. if (options.filters) {
  1227. this.filters = ko.observableArray(_.isArray(options.filters) ? options.filters : options.filters ? [options.filters] : void 0);
  1228. } else {
  1229. this.filters = ko.observableArray([]);
  1230. }
  1231. create_options = this.create_options = {
  1232. store: kb.Store.useOptionsOrCreate(options, collection, observable)
  1233. };
  1234. this.path = options.path;
  1235. create_options.factory = kb.utils.wrappedFactory(observable, this._shareOrCreateFactory(options));
  1236. create_options.path = kb.utils.pathJoin(options.path, 'models');
  1237. create_options.creator = create_options.factory.creatorForPath(null, create_options.path);
  1238. if (create_options.creator) {
  1239. this.models_only = create_options.creator.models_only;
  1240. }
  1241. observable.destroy = _.bind(this.destroy, this);
  1242. observable.shareOptions = _.bind(this.shareOptions, this);
  1243. observable.collection = _.bind(this.collection, this);
  1244. observable.viewModelByModel = _.bind(this.viewModelByModel, this);
  1245. observable.sortedIndex = _.bind(this.sortedIndex, this);
  1246. observable.sortAttribute = _.bind(this.sortAttribute, this);
  1247. observable.hasViewModels = _.bind(this.hasViewModels, this);
  1248. this._col = ko.observable();
  1249. this.collection(collection);
  1250. this._mapper = ko.dependentObservable(function() {
  1251. var filters, model, models, sorted_index_fn, view_model, view_models, _i, _len;
  1252. if (_this.in_edit) {
  1253. return;
  1254. }
  1255. observable = kb.utils.wrappedObservable(_this);
  1256. collection = _this._col();
  1257. if (collection) {
  1258. models = collection.models;
  1259. }
  1260. sorted_index_fn = _this.sorted_index_fn();
  1261. filters = _this.filters();
  1262. if (!models || (collection.models.length === 0)) {
  1263. view_models = [];
  1264. } else {
  1265. if (filters.length) {
  1266. models = _.filter(models, function(model) {
  1267. return !_this._modelIsFiltered(model);
  1268. });
  1269. }
  1270. if (sorted_index_fn) {
  1271. view_models = [];
  1272. for (_i = 0, _len = models.length; _i < _len; _i++) {
  1273. model = models[_i];
  1274. view_model = _this._createViewModel(model);
  1275. view_models.splice(sorted_index_fn(view_models, view_model), 0, view_model);
  1276. }
  1277. } else {
  1278. if (_this.models_only) {
  1279. view_models = filters.length ? models : models.slice();
  1280. } else {
  1281. view_models = _.map(models, function(model) {
  1282. return _this._createViewModel(model);
  1283. });
  1284. }
  1285. }
  1286. }
  1287. _this.in_edit++;
  1288. observable(view_models);
  1289. return _this.in_edit--;
  1290. });
  1291. observable.subscribe(_.bind(this._onObservableArrayChange, this));
  1292. !kb.statistics || kb.statistics.register('CollectionObservable', this);
  1293. return observable;
  1294. }
  1295. CollectionObservable.prototype.destroy = function() {
  1296. var array, collection, observable;
  1297. observable = kb.utils.wrappedObservable(this);
  1298. collection = this._col();
  1299. if (collection) {
  1300. collection.unbind('all', this.__kb._onCollectionChange);
  1301. array = observable();
  1302. array.splice(0, array.length);
  1303. }
  1304. kb.release(this.filters);
  1305. this.filters = this._col = this.sorted_index_fn = this._mapper = this.create_options = null;
  1306. kb.utils.wrappedDestroy(this);
  1307. return !kb.statistics || kb.statistics.unregister('CollectionObservable', this);
  1308. };
  1309. CollectionObservable.prototype.shareOptions = function() {
  1310. var observable;
  1311. observable = kb.utils.wrappedObservable(this);
  1312. return {
  1313. store: kb.utils.wrappedStore(observable),
  1314. factory: kb.utils.wrappedFactory(observable)
  1315. };
  1316. };
  1317. CollectionObservable.prototype.collection = function(collection) {
  1318. var observable, previous_collection;
  1319. observable = kb.utils.wrappedObservable(this);
  1320. previous_collection = this._col();
  1321. if ((arguments.length === 0) || (collection === previous_collection)) {
  1322. observable();
  1323. return previous_collection;
  1324. }
  1325. if (previous_collection) {
  1326. previous_collection.unbind('all', this.__kb._onCollectionChange);
  1327. }
  1328. if (collection) {
  1329. collection.bind('all', this.__kb._onCollectionChange);
  1330. }
  1331. this._col(collection);
  1332. return collection;
  1333. };
  1334. CollectionObservable.prototype.filters = function(filters) {
  1335. if (filters) {
  1336. return this.filters(_.isArray(filters) ? filters : [filters]);
  1337. } else {
  1338. return this.filters([]);
  1339. }
  1340. };
  1341. CollectionObservable.prototype.sortedIndex = function(sorted_index_fn) {
  1342. return this.sorted_index_fn(sorted_index_fn);
  1343. };
  1344. CollectionObservable.prototype.sortAttribute = function(sort_attribute) {
  1345. return this.sorted_index_fn(sort_attribute ? this._sortAttributeFn(sort_attribute) : null);
  1346. };
  1347. CollectionObservable.prototype.viewModelByModel = function(model) {
  1348. var id_attribute;
  1349. if (this.models_only) {
  1350. return null;
  1351. }
  1352. id_attribute = model.hasOwnProperty(model.idAttribute) ? model.idAttribute : 'cid';
  1353. return _.find(kb.utils.wrappedObservable(this)(), function(test) {
  1354. return test.__kb.object[id_attribute] === model[id_attribute];
  1355. });
  1356. };
  1357. CollectionObservable.prototype.hasViewModels = function() {
  1358. return !this.models_only;
  1359. };
  1360. CollectionObservable.prototype._shareOrCreateFactory = function(options) {
  1361. var absolute_models_path, existing_creator, factories, factory;
  1362. absolute_models_path = kb.utils.pathJoin(options.path, 'models');
  1363. factories = options.factories;
  1364. if ((factory = options.factory)) {
  1365. if ((existing_creator = factory.creatorForPath(null, absolute_models_path)) && (!factories || (factories['models'] === existing_creator))) {
  1366. if (!factories) {
  1367. return factory;
  1368. }
  1369. if (factory.hasPathMappings(factories, options.path)) {
  1370. return factory;
  1371. }
  1372. }
  1373. }
  1374. factory = new kb.Factory(options.factory);
  1375. if (factories) {
  1376. factory.addPathMappings(factories, options.path);
  1377. }
  1378. if (!factory.creatorForPath(null, absolute_models_path)) {
  1379. if (options.hasOwnProperty('models_only')) {
  1380. if (options.models_only) {
  1381. factory.addPathMapping(absolute_models_path, {
  1382. models_only: true
  1383. });
  1384. } else {
  1385. factory.addPathMapping(absolute_models_path, kb.ViewModel);
  1386. }
  1387. } else if (options.view_model) {
  1388. factory.addPathMapping(absolute_models_path, options.view_model);
  1389. } else if (options.create) {
  1390. factory.addPathMapping(absolute_models_path, {
  1391. create: options.create
  1392. });
  1393. } else {
  1394. factory.addPathMapping(absolute_models_path, kb.ViewModel);
  1395. }
  1396. }
  1397. return factory;
  1398. };
  1399. CollectionObservable.prototype._onCollectionChange = function(event, arg) {
  1400. var add_index, collection, observable, sorted_index_fn, view_model;
  1401. if (this.in_edit) {
  1402. return;
  1403. }
  1404. switch (event) {
  1405. case 'reset':
  1406. case 'resort':
  1407. if (event === 'resort' && !this.sorted_index_fn()) {
  1408. return;
  1409. }
  1410. return this._col.notifySubscribers(this._col());
  1411. case 'new':
  1412. case 'add':
  1413. if (this._modelIsFiltered(arg)) {
  1414. return;
  1415. }
  1416. observable = kb.utils.wrappedObservable(this);
  1417. collection = this._col();
  1418. view_model = this._createViewMod