From b375e5fca889df5157488511d08fac66d3584dc0 Mon Sep 17 00:00:00 2001 From: Kevin Malakoff Date: Fri, 14 Sep 2012 14:01:31 +0900 Subject: [PATCH] Added AMD loading --- Bakefile.coffee | 29 +- README.md | 2 +- RELEASE_NOTES.md | 1 + examples/pn-nt+_xui.html | 2 +- examples/pn-t-e_zpt_ko.html | 2 +- examples/pn-t_zpt.html | 2 +- examples/pnp-dt_zpt_pth.html | 2 +- examples/pnp-t-e_jq_kb.html | 2 +- examples/pnp-t-nh_zpt_pth.html | 2 +- examples/pnp-t_jq_kb.html | 2 +- examples/pnp-t_zpt_bb.html | 2 +- examples/pnp-t_zpt_kb.html | 2 +- examples/pnp-t_zpt_pth.html | 2 +- examples/pnp-t_zpt_pth_ko.html | 2 +- .../vendor/knockback-core-stack-0.16.4.js | 7727 ----------------- .../vendor/knockback-core-stack-0.16.5.js | 8 +- index.html | 3 +- knockback-navigators.css | 7 + knockback-page-navigator-panes.js | 1420 +-- knockback-page-navigator-panes.min.js | 13 +- knockback-page-navigator-simple.js | 608 +- knockback-page-navigator-simple.min.js | 13 +- knockback-pane-navigator.js | 1142 +-- knockback-pane-navigator.min.js | 13 +- lib/knockback-sample-transitions-jquery.js | 212 + ...knockback-sample-transitions-jquery.min.js | 6 + lib/knockback-sample-transitions.js | 200 - lib/knockback-sample-transitions.min.js | 9 - package.json | 2 +- packages/npm/README.md | 2 +- packages/npm/knockback-navigators.css | 7 + .../npm/knockback-page-navigator-panes.js | 1420 +-- .../npm/knockback-page-navigator-panes.min.js | 13 +- .../npm/knockback-page-navigator-simple.js | 608 +- .../knockback-page-navigator-simple.min.js | 13 +- packages/npm/knockback-pane-navigator.js | 1142 +-- packages/npm/knockback-pane-navigator.min.js | 13 +- .../knockback-sample-transitions-jquery.js | 212 + ...knockback-sample-transitions-jquery.min.js | 6 + .../Content/Scripts/knockback-navigators.css | 7 + .../Scripts/knockback-page-navigator-panes.js | 1420 +-- .../knockback-page-navigator-panes.min.js | 13 +- .../knockback-page-navigator-simple.js | 608 +- .../knockback-page-navigator-simple.min.js | 13 +- .../Scripts/knockback-pane-navigator.js | 1142 +-- .../Scripts/knockback-pane-navigator.min.js | 13 +- .../knockback-sample-transitions-jquery.js | 212 + ...knockback-sample-transitions-jquery.min.js | 6 + src/component-imports.coffee | 4 +- .../module-loader.js | 18 + .../module-loader.js | 18 + src/knockback-pane-navigator/module-loader.js | 18 + .../component-imports.coffee | 3 + ...knockback-transition-cover-vertical.coffee | 0 .../knockback-transition-fade-in.coffee | 0 .../knockback-transition-helpers.coffee | 0 ...ockback-transition-navigation-slide.coffee | 0 .../module-loader.js | 18 + src/license-header.coffee | 8 - test/packaging/bundle-config.coffee | 2 +- test/vendor/knockback-core-stack-0.16.3.js | 7727 ----------------- test/vendor/knockback-core-stack-0.16.4.js | 7727 ----------------- test/vendor/knockback-core-stack-0.16.5.js | 8 +- 63 files changed, 5611 insertions(+), 28247 deletions(-) delete mode 100644 examples/vendor/knockback-core-stack-0.16.4.js create mode 100644 lib/knockback-sample-transitions-jquery.js create mode 100644 lib/knockback-sample-transitions-jquery.min.js delete mode 100644 lib/knockback-sample-transitions.js delete mode 100644 lib/knockback-sample-transitions.min.js create mode 100644 packages/npm/lib/knockback-sample-transitions-jquery.js create mode 100644 packages/npm/lib/knockback-sample-transitions-jquery.min.js create mode 100644 packages/nuget/Content/Scripts/lib/knockback-sample-transitions-jquery.js create mode 100644 packages/nuget/Content/Scripts/lib/knockback-sample-transitions-jquery.min.js create mode 100644 src/knockback-page-navigator-panes/module-loader.js create mode 100644 src/knockback-page-navigator-simple/module-loader.js create mode 100644 src/knockback-pane-navigator/module-loader.js create mode 100644 src/knockback-sample-transitions-jquery/component-imports.coffee rename src/{knockback-sample-transitions => knockback-sample-transitions-jquery}/knockback-transition-cover-vertical.coffee (100%) rename src/{knockback-sample-transitions => knockback-sample-transitions-jquery}/knockback-transition-fade-in.coffee (100%) rename src/{knockback-sample-transitions => knockback-sample-transitions-jquery}/knockback-transition-helpers.coffee (100%) rename src/{knockback-sample-transitions => knockback-sample-transitions-jquery}/knockback-transition-navigation-slide.coffee (100%) create mode 100644 src/knockback-sample-transitions-jquery/module-loader.js delete mode 100644 src/license-header.coffee delete mode 100644 test/vendor/knockback-core-stack-0.16.3.js delete mode 100644 test/vendor/knockback-core-stack-0.16.4.js diff --git a/Bakefile.coffee b/Bakefile.coffee index 2bbe4bf..d7b0c24 100644 --- a/Bakefile.coffee +++ b/Bakefile.coffee @@ -1,9 +1,9 @@ module.exports = knockback_page_navigator_panes: join: 'knockback-page-navigator-panes.js' + wrapper: 'src/knockback-page-navigator-panes/module-loader.js' compress: true files: [ - 'src/license-header.coffee' 'src/component-imports.coffee' 'src/knockback-page-navigator-panes/knockback-page-navigator-panes.coffee' 'src/knockback-page-navigator-panes/knockout-bindings.coffee' @@ -19,9 +19,9 @@ module.exports = knockback_page_navigator_simple: join: 'knockback-page-navigator-simple.js' + wrapper: 'src/knockback-page-navigator-simple/module-loader.js' compress: true files: [ - 'src/license-header.coffee' 'src/component-imports.coffee' 'src/knockback-page-navigator-simple/knockback-page-navigator-simple.coffee' 'src/knockback-page-navigator-simple/knockout-bindings.coffee' @@ -31,9 +31,9 @@ module.exports = knockback_pane_navigator: join: 'knockback-pane-navigator.js' + wrapper: 'src/knockback-pane-navigator/module-loader.js' compress: true files: [ - 'src/license-header.coffee' 'src/component-imports.coffee' 'src/knockback-pane-navigator/knockback-pane-navigator.coffee' 'src/knockback-pane-navigator/knockback-pane-navigator-helpers.coffee' @@ -42,16 +42,17 @@ module.exports = 'src/shared/knockback-transition-saved-state.coffee' ] - knockback_sample_transitions: - join: 'knockback-sample-transitions.js' + knockback_sample_transitions_jquery: + join: 'knockback-sample-transitions-jquery.js' + wrapper: 'src/knockback-sample-transitions-jquery/module-loader.js' output: 'lib' compress: true files: [ - 'src/license-header.coffee' - 'src/knockback-sample-transitions/knockback-transition-helpers.coffee' - 'src/knockback-sample-transitions/knockback-transition-cover-vertical.coffee' - 'src/knockback-sample-transitions/knockback-transition-fade-in.coffee' - 'src/knockback-sample-transitions/knockback-transition-navigation-slide.coffee' + 'src/knockback-sample-transitions-jquery/component-imports.coffee' + 'src/knockback-sample-transitions-jquery/knockback-transition-helpers.coffee' + 'src/knockback-sample-transitions-jquery/knockback-transition-cover-vertical.coffee' + 'src/knockback-sample-transitions-jquery/knockback-transition-fade-in.coffee' + 'src/knockback-sample-transitions-jquery/knockback-transition-navigation-slide.coffee' ] publishing: @@ -70,8 +71,8 @@ module.exports = 'cp knockback-page-navigator-simple.min.js packages/npm/knockback-page-navigator-simple.min.js' 'cp knockback-pane-navigator.js packages/npm/knockback-pane-navigator.js' 'cp knockback-pane-navigator.min.js packages/npm/knockback-pane-navigator.min.js' - 'cp lib/knockback-sample-transitions.js packages/npm/lib/knockback-sample-transitions.js' - 'cp lib/knockback-sample-transitions.min.js packages/npm/lib/knockback-sample-transitions.min.js' + 'cp lib/knockback-sample-transitions-jquery.js packages/npm/lib/knockback-sample-transitions-jquery.js' + 'cp lib/knockback-sample-transitions-jquery.min.js packages/npm/lib/knockback-sample-transitions-jquery.min.js' # nuget 'cp knockback-navigators.css packages/nuget/Content/Scripts/knockback-navigators.css' @@ -81,8 +82,8 @@ module.exports = 'cp knockback-page-navigator-simple.min.js packages/nuget/Content/Scripts/knockback-page-navigator-simple.min.js' 'cp knockback-pane-navigator.js packages/nuget/Content/Scripts/knockback-pane-navigator.js' 'cp knockback-pane-navigator.min.js packages/nuget/Content/Scripts/knockback-pane-navigator.min.js' - 'cp lib/knockback-sample-transitions.js packages/nuget/Content/Scripts/lib/knockback-sample-transitions.js' - 'cp lib/knockback-sample-transitions.min.js packages/nuget/Content/Scripts/lib/knockback-sample-transitions.min.js' + 'cp lib/knockback-sample-transitions-jquery.js packages/nuget/Content/Scripts/lib/knockback-sample-transitions-jquery.js' + 'cp lib/knockback-sample-transitions-jquery.min.js packages/nuget/Content/Scripts/lib/knockback-sample-transitions-jquery.min.js' ] tests: diff --git a/README.md b/README.md index d627896..9054761 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Please see the [release notes](https://github.com/kmalakoff/knockback-navigators **Optional/Non production** -* knockback-sample-transitions.js [(min)](https://raw.github.com/kmalakoff/knockback-navigators/0.1.1/lib/knockback-sample-transitions.js) or [(dev)](https://raw.github.com/kmalakoff/knockback-navigators/0.1.1/lib/knockback-sample-transitions.min.js) +* knockback-sample-transitions-jquery.js [(min)](https://raw.github.com/kmalakoff/knockback-navigators/0.1.1/lib/knockback-sample-transitions-jquery.js) or [(dev)](https://raw.github.com/kmalakoff/knockback-navigators/0.1.1/lib/knockback-sample-transitions-jquery.min.js) If you have some production quality transitions that you'd like to submit to the library, please [let me know](http://kmalakoff.github.com/knockback/issues). diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1463a06..bf46f9e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,6 +3,7 @@ Please refer to the following release notes when upgrading your version of Knock ## 0.1.1 * updated kb.loadUrl and added kb.loadUrlFn so they can be called directly from an HTML View +* renamed knockback-sample-transitions to knockback-sample-transitions-jquery ## 0.1.0 diff --git a/examples/pn-nt+_xui.html b/examples/pn-nt+_xui.html index 249f995..fd8285e 100644 --- a/examples/pn-nt+_xui.html +++ b/examples/pn-nt+_xui.html @@ -39,7 +39,7 @@ - + - + - + - + - + - + - + - + - + - + - + "); - }; - - if (jQueryTmplVersion > 0) { - jQuery['tmpl']['tag']['ko_code'] = { - open: "__.push($1 || '');" - }; - jQuery['tmpl']['tag']['ko_with'] = { - open: "with($1) {", - close: "} " - }; - } - }; - - ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine(); - - // Use this one by default *only if jquery.tmpl is referenced* - var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine(); - if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0) - ko.setTemplateEngine(jqueryTmplTemplateEngineInstance); - - ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine); -})(); -}); -})(window,document,navigator); -/* - knockback.js 0.16.4 - (c) 2011, 2012 Kevin Malakoff. - Knockback.js is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE - Dependencies: Knockout.js, Backbone.js, and Underscore.js. -*/ - -(function() { - return (function(factory) { - // AMD - if (typeof define === 'function' && define.amd) { - return define('knockback', ['underscore', 'backbone', 'knockout'], factory); - } - // CommonJS/NodeJS or No Loader - else { - return factory.call(this); - } - })(function() {// Generated by CoffeeScript 1.3.3 -/* - knockback-core.js 0.16.4 - (c) 2011, 2012 Kevin Malakoff. - Knockback.js is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE - Dependencies: Knockout.js, Backbone.js, and Underscore.js. -*/ - -var Backbone, KB_TYPE_ARRAY, KB_TYPE_COLLECTION, KB_TYPE_MODEL, KB_TYPE_SIMPLE, KB_TYPE_UNKNOWN, addStatisticsEvent, arraySplice, collapseOptions, kb, ko, legacyWarning, onReady, throwMissing, throwUnexpected, _, _argumentsAddKey, _unwrapModels, _wrappedKey, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - -kb = (function() { - - function kb() {} - - kb.VERSION = '0.16.4'; - - kb.TYPE_UNKNOWN = 0; - - kb.TYPE_SIMPLE = 1; - - kb.TYPE_ARRAY = 2; - - kb.TYPE_MODEL = 3; - - kb.TYPE_COLLECTION = 4; - - kb.release = function(obj, pre_release_fn) { - var array, item, view_model, view_models, _i, _j, _len, _len1; - if ((!obj || (obj !== Object(obj))) || ((typeof obj === 'function') && !ko.isObservable(obj)) || obj.__kb_destroyed || ((obj instanceof Backbone.Model) || (obj instanceof Backbone.Collection))) { - return this; - } - if (_.isArray(obj)) { - array = obj.splice(0, obj.length); - for (_i = 0, _len = array.length; _i < _len; _i++) { - item = array[_i]; - kb.release(item); - } - return this; - } - obj.__kb_destroyed = true; - !pre_release_fn || pre_release_fn(); - if (ko.isObservable(obj) || (typeof obj.dispose === 'function') || (typeof obj.destroy === 'function') || (typeof obj.release === 'function')) { - if (ko.isObservable(obj) && _.isArray(array = obj())) { - if (obj.__kb_is_co || (obj.__kb_is_o && (obj.valueType() === KB_TYPE_COLLECTION))) { - if (obj.destroy) { - obj.destroy(); - } else if (obj.dispose) { - obj.dispose(); - } - } else if (array.length) { - view_models = array.slice(0); - array.splice(0, array.length); - for (_j = 0, _len1 = view_models.length; _j < _len1; _j++) { - view_model = view_models[_j]; - kb.release(view_model); - } - } - } else if (obj.release) { - obj.release(); - } else if (obj.destroy) { - obj.destroy(); - } else if (obj.dispose) { - obj.dispose(); - } - } else { - this.releaseKeys(obj); - } - return this; - }; - - kb.releaseKeys = function(obj) { - var key, value; - for (key in obj) { - value = obj[key]; - (key === '__kb') || kb.release(value, (function() { - return obj[key] = null; - })); - } - return this; - }; - - kb.releaseOnNodeRemove = function(view_model, node) { - view_model || throwUnexpected(this, 'missing view model'); - node || throwUnexpected(this, 'missing node'); - return ko.utils.domNodeDisposal.addDisposeCallback(node, function() { - return kb.release(view_model); - }); - }; - - kb.renderTemplate = function(template, view_model, options) { - var el, observable; - if (options == null) { - options = {}; - } - el = document.createElement('div'); - observable = ko.renderTemplate(template, view_model, options, el, 'replaceChildren'); - if (el.children.length === 1) { - el = el.children[0]; - } - kb.releaseOnNodeRemove(view_model, el); - observable.dispose(); - return el; - }; - - kb.renderAutoReleasedTemplate = function(template, view_model, options) { - if (options == null) { - options = {}; - } - legacyWarning('kb.renderAutoReleasedTemplate', '0.16.4', 'Please use kb.renderTemplate instead'); - return this.renderTemplate(template, view_model, options = {}); - }; - - kb.applyBindings = function(view_model, node) { - ko.applyBindings(view_model, node); - return kb.releaseOnNodeRemove(view_model, node); - }; - - return kb; - -})(); - -this.Knockback = this.kb = kb; - -if (typeof exports !== 'undefined') { - module.exports = kb; -} - -if (!this._ && (typeof require !== 'undefined')) { - try { - _ = require('lodash'); - } catch (e) { - _ = require('underscore'); - } -} else { - _ = this._; -} - -kb._ = _ = _.hasOwnProperty('_') ? _._ : _; - -kb.Backbone = Backbone = !this.Backbone && (typeof require !== 'undefined') ? require('backbone') : this.Backbone; - -kb.ko = ko = !this.ko && (typeof require !== 'undefined') ? require('knockout') : this.ko; - -throwMissing = function(instance, message) { - throw "" + instance.constructor.name + ": " + message + " is missing"; -}; - -throwUnexpected = function(instance, message) { - throw "" + instance.constructor.name + ": " + message + " is unexpected"; -}; - -legacyWarning = function(identifier, last_version, message) { - var _base; - this._legacy_warnings || (this._legacy_warnings = {}); - (_base = this._legacy_warnings)[identifier] || (_base[identifier] = 0); - this._legacy_warnings[identifier]++; - return console.warn("warning: '" + identifier + "' has been deprecated (will be removed in Knockback after " + last_version + "). " + message + "."); -}; - -arraySplice = Array.prototype.splice; - -collapseOptions = function(options) { - var result; - result = _.clone(options); - while (options.options) { - _.defaults(result, options.options); - options = options.options; - } - delete result.options; - return result; -}; - -KB_TYPE_UNKNOWN = kb.TYPE_UNKNOWN; - -KB_TYPE_SIMPLE = kb.TYPE_SIMPLE; - -KB_TYPE_ARRAY = kb.TYPE_ARRAY; - -KB_TYPE_MODEL = kb.TYPE_MODEL; - -KB_TYPE_COLLECTION = kb.TYPE_COLLECTION; - -/* - knockback-utils.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.js is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE - Dependencies: Knockout.js, Backbone.js, and Underscore.js. - Optional dependency: Backbone.ModelRef.js. -*/ - - -_wrappedKey = function(obj, key, value) { - if (arguments.length === 2) { - if (obj && obj.__kb && obj.__kb.hasOwnProperty(key)) { - return obj.__kb[key]; - } else { - return void 0; - } - } - obj || throwUnexpected(this, "no obj for wrapping " + key); - obj.__kb || (obj.__kb = {}); - obj.__kb[key] = value; - return value; -}; - -_argumentsAddKey = function(args, key) { - arraySplice.call(args, 1, 0, key); - return args; -}; - -_unwrapModels = function(obj) { - var key, result, value; - if (!obj) { - return obj; - } else if (obj.__kb) { - if ('object' in obj.__kb) { - return obj.__kb.object; - } else { - return obj; - } - } else if (_.isArray(obj)) { - return _.map(obj, function(test) { - return _unwrapModels(test); - }); - } else if (_.isObject(obj) && !ko.isObservable(obj) && !_.isDate(obj) && !_.isString(obj)) { - result = {}; - for (key in obj) { - value = obj[key]; - result[key] = _unwrapModels(value); - } - return result; - } else { - return obj; - } -}; - -kb.utils = (function() { - - function utils() {} - - utils.wrappedObservable = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'observable')); - }; - - utils.wrappedObject = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'object')); - }; - - utils.wrappedModel = function(obj, value) { - if (arguments.length === 1) { - value = _wrappedKey(obj, 'object'); - if (_.isUndefined(value)) { - return obj; - } else { - return value; - } - } else { - return _wrappedKey(obj, 'object', value); - } - }; - - utils.wrappedStore = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'store')); - }; - - utils.wrappedStoreIsOwned = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'store_is_owned')); - }; - - utils.wrappedFactory = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'factory')); - }; - - utils.wrappedModelWatcher = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'model_watcher')); - }; - - utils.wrappedModelWatcherIsOwned = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'model_watcher_is_owned')); - }; - - utils.wrappedDestroy = function(obj) { - var __kb; - if (!obj.__kb) { - return; - } - if (obj.__kb.model_watcher) { - obj.__kb.model_watcher.releaseCallbacks(obj); - } - __kb = obj.__kb; - obj.__kb = null; - if (__kb.observable) { - __kb.observable.destroy = __kb.observable.release = null; - this.wrappedDestroy(__kb.observable); - __kb.observable = null; - } - __kb.factory = null; - if (__kb.model_watcher_is_owned) { - __kb.model_watcher.destroy(); - } - __kb.model_watcher = null; - if (__kb.store_is_owned) { - __kb.store.destroy(); - } - return __kb.store = null; - }; - - utils.valueType = function(observable) { - if (!observable) { - return KB_TYPE_UNKNOWN; - } - if (observable.__kb_is_o) { - return observable.valueType(); - } - if (observable.__kb_is_co || (observable instanceof Backbone.Collection)) { - return KB_TYPE_COLLECTION; - } - if ((observable instanceof kb.ViewModel) || (observable instanceof Backbone.Model)) { - return KB_TYPE_MODEL; - } - if (_.isArray(observable)) { - return KB_TYPE_ARRAY; - } - return KB_TYPE_SIMPLE; - }; - - utils.pathJoin = function(path1, path2) { - return (path1 ? (path1[path1.length - 1] !== '.' ? "" + path1 + "." : path1) : '') + path2; - }; - - utils.optionsPathJoin = function(options, path) { - return _.defaults({ - path: this.pathJoin(options.path, path) - }, options); - }; - - utils.inferCreator = function(value, factory, path, owner, key) { - var creator, relation; - if (factory) { - creator = factory.creatorForPath(value, path); - } - if (creator) { - return creator; - } - if (owner && Backbone.RelationalModel && (owner instanceof Backbone.RelationalModel)) { - key = ko.utils.unwrapObservable(key); - relation = _.find(owner.getRelations(), function(test) { - return test.key === key; - }); - if (relation) { - if (relation.collectionType || _.isArray(relation.keyContents)) { - return kb.CollectionObservable; - } else { - return kb.ViewModel; - } - } - } - if (!value) { - return null; - } - if (value instanceof Backbone.Model) { - return kb.ViewModel; - } - if (value instanceof Backbone.Collection) { - return kb.CollectionObservable; - } - return null; - }; - - utils.createFromDefaultCreator = function(obj, options) { - if (obj instanceof Backbone.Model) { - return kb.viewModel(obj, options); - } - if (obj instanceof Backbone.Collection) { - return kb.collectionObservable(obj, options); - } - if (_.isArray(obj)) { - return ko.observableArray(obj); - } - return ko.observable(obj); - }; - - utils.release = function(obj) { - legacyWarning('kb.utils.release', '0.16.4', 'Please use kb.release instead'); - return kb.release(obj); - }; - - return utils; - -})(); - -/* - knockback_factory.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Factory is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.Factory = (function() { - - Factory.useOptionsOrCreate = function(options, obj, owner_path) { - var factory; - if (options.factory && (!options.factories || (options.factories && options.factory.hasPathMappings(options.factories, owner_path)))) { - return kb.utils.wrappedFactory(obj, options.factory); - } - factory = kb.utils.wrappedFactory(obj, new kb.Factory(options.factory)); - if (options.factories) { - factory.addPathMappings(options.factories, owner_path); - } - return factory; - }; - - function Factory(parent_factory) { - this.parent_factory = parent_factory; - this.paths = {}; - } - - Factory.prototype.hasPath = function(path) { - return this.paths.hasOwnProperty(path) || (this.parent_factory && this.parent_factory.hasPath(path)); - }; - - Factory.prototype.addPathMapping = function(path, create_info) { - return this.paths[path] = create_info; - }; - - Factory.prototype.addPathMappings = function(factories, owner_path) { - var create_info, path; - for (path in factories) { - create_info = factories[path]; - this.paths[kb.utils.pathJoin(owner_path, path)] = create_info; - } - return this; - }; - - Factory.prototype.hasPathMappings = function(factories, owner_path) { - var all_exist, creator, existing_creator, path; - all_exist = true; - for (path in factories) { - creator = factories[path]; - all_exist &= (existing_creator = this.creatorForPath(null, kb.utils.pathJoin(owner_path, path))) && (creator === existing_creator); - } - return all_exist; - }; - - Factory.prototype.creatorForPath = function(obj, path) { - var creator; - if ((creator = this.paths[path])) { - if (creator.view_model) { - return creator.view_model; - } else { - return creator; - } - } - if (this.parent_factory) { - if ((creator = this.parent_factory.creatorForPath(obj, path))) { - return creator; - } - } - return null; - }; - - return Factory; - -})(); - -/* - knockback_store.js - (c) 2012 Kevin Malakoff. - Knockback.Store is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.Store = (function() { - - Store.useOptionsOrCreate = function(options, obj, observable) { - if (options.store) { - options.store.register(obj, observable, options); - return kb.utils.wrappedStore(observable, options.store); - } else { - kb.utils.wrappedStoreIsOwned(observable, true); - return kb.utils.wrappedStore(observable, new kb.Store()); - } - }; - - function Store() { - this.observable_records = []; - this.replaced_observables = []; - } - - Store.prototype.destroy = function() { - var observable, record, _i, _j, _len, _len1, _ref, _ref1; - _ref = this.observable_records; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - record = _ref[_i]; - kb.release(record.observable); - } - _ref1 = this.replaced_observables; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - observable = _ref1[_j]; - kb.release(observable); - } - this.observable_records = null; - return this.replaced_observables = null; - }; - - Store.prototype.register = function(obj, observable, options) { - var creator; - if (!observable) { - return; - } - if (ko.isObservable(observable) || observable.__kb_is_co) { - return; - } - kb.utils.wrappedObject(observable, obj); - if (!obj) { - observable.__kb_null = true; - } - creator = options.creator ? options.creator : (options.path && options.factory ? options.factory.creatorForPath(obj, options.path) : null); - if (!creator) { - creator = observable.constructor; - } - this.observable_records.push({ - obj: obj, - observable: observable, - creator: creator - }); - return observable; - }; - - Store.prototype.findIndex = function(obj, creator) { - var index, record, _ref; - if (!obj || (obj instanceof Backbone.Model)) { - _ref = this.observable_records; - for (index in _ref) { - record = _ref[index]; - if (!record.observable) { - continue; - } - if (record.observable.__kb_destroyed) { - record.obj = null; - record.observable = null; - continue; - } - if ((!obj && !record.observable.__kb_null) || (obj && (record.observable.__kb_null || (record.obj !== obj)))) { - continue; - } else if ((record.creator === creator) || (record.creator.create && (record.creator.create === creator.create))) { - return index; - } - } - } - return -1; - }; - - Store.prototype.find = function(obj, creator) { - var index; - if ((index = this.findIndex(obj, creator)) < 0) { - return null; - } else { - return this.observable_records[index].observable; - } - }; - - Store.prototype.isRegistered = function(observable) { - var record, _i, _len, _ref; - _ref = this.observable_records; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - record = _ref[_i]; - if (record.observable === observable) { - return true; - } - } - return false; - }; - - Store.prototype.findOrCreate = function(obj, options) { - var creator, observable; - options.store = this; - options.creator || (options.creator = kb.utils.inferCreator(obj, options.factory, options.path)); - if (!options.creator && (obj instanceof Backbone.Model)) { - options.creator = kv.ViewModel; - } - creator = options.creator; - if (!creator) { - return kb.utils.createFromDefaultCreator(obj, options); - } else if (creator.models_only) { - return obj; - } - if (creator) { - observable = this.find(obj, creator); - } - if (observable) { - return observable; - } - if (creator.create) { - observable = creator.create(obj, options); - } else { - observable = new creator(obj, options); - } - observable || (observable = ko.observable(null)); - if (!ko.isObservable(observable)) { - this.isRegistered(observable) || this.register(obj, observable, options); - } - return observable; - }; - - Store.prototype.findOrReplace = function(obj, creator, observable) { - var index, record; - obj || raiseUnexpected('obj missing'); - if ((index = this.findIndex(obj, creator)) < 0) { - return this.register(obj, observable, { - creator: creator - }); - } else { - record = this.observable_records[index]; - (kb.utils.wrappedObject(record.observable) === obj) || throwUnexpected(this, 'different object'); - if (record.observable !== observable) { - (record.observable.constructor === observable.constructor) || throwUnexpected(this, 'replacing different type'); - this.replaced_observables.push(record.observable); - record.observable = observable; - } - return observable; - } - }; - - return Store; - -})(); - -/* - knockback_model_watcher.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Observable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -addStatisticsEvent = function(model, event_name, info) { - return !kb.statistics || kb.statistics.addModelEvent({ - name: event_name, - model: model, - key: info.key, - path: info.path - }); -}; - -kb.ModelWatcher = (function() { - - ModelWatcher.useOptionsOrCreate = function(options, model, obj, callback_options) { - if (options.model_watcher) { - if (!(options.model_watcher.model() === model || (options.model_watcher.model_ref === model))) { - throwUnexpected(this, 'model not matching'); - } - return kb.utils.wrappedModelWatcher(obj, options.model_watcher).registerCallbacks(obj, callback_options); - } else { - kb.utils.wrappedModelWatcherIsOwned(obj, true); - return kb.utils.wrappedModelWatcher(obj, new kb.ModelWatcher(model)).registerCallbacks(obj, callback_options); - } - }; - - function ModelWatcher(model, obj, callback_options) { - this._onModelUnloaded = __bind(this._onModelUnloaded, this); - - this._onModelLoaded = __bind(this._onModelLoaded, this); - this.__kb || (this.__kb = {}); - this.__kb.callbacks = {}; - this.__kb._onModelLoaded = _.bind(this._onModelLoaded, this); - this.__kb._onModelUnloaded = _.bind(this._onModelUnloaded, this); - if (callback_options) { - this.registerCallbacks(obj, callback_options); - } - if (model) { - this.model(model); - } else { - this.m = null; - } - } - - ModelWatcher.prototype.destroy = function() { - this.model(null); - this.__kb.callbacks = null; - return kb.utils.wrappedDestroy(this); - }; - - ModelWatcher.prototype.model = function(new_model) { - var callbacks, event_name, info, list, previous_model, _i, _len, _ref; - if ((arguments.length === 0) || (this.m === new_model)) { - return this.m; - } - if (this.model_ref) { - this.model_ref.unbind('loaded', this.__kb._onModelLoaded); - this.model_ref.unbind('unloaded', this.__kb._onModelUnloaded); - this.model_ref.release(); - this.model_ref = null; - } - if (Backbone.ModelRef && (new_model instanceof Backbone.ModelRef)) { - this.model_ref = new_model; - this.model_ref.retain(); - this.model_ref.bind('loaded', this.__kb._onModelLoaded); - this.model_ref.bind('unloaded', this.__kb._onModelUnloaded); - new_model = this.model_ref.model(); - } else { - delete this.model_ref; - } - previous_model = this.m; - this.m = new_model; - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - if (previous_model) { - previous_model.unbind(event_name, callbacks.fn); - } - if (new_model) { - new_model.bind(event_name, callbacks.fn); - } - list = callbacks.list; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (info.model) { - info.model(new_model); - } - } - } - return new_model; - }; - - ModelWatcher.prototype.registerCallbacks = function(obj, callback_info) { - var callbacks, event_name, info, list; - obj || throwMissing(this, 'obj'); - callback_info || throwMissing(this, 'info'); - event_name = callback_info.event_name ? callback_info.event_name : 'change'; - callbacks = this.__kb.callbacks[event_name]; - if (!callbacks) { - list = []; - callbacks = { - list: list, - fn: function(model) { - var info, _i, _len; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (info.update && !info.rel_fn) { - if (model && info.key && (model.hasChanged && !model.hasChanged(ko.utils.unwrapObservable(info.key)))) { - continue; - } - !kb.statistics || addStatisticsEvent(model, event_name, info); - info.update(); - } - } - return null; - } - }; - this.__kb.callbacks[event_name] = callbacks; - if (this.m) { - this.m.bind(event_name, callbacks.fn); - } - } - info = _.defaults({ - obj: obj - }, callback_info); - callbacks.list.push(info); - if (this.m) { - if (Backbone.RelationalModel && (this.m instanceof Backbone.RelationalModel)) { - this._modelBindRelatationalInfo(event_name, info); - } - info.model(this.m) && info.model; - } - return this; - }; - - ModelWatcher.prototype.releaseCallbacks = function(obj) { - var callbacks, event_name, index, info, _ref, _ref1; - if (!this.__kb.callbacks) { - return; - } - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - _ref1 = callbacks.list; - for (index in _ref1) { - info = _ref1[index]; - if (info.obj === obj) { - callbacks.list.splice(index, 1); - if (info.rel_fn) { - this._modelUnbindRelatationalInfo(event_name, info); - } - if (info.model) { - info.model(null); - } - return; - } - } - } - }; - - ModelWatcher.prototype._onModelLoaded = function(model) { - var callbacks, event_name, info, is_relational, list, _i, _len, _ref; - is_relational = Backbone.RelationalModel && (model instanceof Backbone.RelationalModel); - this.m = model; - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - model.bind(event_name, callbacks.fn); - list = callbacks.list; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (is_relational) { - this._modelBindRelatationalInfo(event_name, info); - } - if (info.model) { - info.model(model); - } - } - } - return this; - }; - - ModelWatcher.prototype._onModelUnloaded = function(model) { - var callbacks, event_name, info, list, _i, _len, _ref; - this.m = null; - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - model.unbind(event_name, callbacks.fn); - list = callbacks.list; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (info.rel_fn) { - this._modelUnbindRelatationalInfo(event_name, info); - } - if (info.model) { - info.model(null); - } - } - } - return this; - }; - - ModelWatcher.prototype._modelBindRelatationalInfo = function(event_name, info) { - var key, relation; - if ((event_name === 'change') && info.key && info.update) { - key = ko.utils.unwrapObservable(info.key); - relation = _.find(this.m.getRelations(), function(test) { - return test.key === key; - }); - if (!relation) { - return; - } - info.rel_fn = function(model) { - !kb.statistics || addStatisticsEvent(model, "" + event_name + " (relational)", info); - return info.update(); - }; - if (relation.collectionType || _.isArray(relation.keyContents)) { - info.is_collection = true; - this.m.bind("add:" + info.key, info.rel_fn); - this.m.bind("remove:" + info.key, info.rel_fn); - } else { - this.m.bind("update:" + info.key, info.rel_fn); - } - } - return this; - }; - - ModelWatcher.prototype._modelUnbindRelatationalInfo = function(event_name, info) { - if (!info.rel_fn) { - return; - } - if (info.is_collection) { - this.m.unbind("add:" + info.key, info.rel_fn); - this.m.unbind("remove:" + info.key, info.rel_fn); - } else { - this.m.unbind("update:" + info.key, info.rel_fn); - } - info.rel_fn = null; - return this; - }; - - return ModelWatcher; - -})(); - -kb.modelObservable = function(model, observable) { - return new kb.ModelWatcher(model, observable); -}; - -/* - knockback-observable.js - (c) 2012 Kevin Malakoff. - Knockback.Observable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.Observable = (function() { - - function Observable(model, options, vm) { - var create_options, model_watcher, observable, - _this = this; - this.vm = vm; - options || throwMissing(this, 'options'); - this.vm || (this.vm = {}); - if (_.isString(options) || ko.isObservable(options)) { - create_options = this.create_options = { - key: options - }; - } else { - create_options = this.create_options = collapseOptions(options); - } - this.key = create_options.key; - delete create_options.key; - this.key || throwMissing(this, 'key'); - !create_options.args || (this.args = create_options.args, delete create_options.args); - !create_options.read || (this.read = create_options.read, delete create_options.read); - !create_options.write || (this.write = create_options.write, delete create_options.write); - model_watcher = create_options.model_watcher; - delete create_options.model_watcher; - this.vo = ko.observable(null); - observable = kb.utils.wrappedObservable(this, ko.dependentObservable({ - read: function() { - var arg, args, new_value, _i, _len, _ref; - args = [ko.utils.unwrapObservable(_this.key)]; - if (_this.args) { - if (_.isArray(_this.args)) { - _ref = _this.args; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - arg = _ref[_i]; - args.push(ko.utils.unwrapObservable(arg)); - } - } else { - args.push(ko.utils.unwrapObservable(_this.args)); - } - } - if (_this.m) { - new_value = _this.read ? _this.read.apply(_this.vm, args) : _this.m.get.apply(_this.m, args); - _this.update(new_value); - } - return ko.utils.unwrapObservable(_this.vo()); - }, - write: function(new_value) { - var arg, args, set_info, unwrapped_new_value, _i, _len, _ref; - unwrapped_new_value = _unwrapModels(new_value); - set_info = {}; - set_info[ko.utils.unwrapObservable(_this.key)] = unwrapped_new_value; - args = _this.write ? [unwrapped_new_value] : [set_info]; - if (_this.args) { - if (_.isArray(_this.args)) { - _ref = _this.args; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - arg = _ref[_i]; - args.push(ko.utils.unwrapObservable(arg)); - } - } else { - args.push(ko.utils.unwrapObservable(_this.args)); - } - } - if (_this.m) { - if (_this.write) { - _this.write.apply(_this.vm, args); - } else { - _this.m.set.apply(_this.m, args); - } - } - return _this.update(new_value); - }, - owner: this.vm - })); - observable.__kb_is_o = true; - create_options.store = kb.utils.wrappedStore(observable, create_options.store); - create_options.path = kb.utils.pathJoin(create_options.path, this.key); - if (create_options.factories && ((typeof create_options.factories === 'function') || create_options.factories.create)) { - create_options.factory = kb.utils.wrappedFactory(observable, new kb.Factory(create_options.factory)); - create_options.factory.addPathMapping(create_options.path, create_options.factories); - } else { - create_options.factory = kb.Factory.useOptionsOrCreate(create_options, observable, create_options.path); - } - delete create_options.factories; - observable.value = _.bind(this.value, this); - observable.valueType = _.bind(this.valueType, this); - observable.destroy = _.bind(this.destroy, this); - kb.ModelWatcher.useOptionsOrCreate({ - model_watcher: model_watcher - }, model, this, { - model: _.bind(this.model, this), - update: _.bind(this.update, this), - key: this.key, - path: create_options.path - }); - this.__kb_value || this.update(); - if (kb.LocalizedObservable && create_options.localizer) { - observable = new create_options.localizer(observable); - delete create_options.localizer; - } - if (kb.DefaultObservable && create_options.hasOwnProperty('default')) { - observable = kb.defaultObservable(observable, create_options["default"]); - delete create_options["default"]; - } - return observable; - } - - Observable.prototype.destroy = function() { - this.__kb_destroyed = true; - kb.release(this.__kb_value); - this.__kb_value = null; - this.vm = null; - this.create_options = null; - return kb.utils.wrappedDestroy(this); - }; - - Observable.prototype.value = function() { - return this.__kb_value; - }; - - Observable.prototype.valueType = function() { - var new_value; - new_value = this.m ? this.m.get(this.key) : null; - this.value_type || this._updateValueObservable(new_value); - return this.value_type; - }; - - Observable.prototype.model = function(new_model) { - if ((arguments.length === 0) || (this.m === new_model)) { - return this.m; - } - this.m = new_model; - return this.__kb_destroyed || this.update(); - }; - - Observable.prototype.update = function(new_value) { - var new_type, value; - if (this.m && !arguments.length) { - new_value = this.m.get(ko.utils.unwrapObservable(this.key)); - } - new_value || (new_value = null); - new_type = kb.utils.valueType(new_value); - if (!this.__kb_value || (this.__kb_value.__kb_destroyed || (this.__kb_value.__kb_null && new_value))) { - this.__kb_value = null; - this.value_type = void 0; - } - value = this.__kb_value; - if (_.isUndefined(this.value_type) || (this.value_type !== new_type && new_type !== KB_TYPE_UNKNOWN)) { - if ((this.value_type === KB_TYPE_COLLECTION) && (new_type === KB_TYPE_ARRAY)) { - return value(new_value); - } else { - return this._updateValueObservable(new_value); - } - } else if (this.value_type === KB_TYPE_MODEL) { - if (typeof value.model === 'function') { - if (value.model() !== new_value) { - return value.model(new_value); - } - } else if (kb.utils.wrappedObject(value) !== new_value) { - return this._updateValueObservable(new_value); - } - } else if (this.value_type === KB_TYPE_COLLECTION) { - if (value.collection() !== new_value) { - return value.collection(new_value); - } - } else { - if (value() !== new_value) { - return value(new_value); - } - } - }; - - Observable.prototype._updateValueObservable = function(new_value) { - var create_options, creator, previous_value, value; - create_options = this.create_options; - create_options.creator = kb.utils.inferCreator(new_value, create_options.factory, create_options.path, this.m, this.key); - this.value_type = KB_TYPE_UNKNOWN; - creator = create_options.creator; - previous_value = this.__kb_value; - this.__kb_value = null; - if (previous_value) { - kb.release(previous_value); - } - if (creator) { - if (create_options.store) { - value = create_options.store.findOrCreate(new_value, create_options); - } else { - if (creator.models_only) { - value = new_value; - this.value_type = KB_TYPE_SIMPLE; - } else if (creator.create) { - value = creator.create(new_value, create_options); - } else { - value = new creator(new_value, create_options); - } - } - } else { - this.value_type = KB_TYPE_SIMPLE; - if (_.isArray(new_value)) { - value = ko.observableArray(new_value); - } else { - value = ko.observable(new_value); - } - } - if (this.value_type === KB_TYPE_UNKNOWN) { - if (!ko.isObservable(value)) { - this.value_type = KB_TYPE_MODEL; - if (typeof value.model !== 'function') { - kb.utils.wrappedObject(value, new_value); - } - } else if (value.__kb_is_co) { - this.value_type = KB_TYPE_COLLECTION; - } else { - this.value_type = KB_TYPE_SIMPLE; - } - } - this.__kb_value = value; - return this.vo(value); - }; - - return Observable; - -})(); - -kb.observable = function(model, options, view_model) { - return new kb.Observable(model, options, view_model); -}; - -/* - knockback-view-model.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Observable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.ViewModel = (function() { - - ViewModel.extend = Backbone.Model.extend; - - function ViewModel(model, options, view_model) { - var attribute_keys, bb_model, keys, mapped_keys, mapping_info, model_watcher, vm_key, _ref; - !model || (model instanceof Backbone.Model) || ((typeof model.get === 'function') && (typeof model.bind === 'function')) || throwUnexpected(this, 'not a model'); - options || (options = {}); - view_model || (view_model = {}); - if (_.isArray(options)) { - options = { - keys: options - }; - } else { - options = collapseOptions(options); - } - this.__kb || (this.__kb = {}); - this.__kb.vm_keys = {}; - this.__kb.model_keys = {}; - this.__kb.view_model = _.isUndefined(view_model) ? this : view_model; - !options.internals || (this.__kb.internals = options.internals); - !options.excludes || (this.__kb.excludes = options.excludes); - kb.Store.useOptionsOrCreate(options, model, this); - this.__kb.path = options.path; - kb.Factory.useOptionsOrCreate(options, this, options.path); - model_watcher = kb.utils.wrappedModelWatcher(this, new kb.ModelWatcher(model, this, { - model: _.bind(this.model, this) - })); - if (options.requires && _.isArray(options.requires)) { - keys = _.clone(options.requires); - } - if (this.__kb.internals) { - keys = keys ? _.union(keys, this.__kb.internals) : _.clone(this.__kb.internals); - } - if (options.keys) { - if (_.isArray(options.keys)) { - this.__kb.keys = options.keys; - keys = keys ? _.union(keys, options.keys) : _.clone(options.keys); - } else { - mapped_keys = {}; - _ref = options.keys; - for (vm_key in _ref) { - mapping_info = _ref[vm_key]; - mapped_keys[_.isString(mapping_info) ? mapping_info : (mapping_info.key ? mapping_info.key : vm_key)] = true; - } - this.__kb.keys = _.keys(mapped_keys); - } - } else { - bb_model = model_watcher.model(); - if (bb_model && bb_model.attributes) { - attribute_keys = _.keys(bb_model.attributes); - keys = keys ? _.union(keys, attribute_keys) : attribute_keys; - } - } - if (keys && this.__kb.excludes) { - keys = _.difference(keys, this.__kb.excludes); - } - if (_.isObject(options.keys) && !_.isArray(options.keys)) { - this._mapObservables(model, options.keys); - } - if (_.isObject(options.requires) && !_.isArray(options.requires)) { - this._mapObservables(model, options.requires); - } - !options.mappings || this._mapObservables(model, options.mappings); - !keys || this._createObservables(model, keys); - !kb.statistics || kb.statistics.register('ViewModel', this); - } - - ViewModel.prototype.destroy = function() { - var vm_key; - if (this.__kb.view_model !== this) { - for (vm_key in this.__kb.vm_keys) { - this.__kb.view_model[vm_key] = null; - } - } - this.__kb.view_model = null; - kb.releaseKeys(this); - kb.utils.wrappedDestroy(this); - return !kb.statistics || kb.statistics.unregister('ViewModel', this); - }; - - ViewModel.prototype.shareOptions = function() { - return { - store: kb.utils.wrappedStore(this), - factory: kb.utils.wrappedFactory(this) - }; - }; - - ViewModel.prototype.model = function(new_model) { - var missing, model, model_watcher; - model = kb.utils.wrappedObject(this); - if ((arguments.length === 0) || (model === new_model)) { - return model; - } - if (this.__kb_null) { - !new_model || throwUnexpected(this, 'model set on shared null'); - return; - } - kb.utils.wrappedObject(this, new_model); - model_watcher = kb.utils.wrappedModelWatcher(this); - if (!model_watcher) { - return; - } - model_watcher.model(new_model); - if (this.__kb.keys || !new_model || !new_model.attributes) { - return; - } - missing = _.difference(_.keys(new_model.attributes), _.keys(this.__kb.model_keys)); - if (missing) { - return this._createObservables(new_model, missing); - } - }; - - ViewModel.prototype._createObservables = function(model, keys) { - var create_options, key, vm_key, _i, _len; - create_options = { - store: kb.utils.wrappedStore(this), - factory: kb.utils.wrappedFactory(this), - path: this.__kb.path, - model_watcher: kb.utils.wrappedModelWatcher(this) - }; - for (_i = 0, _len = keys.length; _i < _len; _i++) { - key = keys[_i]; - vm_key = this.__kb.internals && _.contains(this.__kb.internals, key) ? "_" + key : key; - if (this[vm_key]) { - continue; - } - this.__kb.vm_keys[vm_key] = true; - this.__kb.model_keys[key] = true; - create_options.key = key; - this[vm_key] = this.__kb.view_model[vm_key] = kb.observable(model, create_options, this); - } - return this; - }; - - ViewModel.prototype._mapObservables = function(model, mappings) { - var create_options, mapping_info, vm_key; - create_options = { - store: kb.utils.wrappedStore(this), - factory: kb.utils.wrappedFactory(this), - path: this.__kb.path, - model_watcher: kb.utils.wrappedModelWatcher(this) - }; - for (vm_key in mappings) { - mapping_info = mappings[vm_key]; - if (this[vm_key]) { - continue; - } - mapping_info = _.isString(mapping_info) ? { - key: mapping_info - } : _.clone(mapping_info); - mapping_info.key || (mapping_info.key = vm_key); - this.__kb.vm_keys[vm_key] = true; - this.__kb.model_keys[mapping_info.key] = true; - this[vm_key] = this.__kb.view_model[vm_key] = kb.observable(model, _.defaults(mapping_info, create_options), this); - } - return this; - }; - - return ViewModel; - -})(); - -kb.viewModel = function(model, options, view_model) { - return new kb.ViewModel(model, options, view_model); -}; - -kb.observables = function(model, binding_info, view_model) { - legacyWarning('kb.observables', '0.16.4', 'Please use kb.viewModel instead'); - return new kb.ViewModel(model, binding_info, view_model); -}; - -/* - knockback-collection-observable.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.CollectionObservable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.CollectionObservable = (function() { - - CollectionObservable.extend = Backbone.Model.extend; - - function CollectionObservable(collection, options) { - var create_options, observable, - _this = this; - !collection || (collection instanceof Backbone.Collection) || throwUnexpected(this, 'not a collection'); - options || (options = {}); - observable = kb.utils.wrappedObservable(this, ko.observableArray([])); - observable.__kb_is_co = true; - this.in_edit = 0; - this.__kb || (this.__kb = {}); - this.__kb._onCollectionChange = _.bind(this._onCollectionChange, this); - options = collapseOptions(options); - if (options.sort_attribute) { - this.sorted_index_fn = ko.observable(this._sortAttributeFn(options.sort_attribute)); - } else { - if (options.sorted_index) { - legacyWarning(this, '0.16.4', 'use sorted_index_fn instead'); - options.sorted_index_fn = options.sorted_index; - } - this.sorted_index_fn = ko.observable(options.sorted_index_fn); - } - if (options.filters) { - this.filters = ko.observableArray(_.isArray(options.filters) ? options.filters : options.filters ? [options.filters] : void 0); - } else { - this.filters = ko.observableArray([]); - } - create_options = this.create_options = { - store: kb.Store.useOptionsOrCreate(options, collection, observable) - }; - this.path = options.path; - create_options.factory = kb.utils.wrappedFactory(observable, this._shareOrCreateFactory(options)); - create_options.path = kb.utils.pathJoin(options.path, 'models'); - create_options.creator = create_options.factory.creatorForPath(null, create_options.path); - if (create_options.creator) { - this.models_only = create_options.creator.models_only; - } - observable.destroy = _.bind(this.destroy, this); - observable.shareOptions = _.bind(this.shareOptions, this); - observable.collection = _.bind(this.collection, this); - observable.viewModelByModel = _.bind(this.viewModelByModel, this); - observable.sortedIndex = _.bind(this.sortedIndex, this); - observable.sortAttribute = _.bind(this.sortAttribute, this); - observable.hasViewModels = _.bind(this.hasViewModels, this); - this._col = ko.observable(); - this.collection(collection); - this._mapper = ko.dependentObservable(function() { - var filters, model, models, sorted_index_fn, view_model, view_models, _i, _len; - if (_this.in_edit) { - return; - } - observable = kb.utils.wrappedObservable(_this); - collection = _this._col(); - if (collection) { - models = collection.models; - } - sorted_index_fn = _this.sorted_index_fn(); - filters = _this.filters(); - if (!models || (collection.models.length === 0)) { - view_models = []; - } else { - if (filters.length) { - models = _.filter(models, function(model) { - return !_this._modelIsFiltered(model); - }); - } - if (sorted_index_fn) { - view_models = []; - for (_i = 0, _len = models.length; _i < _len; _i++) { - model = models[_i]; - view_model = _this._createViewModel(model); - view_models.splice(sorted_index_fn(view_models, view_model), 0, view_model); - } - } else { - if (_this.models_only) { - view_models = filters.length ? models : models.slice(); - } else { - view_models = _.map(models, function(model) { - return _this._createViewModel(model); - }); - } - } - } - _this.in_edit++; - observable(view_models); - return _this.in_edit--; - }); - observable.subscribe(_.bind(this._onObservableArrayChange, this)); - !kb.statistics || kb.statistics.register('CollectionObservable', this); - return observable; - } - - CollectionObservable.prototype.destroy = function() { - var array, collection, observable; - observable = kb.utils.wrappedObservable(this); - collection = this._col(); - if (collection) { - collection.unbind('all', this.__kb._onCollectionChange); - array = observable(); - array.splice(0, array.length); - } - kb.release(this.filters); - this.filters = this._col = this.sorted_index_fn = this._mapper = this.create_options = null; - kb.utils.wrappedDestroy(this); - return !kb.statistics || kb.statistics.unregister('CollectionObservable', this); - }; - - CollectionObservable.prototype.shareOptions = function() { - var observable; - observable = kb.utils.wrappedObservable(this); - return { - store: kb.utils.wrappedStore(observable), - factory: kb.utils.wrappedFactory(observable) - }; - }; - - CollectionObservable.prototype.collection = function(collection) { - var observable, previous_collection; - observable = kb.utils.wrappedObservable(this); - previous_collection = this._col(); - if ((arguments.length === 0) || (collection === previous_collection)) { - observable(); - return previous_collection; - } - if (previous_collection) { - previous_collection.unbind('all', this.__kb._onCollectionChange); - } - if (collection) { - collection.bind('all', this.__kb._onCollectionChange); - } - this._col(collection); - return collection; - }; - - CollectionObservable.prototype.filters = function(filters) { - if (filters) { - return this.filters(_.isArray(filters) ? filters : [filters]); - } else { - return this.filters([]); - } - }; - - CollectionObservable.prototype.sortedIndex = function(sorted_index_fn) { - return this.sorted_index_fn(sorted_index_fn); - }; - - CollectionObservable.prototype.sortAttribute = function(sort_attribute) { - return this.sorted_index_fn(sort_attribute ? this._sortAttributeFn(sort_attribute) : null); - }; - - CollectionObservable.prototype.viewModelByModel = function(model) { - var id_attribute; - if (this.models_only) { - return null; - } - id_attribute = model.hasOwnProperty(model.idAttribute) ? model.idAttribute : 'cid'; - return _.find(kb.utils.wrappedObservable(this)(), function(test) { - return test.__kb.object[id_attribute] === model[id_attribute]; - }); - }; - - CollectionObservable.prototype.hasViewModels = function() { - return !this.models_only; - }; - - CollectionObservable.prototype._shareOrCreateFactory = function(options) { - var absolute_models_path, existing_creator, factories, factory; - absolute_models_path = kb.utils.pathJoin(options.path, 'models'); - factories = options.factories; - if ((factory = options.factory)) { - if ((existing_creator = factory.creatorForPath(null, absolute_models_path)) && (!factories || (factories['models'] === existing_creator))) { - if (!factories) { - return factory; - } - if (factory.hasPathMappings(factories, options.path)) { - return factory; - } - } - } - factory = new kb.Factory(options.factory); - if (factories) { - factory.addPathMappings(factories, options.path); - } - if (!factory.creatorForPath(null, absolute_models_path)) { - if (options.hasOwnProperty('models_only')) { - if (options.models_only) { - factory.addPathMapping(absolute_models_path, { - models_only: true - }); - } else { - factory.addPathMapping(absolute_models_path, kb.ViewModel); - } - } else if (options.view_model) { - factory.addPathMapping(absolute_models_path, options.view_model); - } else if (options.create) { - factory.addPathMapping(absolute_models_path, { - create: options.create - }); - } else { - factory.addPathMapping(absolute_models_path, kb.ViewModel); - } - } - return factory; - }; - - CollectionObservable.prototype._onCollectionChange = function(event, arg) { - var add_index, collection, observable, sorted_index_fn, view_model; - if (this.in_edit) { - return; - } - switch (event) { - case 'reset': - case 'resort': - if (event === 'resort' && !this.sorted_index_fn()) { - return; - } - return this._col.notifySubscribers(this._col()); - case 'new': - case 'add': - if (this._modelIsFiltered(arg)) { - return; - } - observable = kb.utils.wrappedObservable(this); - collection = this._col(); - view_model = this._createViewModel(arg); - if ((sorted_index_fn = this.sorted_index_fn())) { - add_index = sorted_index_fn(observable(), view_model); - } else { - add_index = collection.indexOf(arg); - } - this.in_edit++; - observable.splice(add_index, 0, view_model); - return this.in_edit--; - case 'remove': - case 'destroy': - return this._onModelRemove(arg); - case 'change': - if (this._modelIsFiltered(arg)) { - return this._onModelRemove(arg); - } else if (this.sorted_index_fn()) { - return this._onModelResort(arg); - } - } - }; - - CollectionObservable.prototype._onModelRemove = function(model) { - var observable, view_model; - view_model = this.models_only ? model : this.viewModelByModel(model); - if (!view_model) { - return; - } - observable = kb.utils.wrappedObservable(this); - this.in_edit++; - observable.remove(view_model); - return this.in_edit--; - }; - - CollectionObservable.prototype._onModelResort = function(model) { - var new_index, observable, previous_index, sorted_index_fn, sorted_view_models, view_model; - observable = kb.utils.wrappedObservable(this); - view_model = this.models_only ? model : this.viewModelByModel(model); - previous_index = observable.indexOf(view_model); - if ((sorted_index_fn = this.sorted_index_fn())) { - sorted_view_models = _.clone(observable()); - sorted_view_models.splice(previous_index, 1); - new_index = sorted_index_fn(sorted_view_models, view_model); - } else { - new_index = this._col().indexOf(model); - } - if (previous_index === new_index) { - return; - } - this.in_edit++; - observable.splice(previous_index, 1); - observable.splice(new_index, 0, view_model); - return this.in_edit--; - }; - - CollectionObservable.prototype._onObservableArrayChange = function(values) { - var collection, has_view_model, models, observable, value, _i, _j, _len, _len1, - _this = this; - if (this.in_edit) { - return; - } - observable = kb.utils.wrappedObservable(this); - collection = this._col(); - if (!collection) { - return; - } - if (!this.models_only) { - for (_i = 0, _len = values.length; _i < _len; _i++) { - value = values[_i]; - if (value && !(value instanceof Backbone.Model)) { - has_view_model = true; - break; - } - } - if (has_view_model) { - for (_j = 0, _len1 = values.length; _j < _len1; _j++) { - value = values[_j]; - this.create_options.store.findOrReplace(kb.utils.wrappedObject(value), this.create_options.creator, value); - } - } - } - models = _.map(values, function(test) { - return kb.utils.wrappedModel(test); - }); - if (this.filters().length) { - models = _.filter(models, function(model) { - return !_this._modelIsFiltered(model); - }); - } - this.in_edit++; - collection.reset(models); - this.in_edit--; - return this; - }; - - CollectionObservable.prototype._sortAttributeFn = function(sort_attribute) { - if (this.models_only) { - return function(models, model) { - var attribute_name; - attribute_name = ko.utils.unwrapObservable(sort_attribute); - return _.sortedIndex(models, model, function(test) { - return test.get(attribute_name); - }); - }; - } else { - return function(view_models, model) { - var attribute_name; - attribute_name = ko.utils.unwrapObservable(sort_attribute); - return _.sortedIndex(view_models, model, function(test) { - return kb.utils.wrappedModel(test).get(attribute_name); - }); - }; - } - }; - - CollectionObservable.prototype._createViewModel = function(model) { - if (this.models_only) { - return model; - } else { - return this.create_options.store.findOrCreate(model, this.create_options); - } - }; - - CollectionObservable.prototype._modelIsFiltered = function(model) { - var filter, filters, _i, _len; - filters = this.filters(); - for (_i = 0, _len = filters.length; _i < _len; _i++) { - filter = filters[_i]; - filter = ko.utils.unwrapObservable(filter); - if (((typeof filter === 'function') && filter(model)) || (model && (model.id === filter))) { - return true; - } - } - return false; - }; - - return CollectionObservable; - -})(); - -kb.collectionObservable = function(collection, options) { - return new kb.CollectionObservable(collection, options); -}; - -kb.sortedIndexWrapAttr = kb.siwa = function(attribute_name, wrapper_constructor) { - return function(models, model) { - return _.sortedIndex(models, model, function(test) { - return new wrapper_constructor(kb.utils.wrappedModel(test).get(attribute_name)); - }); - }; -}; - -/* - knockback-inject.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Inject is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -ko.bindingHandlers['inject'] = { - 'init': function(element, value_accessor, all_bindings_accessor, view_model) { - var data, wrapper; - data = ko.utils.unwrapObservable(value_accessor()); - wrapper = ko.dependentObservable(function() { - var key, value, _results; - if (_.isFunction(data)) { - return data(view_model, element, value_accessor, all_bindings_accessor); - } else if (_.isObject(data)) { - _results = []; - for (key in data) { - value = data[key]; - if (_.isObject(value) && value.resolve && _.isFunction(value.resolve)) { - _results.push(view_model[key] = value.resolve(view_model, element, value_accessor, all_bindings_accessor)); - } else { - _results.push(view_model[key] = value); - } - } - return _results; - } - }); - return wrapper.dispose(); - } -}; - -kb.injectApps = function(root) { - var app, apps, getAppElements, options, _i, _len; - apps = []; - getAppElements = function(el) { - var attr, child_el, _i, _len, _ref; - if (!el.__kb_injected) { - if (el.attributes && (attr = _.find(el.attributes, function(attr) { - return attr.name === 'kb-app'; - }))) { - el.__kb_injected = true; - apps.push([el, attr]); - } - } - _ref = el.childNodes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child_el = _ref[_i]; - getAppElements(child_el); - } - }; - getAppElements(root || document); - for (_i = 0, _len = apps.length; _i < _len; _i++) { - app = apps[_i]; - if (app[1].value) { - options = ko.utils.buildEvalWithinScopeFunction("{" + app[1].value + "}", 0)(); - } - options || (options = {}); - options.view_model || (options.view_model = {}); - if (options.beforeBinding) { - options.beforeBinding(options.view_model, app[0], options); - } - kb.applyBindings(options.view_model, app[0], {}); - if (options.afterBinding) { - options.afterBinding(options.view_model, app[0], options); - } - } -}; - -(onReady = function() { - if (!document.body) { - return setTimeout(onReady, 1); - } - kb.injectApps(); -})(); -; return kb;}); -}).call(this); \ No newline at end of file diff --git a/examples/vendor/knockback-core-stack-0.16.5.js b/examples/vendor/knockback-core-stack-0.16.5.js index 4e857c6..fa31768 100644 --- a/examples/vendor/knockback-core-stack-0.16.5.js +++ b/examples/vendor/knockback-core-stack-0.16.5.js @@ -6061,7 +6061,7 @@ kb = (function() { if (options == null) { options = {}; } - legacyWarning('kb.renderAutoReleasedTemplate', '0.16.5', 'Please use kb.renderTemplate instead'); + legacyWarning('kb.renderAutoReleasedTemplate', '0.16.3', 'Please use kb.renderTemplate instead'); return this.renderTemplate(template, view_model, options = {}); }; @@ -6338,7 +6338,7 @@ kb.utils = (function() { }; utils.release = function(obj) { - legacyWarning('kb.utils.release', '0.16.5', 'Please use kb.release instead'); + legacyWarning('kb.utils.release', '0.16.0', 'Please use kb.release instead'); return kb.release(obj); }; @@ -7262,7 +7262,7 @@ kb.viewModel = function(model, options, view_model) { }; kb.observables = function(model, binding_info, view_model) { - legacyWarning('kb.observables', '0.16.5', 'Please use kb.viewModel instead'); + legacyWarning('kb.observables', '0.16.0', 'Please use kb.viewModel instead'); return new kb.ViewModel(model, binding_info, view_model); }; @@ -7294,7 +7294,7 @@ kb.CollectionObservable = (function() { this.sorted_index_fn = ko.observable(this._sortAttributeFn(options.sort_attribute)); } else { if (options.sorted_index) { - legacyWarning(this, '0.16.5', 'use sorted_index_fn instead'); + legacyWarning(this, '0.16.3', 'use sorted_index_fn instead'); options.sorted_index_fn = options.sorted_index; } this.sorted_index_fn = ko.observable(options.sorted_index_fn); diff --git a/index.html b/index.html index 6e3b477..ff820ca 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ + KnockbackNavigators.js Interactive Tests @@ -120,7 +121,7 @@

Knockback.js

- + "); - }; - - if (jQueryTmplVersion > 0) { - jQuery['tmpl']['tag']['ko_code'] = { - open: "__.push($1 || '');" - }; - jQuery['tmpl']['tag']['ko_with'] = { - open: "with($1) {", - close: "} " - }; - } - }; - - ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine(); - - // Use this one by default *only if jquery.tmpl is referenced* - var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine(); - if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0) - ko.setTemplateEngine(jqueryTmplTemplateEngineInstance); - - ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine); -})(); -}); -})(window,document,navigator); -/* - knockback.js 0.16.4 - (c) 2011, 2012 Kevin Malakoff. - Knockback.js is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE - Dependencies: Knockout.js, Backbone.js, and Underscore.js. -*/ - -(function() { - return (function(factory) { - // AMD - if (typeof define === 'function' && define.amd) { - return define('knockback', ['underscore', 'backbone', 'knockout'], factory); - } - // CommonJS/NodeJS or No Loader - else { - return factory.call(this); - } - })(function() {// Generated by CoffeeScript 1.3.3 -/* - knockback-core.js 0.16.4 - (c) 2011, 2012 Kevin Malakoff. - Knockback.js is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE - Dependencies: Knockout.js, Backbone.js, and Underscore.js. -*/ - -var Backbone, KB_TYPE_ARRAY, KB_TYPE_COLLECTION, KB_TYPE_MODEL, KB_TYPE_SIMPLE, KB_TYPE_UNKNOWN, addStatisticsEvent, arraySplice, collapseOptions, kb, ko, legacyWarning, onReady, throwMissing, throwUnexpected, _, _argumentsAddKey, _unwrapModels, _wrappedKey, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - -kb = (function() { - - function kb() {} - - kb.VERSION = '0.16.4'; - - kb.TYPE_UNKNOWN = 0; - - kb.TYPE_SIMPLE = 1; - - kb.TYPE_ARRAY = 2; - - kb.TYPE_MODEL = 3; - - kb.TYPE_COLLECTION = 4; - - kb.release = function(obj, pre_release_fn) { - var array, item, view_model, view_models, _i, _j, _len, _len1; - if ((!obj || (obj !== Object(obj))) || ((typeof obj === 'function') && !ko.isObservable(obj)) || obj.__kb_destroyed || ((obj instanceof Backbone.Model) || (obj instanceof Backbone.Collection))) { - return this; - } - if (_.isArray(obj)) { - array = obj.splice(0, obj.length); - for (_i = 0, _len = array.length; _i < _len; _i++) { - item = array[_i]; - kb.release(item); - } - return this; - } - obj.__kb_destroyed = true; - !pre_release_fn || pre_release_fn(); - if (ko.isObservable(obj) || (typeof obj.dispose === 'function') || (typeof obj.destroy === 'function') || (typeof obj.release === 'function')) { - if (ko.isObservable(obj) && _.isArray(array = obj())) { - if (obj.__kb_is_co || (obj.__kb_is_o && (obj.valueType() === KB_TYPE_COLLECTION))) { - if (obj.destroy) { - obj.destroy(); - } else if (obj.dispose) { - obj.dispose(); - } - } else if (array.length) { - view_models = array.slice(0); - array.splice(0, array.length); - for (_j = 0, _len1 = view_models.length; _j < _len1; _j++) { - view_model = view_models[_j]; - kb.release(view_model); - } - } - } else if (obj.release) { - obj.release(); - } else if (obj.destroy) { - obj.destroy(); - } else if (obj.dispose) { - obj.dispose(); - } - } else { - this.releaseKeys(obj); - } - return this; - }; - - kb.releaseKeys = function(obj) { - var key, value; - for (key in obj) { - value = obj[key]; - (key === '__kb') || kb.release(value, (function() { - return obj[key] = null; - })); - } - return this; - }; - - kb.releaseOnNodeRemove = function(view_model, node) { - view_model || throwUnexpected(this, 'missing view model'); - node || throwUnexpected(this, 'missing node'); - return ko.utils.domNodeDisposal.addDisposeCallback(node, function() { - return kb.release(view_model); - }); - }; - - kb.renderTemplate = function(template, view_model, options) { - var el, observable; - if (options == null) { - options = {}; - } - el = document.createElement('div'); - observable = ko.renderTemplate(template, view_model, options, el, 'replaceChildren'); - if (el.children.length === 1) { - el = el.children[0]; - } - kb.releaseOnNodeRemove(view_model, el); - observable.dispose(); - return el; - }; - - kb.renderTemplate = function(template, view_model, options) { - if (options == null) { - options = {}; - } - legacyWarning('kb.renderTemplate', '0.16.4', 'Please use kb.renderTemplate instead'); - return this.renderTemplate(template, view_model, options = {}); - }; - - kb.applyBindings = function(view_model, node) { - ko.applyBindings(view_model, node); - return kb.releaseOnNodeRemove(view_model, node); - }; - - return kb; - -})(); - -this.Knockback = this.kb = kb; - -if (typeof exports !== 'undefined') { - module.exports = kb; -} - -if (!this._ && (typeof require !== 'undefined')) { - try { - _ = require('lodash'); - } catch (e) { - _ = require('underscore'); - } -} else { - _ = this._; -} - -kb._ = _ = _.hasOwnProperty('_') ? _._ : _; - -kb.Backbone = Backbone = !this.Backbone && (typeof require !== 'undefined') ? require('backbone') : this.Backbone; - -kb.ko = ko = !this.ko && (typeof require !== 'undefined') ? require('knockout') : this.ko; - -throwMissing = function(instance, message) { - throw "" + instance.constructor.name + ": " + message + " is missing"; -}; - -throwUnexpected = function(instance, message) { - throw "" + instance.constructor.name + ": " + message + " is unexpected"; -}; - -legacyWarning = function(identifier, last_version, message) { - var _base; - this._legacy_warnings || (this._legacy_warnings = {}); - (_base = this._legacy_warnings)[identifier] || (_base[identifier] = 0); - this._legacy_warnings[identifier]++; - return console.warn("warning: '" + identifier + "' has been deprecated (will be removed in Knockback after " + last_version + "). " + message + "."); -}; - -arraySplice = Array.prototype.splice; - -collapseOptions = function(options) { - var result; - result = _.clone(options); - while (options.options) { - _.defaults(result, options.options); - options = options.options; - } - delete result.options; - return result; -}; - -KB_TYPE_UNKNOWN = kb.TYPE_UNKNOWN; - -KB_TYPE_SIMPLE = kb.TYPE_SIMPLE; - -KB_TYPE_ARRAY = kb.TYPE_ARRAY; - -KB_TYPE_MODEL = kb.TYPE_MODEL; - -KB_TYPE_COLLECTION = kb.TYPE_COLLECTION; - -/* - knockback-utils.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.js is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE - Dependencies: Knockout.js, Backbone.js, and Underscore.js. - Optional dependency: Backbone.ModelRef.js. -*/ - - -_wrappedKey = function(obj, key, value) { - if (arguments.length === 2) { - if (obj && obj.__kb && obj.__kb.hasOwnProperty(key)) { - return obj.__kb[key]; - } else { - return void 0; - } - } - obj || throwUnexpected(this, "no obj for wrapping " + key); - obj.__kb || (obj.__kb = {}); - obj.__kb[key] = value; - return value; -}; - -_argumentsAddKey = function(args, key) { - arraySplice.call(args, 1, 0, key); - return args; -}; - -_unwrapModels = function(obj) { - var key, result, value; - if (!obj) { - return obj; - } else if (obj.__kb) { - if ('object' in obj.__kb) { - return obj.__kb.object; - } else { - return obj; - } - } else if (_.isArray(obj)) { - return _.map(obj, function(test) { - return _unwrapModels(test); - }); - } else if (_.isObject(obj) && !ko.isObservable(obj) && !_.isDate(obj) && !_.isString(obj)) { - result = {}; - for (key in obj) { - value = obj[key]; - result[key] = _unwrapModels(value); - } - return result; - } else { - return obj; - } -}; - -kb.utils = (function() { - - function utils() {} - - utils.wrappedObservable = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'observable')); - }; - - utils.wrappedObject = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'object')); - }; - - utils.wrappedModel = function(obj, value) { - if (arguments.length === 1) { - value = _wrappedKey(obj, 'object'); - if (_.isUndefined(value)) { - return obj; - } else { - return value; - } - } else { - return _wrappedKey(obj, 'object', value); - } - }; - - utils.wrappedStore = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'store')); - }; - - utils.wrappedStoreIsOwned = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'store_is_owned')); - }; - - utils.wrappedFactory = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'factory')); - }; - - utils.wrappedModelWatcher = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'model_watcher')); - }; - - utils.wrappedModelWatcherIsOwned = function(obj, value) { - return _wrappedKey.apply(this, _argumentsAddKey(arguments, 'model_watcher_is_owned')); - }; - - utils.wrappedDestroy = function(obj) { - var __kb; - if (!obj.__kb) { - return; - } - if (obj.__kb.model_watcher) { - obj.__kb.model_watcher.releaseCallbacks(obj); - } - __kb = obj.__kb; - obj.__kb = null; - if (__kb.observable) { - __kb.observable.destroy = __kb.observable.release = null; - this.wrappedDestroy(__kb.observable); - __kb.observable = null; - } - __kb.factory = null; - if (__kb.model_watcher_is_owned) { - __kb.model_watcher.destroy(); - } - __kb.model_watcher = null; - if (__kb.store_is_owned) { - __kb.store.destroy(); - } - return __kb.store = null; - }; - - utils.valueType = function(observable) { - if (!observable) { - return KB_TYPE_UNKNOWN; - } - if (observable.__kb_is_o) { - return observable.valueType(); - } - if (observable.__kb_is_co || (observable instanceof Backbone.Collection)) { - return KB_TYPE_COLLECTION; - } - if ((observable instanceof kb.ViewModel) || (observable instanceof Backbone.Model)) { - return KB_TYPE_MODEL; - } - if (_.isArray(observable)) { - return KB_TYPE_ARRAY; - } - return KB_TYPE_SIMPLE; - }; - - utils.pathJoin = function(path1, path2) { - return (path1 ? (path1[path1.length - 1] !== '.' ? "" + path1 + "." : path1) : '') + path2; - }; - - utils.optionsPathJoin = function(options, path) { - return _.defaults({ - path: this.pathJoin(options.path, path) - }, options); - }; - - utils.inferCreator = function(value, factory, path, owner, key) { - var creator, relation; - if (factory) { - creator = factory.creatorForPath(value, path); - } - if (creator) { - return creator; - } - if (owner && Backbone.RelationalModel && (owner instanceof Backbone.RelationalModel)) { - key = ko.utils.unwrapObservable(key); - relation = _.find(owner.getRelations(), function(test) { - return test.key === key; - }); - if (relation) { - if (relation.collectionType || _.isArray(relation.keyContents)) { - return kb.CollectionObservable; - } else { - return kb.ViewModel; - } - } - } - if (!value) { - return null; - } - if (value instanceof Backbone.Model) { - return kb.ViewModel; - } - if (value instanceof Backbone.Collection) { - return kb.CollectionObservable; - } - return null; - }; - - utils.createFromDefaultCreator = function(obj, options) { - if (obj instanceof Backbone.Model) { - return kb.viewModel(obj, options); - } - if (obj instanceof Backbone.Collection) { - return kb.collectionObservable(obj, options); - } - if (_.isArray(obj)) { - return ko.observableArray(obj); - } - return ko.observable(obj); - }; - - utils.release = function(obj) { - legacyWarning('kb.utils.release', '0.16.4', 'Please use kb.release instead'); - return kb.release(obj); - }; - - return utils; - -})(); - -/* - knockback_factory.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Factory is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.Factory = (function() { - - Factory.useOptionsOrCreate = function(options, obj, owner_path) { - var factory; - if (options.factory && (!options.factories || (options.factories && options.factory.hasPathMappings(options.factories, owner_path)))) { - return kb.utils.wrappedFactory(obj, options.factory); - } - factory = kb.utils.wrappedFactory(obj, new kb.Factory(options.factory)); - if (options.factories) { - factory.addPathMappings(options.factories, owner_path); - } - return factory; - }; - - function Factory(parent_factory) { - this.parent_factory = parent_factory; - this.paths = {}; - } - - Factory.prototype.hasPath = function(path) { - return this.paths.hasOwnProperty(path) || (this.parent_factory && this.parent_factory.hasPath(path)); - }; - - Factory.prototype.addPathMapping = function(path, create_info) { - return this.paths[path] = create_info; - }; - - Factory.prototype.addPathMappings = function(factories, owner_path) { - var create_info, path; - for (path in factories) { - create_info = factories[path]; - this.paths[kb.utils.pathJoin(owner_path, path)] = create_info; - } - return this; - }; - - Factory.prototype.hasPathMappings = function(factories, owner_path) { - var all_exist, creator, existing_creator, path; - all_exist = true; - for (path in factories) { - creator = factories[path]; - all_exist &= (existing_creator = this.creatorForPath(null, kb.utils.pathJoin(owner_path, path))) && (creator === existing_creator); - } - return all_exist; - }; - - Factory.prototype.creatorForPath = function(obj, path) { - var creator; - if ((creator = this.paths[path])) { - if (creator.view_model) { - return creator.view_model; - } else { - return creator; - } - } - if (this.parent_factory) { - if ((creator = this.parent_factory.creatorForPath(obj, path))) { - return creator; - } - } - return null; - }; - - return Factory; - -})(); - -/* - knockback_store.js - (c) 2012 Kevin Malakoff. - Knockback.Store is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.Store = (function() { - - Store.useOptionsOrCreate = function(options, obj, observable) { - if (options.store) { - options.store.register(obj, observable, options); - return kb.utils.wrappedStore(observable, options.store); - } else { - kb.utils.wrappedStoreIsOwned(observable, true); - return kb.utils.wrappedStore(observable, new kb.Store()); - } - }; - - function Store() { - this.observable_records = []; - this.replaced_observables = []; - } - - Store.prototype.destroy = function() { - var observable, record, _i, _j, _len, _len1, _ref, _ref1; - _ref = this.observable_records; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - record = _ref[_i]; - kb.release(record.observable); - } - _ref1 = this.replaced_observables; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - observable = _ref1[_j]; - kb.release(observable); - } - this.observable_records = null; - return this.replaced_observables = null; - }; - - Store.prototype.register = function(obj, observable, options) { - var creator; - if (!observable) { - return; - } - if (ko.isObservable(observable) || observable.__kb_is_co) { - return; - } - kb.utils.wrappedObject(observable, obj); - if (!obj) { - observable.__kb_null = true; - } - creator = options.creator ? options.creator : (options.path && options.factory ? options.factory.creatorForPath(obj, options.path) : null); - if (!creator) { - creator = observable.constructor; - } - this.observable_records.push({ - obj: obj, - observable: observable, - creator: creator - }); - return observable; - }; - - Store.prototype.findIndex = function(obj, creator) { - var index, record, _ref; - if (!obj || (obj instanceof Backbone.Model)) { - _ref = this.observable_records; - for (index in _ref) { - record = _ref[index]; - if (!record.observable) { - continue; - } - if (record.observable.__kb_destroyed) { - record.obj = null; - record.observable = null; - continue; - } - if ((!obj && !record.observable.__kb_null) || (obj && (record.observable.__kb_null || (record.obj !== obj)))) { - continue; - } else if ((record.creator === creator) || (record.creator.create && (record.creator.create === creator.create))) { - return index; - } - } - } - return -1; - }; - - Store.prototype.find = function(obj, creator) { - var index; - if ((index = this.findIndex(obj, creator)) < 0) { - return null; - } else { - return this.observable_records[index].observable; - } - }; - - Store.prototype.isRegistered = function(observable) { - var record, _i, _len, _ref; - _ref = this.observable_records; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - record = _ref[_i]; - if (record.observable === observable) { - return true; - } - } - return false; - }; - - Store.prototype.findOrCreate = function(obj, options) { - var creator, observable; - options.store = this; - options.creator || (options.creator = kb.utils.inferCreator(obj, options.factory, options.path)); - if (!options.creator && (obj instanceof Backbone.Model)) { - options.creator = kv.ViewModel; - } - creator = options.creator; - if (!creator) { - return kb.utils.createFromDefaultCreator(obj, options); - } else if (creator.models_only) { - return obj; - } - if (creator) { - observable = this.find(obj, creator); - } - if (observable) { - return observable; - } - if (creator.create) { - observable = creator.create(obj, options); - } else { - observable = new creator(obj, options); - } - observable || (observable = ko.observable(null)); - if (!ko.isObservable(observable)) { - this.isRegistered(observable) || this.register(obj, observable, options); - } - return observable; - }; - - Store.prototype.findOrReplace = function(obj, creator, observable) { - var index, record; - obj || raiseUnexpected('obj missing'); - if ((index = this.findIndex(obj, creator)) < 0) { - return this.register(obj, observable, { - creator: creator - }); - } else { - record = this.observable_records[index]; - (kb.utils.wrappedObject(record.observable) === obj) || throwUnexpected(this, 'different object'); - if (record.observable !== observable) { - (record.observable.constructor === observable.constructor) || throwUnexpected(this, 'replacing different type'); - this.replaced_observables.push(record.observable); - record.observable = observable; - } - return observable; - } - }; - - return Store; - -})(); - -/* - knockback_model_watcher.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Observable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -addStatisticsEvent = function(model, event_name, info) { - return !kb.statistics || kb.statistics.addModelEvent({ - name: event_name, - model: model, - key: info.key, - path: info.path - }); -}; - -kb.ModelWatcher = (function() { - - ModelWatcher.useOptionsOrCreate = function(options, model, obj, callback_options) { - if (options.model_watcher) { - if (!(options.model_watcher.model() === model || (options.model_watcher.model_ref === model))) { - throwUnexpected(this, 'model not matching'); - } - return kb.utils.wrappedModelWatcher(obj, options.model_watcher).registerCallbacks(obj, callback_options); - } else { - kb.utils.wrappedModelWatcherIsOwned(obj, true); - return kb.utils.wrappedModelWatcher(obj, new kb.ModelWatcher(model)).registerCallbacks(obj, callback_options); - } - }; - - function ModelWatcher(model, obj, callback_options) { - this._onModelUnloaded = __bind(this._onModelUnloaded, this); - - this._onModelLoaded = __bind(this._onModelLoaded, this); - this.__kb || (this.__kb = {}); - this.__kb.callbacks = {}; - this.__kb._onModelLoaded = _.bind(this._onModelLoaded, this); - this.__kb._onModelUnloaded = _.bind(this._onModelUnloaded, this); - if (callback_options) { - this.registerCallbacks(obj, callback_options); - } - if (model) { - this.model(model); - } else { - this.m = null; - } - } - - ModelWatcher.prototype.destroy = function() { - this.model(null); - this.__kb.callbacks = null; - return kb.utils.wrappedDestroy(this); - }; - - ModelWatcher.prototype.model = function(new_model) { - var callbacks, event_name, info, list, previous_model, _i, _len, _ref; - if ((arguments.length === 0) || (this.m === new_model)) { - return this.m; - } - if (this.model_ref) { - this.model_ref.unbind('loaded', this.__kb._onModelLoaded); - this.model_ref.unbind('unloaded', this.__kb._onModelUnloaded); - this.model_ref.release(); - this.model_ref = null; - } - if (Backbone.ModelRef && (new_model instanceof Backbone.ModelRef)) { - this.model_ref = new_model; - this.model_ref.retain(); - this.model_ref.bind('loaded', this.__kb._onModelLoaded); - this.model_ref.bind('unloaded', this.__kb._onModelUnloaded); - new_model = this.model_ref.model(); - } else { - delete this.model_ref; - } - previous_model = this.m; - this.m = new_model; - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - if (previous_model) { - previous_model.unbind(event_name, callbacks.fn); - } - if (new_model) { - new_model.bind(event_name, callbacks.fn); - } - list = callbacks.list; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (info.model) { - info.model(new_model); - } - } - } - return new_model; - }; - - ModelWatcher.prototype.registerCallbacks = function(obj, callback_info) { - var callbacks, event_name, info, list; - obj || throwMissing(this, 'obj'); - callback_info || throwMissing(this, 'info'); - event_name = callback_info.event_name ? callback_info.event_name : 'change'; - callbacks = this.__kb.callbacks[event_name]; - if (!callbacks) { - list = []; - callbacks = { - list: list, - fn: function(model) { - var info, _i, _len; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (info.update && !info.rel_fn) { - if (model && info.key && (model.hasChanged && !model.hasChanged(ko.utils.unwrapObservable(info.key)))) { - continue; - } - !kb.statistics || addStatisticsEvent(model, event_name, info); - info.update(); - } - } - return null; - } - }; - this.__kb.callbacks[event_name] = callbacks; - if (this.m) { - this.m.bind(event_name, callbacks.fn); - } - } - info = _.defaults({ - obj: obj - }, callback_info); - callbacks.list.push(info); - if (this.m) { - if (Backbone.RelationalModel && (this.m instanceof Backbone.RelationalModel)) { - this._modelBindRelatationalInfo(event_name, info); - } - info.model(this.m) && info.model; - } - return this; - }; - - ModelWatcher.prototype.releaseCallbacks = function(obj) { - var callbacks, event_name, index, info, _ref, _ref1; - if (!this.__kb.callbacks) { - return; - } - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - _ref1 = callbacks.list; - for (index in _ref1) { - info = _ref1[index]; - if (info.obj === obj) { - callbacks.list.splice(index, 1); - if (info.rel_fn) { - this._modelUnbindRelatationalInfo(event_name, info); - } - if (info.model) { - info.model(null); - } - return; - } - } - } - }; - - ModelWatcher.prototype._onModelLoaded = function(model) { - var callbacks, event_name, info, is_relational, list, _i, _len, _ref; - is_relational = Backbone.RelationalModel && (model instanceof Backbone.RelationalModel); - this.m = model; - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - model.bind(event_name, callbacks.fn); - list = callbacks.list; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (is_relational) { - this._modelBindRelatationalInfo(event_name, info); - } - if (info.model) { - info.model(model); - } - } - } - return this; - }; - - ModelWatcher.prototype._onModelUnloaded = function(model) { - var callbacks, event_name, info, list, _i, _len, _ref; - this.m = null; - _ref = this.__kb.callbacks; - for (event_name in _ref) { - callbacks = _ref[event_name]; - model.unbind(event_name, callbacks.fn); - list = callbacks.list; - for (_i = 0, _len = list.length; _i < _len; _i++) { - info = list[_i]; - if (info.rel_fn) { - this._modelUnbindRelatationalInfo(event_name, info); - } - if (info.model) { - info.model(null); - } - } - } - return this; - }; - - ModelWatcher.prototype._modelBindRelatationalInfo = function(event_name, info) { - var key, relation; - if ((event_name === 'change') && info.key && info.update) { - key = ko.utils.unwrapObservable(info.key); - relation = _.find(this.m.getRelations(), function(test) { - return test.key === key; - }); - if (!relation) { - return; - } - info.rel_fn = function(model) { - !kb.statistics || addStatisticsEvent(model, "" + event_name + " (relational)", info); - return info.update(); - }; - if (relation.collectionType || _.isArray(relation.keyContents)) { - info.is_collection = true; - this.m.bind("add:" + info.key, info.rel_fn); - this.m.bind("remove:" + info.key, info.rel_fn); - } else { - this.m.bind("update:" + info.key, info.rel_fn); - } - } - return this; - }; - - ModelWatcher.prototype._modelUnbindRelatationalInfo = function(event_name, info) { - if (!info.rel_fn) { - return; - } - if (info.is_collection) { - this.m.unbind("add:" + info.key, info.rel_fn); - this.m.unbind("remove:" + info.key, info.rel_fn); - } else { - this.m.unbind("update:" + info.key, info.rel_fn); - } - info.rel_fn = null; - return this; - }; - - return ModelWatcher; - -})(); - -kb.modelObservable = function(model, observable) { - return new kb.ModelWatcher(model, observable); -}; - -/* - knockback-observable.js - (c) 2012 Kevin Malakoff. - Knockback.Observable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.Observable = (function() { - - function Observable(model, options, vm) { - var create_options, model_watcher, observable, - _this = this; - this.vm = vm; - options || throwMissing(this, 'options'); - this.vm || (this.vm = {}); - if (_.isString(options) || ko.isObservable(options)) { - create_options = this.create_options = { - key: options - }; - } else { - create_options = this.create_options = collapseOptions(options); - } - this.key = create_options.key; - delete create_options.key; - this.key || throwMissing(this, 'key'); - !create_options.args || (this.args = create_options.args, delete create_options.args); - !create_options.read || (this.read = create_options.read, delete create_options.read); - !create_options.write || (this.write = create_options.write, delete create_options.write); - model_watcher = create_options.model_watcher; - delete create_options.model_watcher; - this.vo = ko.observable(null); - observable = kb.utils.wrappedObservable(this, ko.dependentObservable({ - read: function() { - var arg, args, new_value, _i, _len, _ref; - args = [ko.utils.unwrapObservable(_this.key)]; - if (_this.args) { - if (_.isArray(_this.args)) { - _ref = _this.args; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - arg = _ref[_i]; - args.push(ko.utils.unwrapObservable(arg)); - } - } else { - args.push(ko.utils.unwrapObservable(_this.args)); - } - } - if (_this.m) { - new_value = _this.read ? _this.read.apply(_this.vm, args) : _this.m.get.apply(_this.m, args); - _this.update(new_value); - } - return ko.utils.unwrapObservable(_this.vo()); - }, - write: function(new_value) { - var arg, args, set_info, unwrapped_new_value, _i, _len, _ref; - unwrapped_new_value = _unwrapModels(new_value); - set_info = {}; - set_info[ko.utils.unwrapObservable(_this.key)] = unwrapped_new_value; - args = _this.write ? [unwrapped_new_value] : [set_info]; - if (_this.args) { - if (_.isArray(_this.args)) { - _ref = _this.args; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - arg = _ref[_i]; - args.push(ko.utils.unwrapObservable(arg)); - } - } else { - args.push(ko.utils.unwrapObservable(_this.args)); - } - } - if (_this.m) { - if (_this.write) { - _this.write.apply(_this.vm, args); - } else { - _this.m.set.apply(_this.m, args); - } - } - return _this.update(new_value); - }, - owner: this.vm - })); - observable.__kb_is_o = true; - create_options.store = kb.utils.wrappedStore(observable, create_options.store); - create_options.path = kb.utils.pathJoin(create_options.path, this.key); - if (create_options.factories && ((typeof create_options.factories === 'function') || create_options.factories.create)) { - create_options.factory = kb.utils.wrappedFactory(observable, new kb.Factory(create_options.factory)); - create_options.factory.addPathMapping(create_options.path, create_options.factories); - } else { - create_options.factory = kb.Factory.useOptionsOrCreate(create_options, observable, create_options.path); - } - delete create_options.factories; - observable.value = _.bind(this.value, this); - observable.valueType = _.bind(this.valueType, this); - observable.destroy = _.bind(this.destroy, this); - kb.ModelWatcher.useOptionsOrCreate({ - model_watcher: model_watcher - }, model, this, { - model: _.bind(this.model, this), - update: _.bind(this.update, this), - key: this.key, - path: create_options.path - }); - this.__kb_value || this.update(); - if (kb.LocalizedObservable && create_options.localizer) { - observable = new create_options.localizer(observable); - delete create_options.localizer; - } - if (kb.DefaultObservable && create_options.hasOwnProperty('default')) { - observable = kb.defaultObservable(observable, create_options["default"]); - delete create_options["default"]; - } - return observable; - } - - Observable.prototype.destroy = function() { - this.__kb_destroyed = true; - kb.release(this.__kb_value); - this.__kb_value = null; - this.vm = null; - this.create_options = null; - return kb.utils.wrappedDestroy(this); - }; - - Observable.prototype.value = function() { - return this.__kb_value; - }; - - Observable.prototype.valueType = function() { - var new_value; - new_value = this.m ? this.m.get(this.key) : null; - this.value_type || this._updateValueObservable(new_value); - return this.value_type; - }; - - Observable.prototype.model = function(new_model) { - if ((arguments.length === 0) || (this.m === new_model)) { - return this.m; - } - this.m = new_model; - return this.__kb_destroyed || this.update(); - }; - - Observable.prototype.update = function(new_value) { - var new_type, value; - if (this.m && !arguments.length) { - new_value = this.m.get(ko.utils.unwrapObservable(this.key)); - } - new_value || (new_value = null); - new_type = kb.utils.valueType(new_value); - if (!this.__kb_value || (this.__kb_value.__kb_destroyed || (this.__kb_value.__kb_null && new_value))) { - this.__kb_value = null; - this.value_type = void 0; - } - value = this.__kb_value; - if (_.isUndefined(this.value_type) || (this.value_type !== new_type && new_type !== KB_TYPE_UNKNOWN)) { - if ((this.value_type === KB_TYPE_COLLECTION) && (new_type === KB_TYPE_ARRAY)) { - return value(new_value); - } else { - return this._updateValueObservable(new_value); - } - } else if (this.value_type === KB_TYPE_MODEL) { - if (typeof value.model === 'function') { - if (value.model() !== new_value) { - return value.model(new_value); - } - } else if (kb.utils.wrappedObject(value) !== new_value) { - return this._updateValueObservable(new_value); - } - } else if (this.value_type === KB_TYPE_COLLECTION) { - if (value.collection() !== new_value) { - return value.collection(new_value); - } - } else { - if (value() !== new_value) { - return value(new_value); - } - } - }; - - Observable.prototype._updateValueObservable = function(new_value) { - var create_options, creator, previous_value, value; - create_options = this.create_options; - create_options.creator = kb.utils.inferCreator(new_value, create_options.factory, create_options.path, this.m, this.key); - this.value_type = KB_TYPE_UNKNOWN; - creator = create_options.creator; - previous_value = this.__kb_value; - this.__kb_value = null; - if (previous_value) { - kb.release(previous_value); - } - if (creator) { - if (create_options.store) { - value = create_options.store.findOrCreate(new_value, create_options); - } else { - if (creator.models_only) { - value = new_value; - this.value_type = KB_TYPE_SIMPLE; - } else if (creator.create) { - value = creator.create(new_value, create_options); - } else { - value = new creator(new_value, create_options); - } - } - } else { - this.value_type = KB_TYPE_SIMPLE; - if (_.isArray(new_value)) { - value = ko.observableArray(new_value); - } else { - value = ko.observable(new_value); - } - } - if (this.value_type === KB_TYPE_UNKNOWN) { - if (!ko.isObservable(value)) { - this.value_type = KB_TYPE_MODEL; - if (typeof value.model !== 'function') { - kb.utils.wrappedObject(value, new_value); - } - } else if (value.__kb_is_co) { - this.value_type = KB_TYPE_COLLECTION; - } else { - this.value_type = KB_TYPE_SIMPLE; - } - } - this.__kb_value = value; - return this.vo(value); - }; - - return Observable; - -})(); - -kb.observable = function(model, options, view_model) { - return new kb.Observable(model, options, view_model); -}; - -/* - knockback-view-model.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Observable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.ViewModel = (function() { - - ViewModel.extend = Backbone.Model.extend; - - function ViewModel(model, options, view_model) { - var attribute_keys, bb_model, keys, mapped_keys, mapping_info, model_watcher, vm_key, _ref; - !model || (model instanceof Backbone.Model) || ((typeof model.get === 'function') && (typeof model.bind === 'function')) || throwUnexpected(this, 'not a model'); - options || (options = {}); - view_model || (view_model = {}); - if (_.isArray(options)) { - options = { - keys: options - }; - } else { - options = collapseOptions(options); - } - this.__kb || (this.__kb = {}); - this.__kb.vm_keys = {}; - this.__kb.model_keys = {}; - this.__kb.view_model = _.isUndefined(view_model) ? this : view_model; - !options.internals || (this.__kb.internals = options.internals); - !options.excludes || (this.__kb.excludes = options.excludes); - kb.Store.useOptionsOrCreate(options, model, this); - this.__kb.path = options.path; - kb.Factory.useOptionsOrCreate(options, this, options.path); - model_watcher = kb.utils.wrappedModelWatcher(this, new kb.ModelWatcher(model, this, { - model: _.bind(this.model, this) - })); - if (options.requires && _.isArray(options.requires)) { - keys = _.clone(options.requires); - } - if (this.__kb.internals) { - keys = keys ? _.union(keys, this.__kb.internals) : _.clone(this.__kb.internals); - } - if (options.keys) { - if (_.isArray(options.keys)) { - this.__kb.keys = options.keys; - keys = keys ? _.union(keys, options.keys) : _.clone(options.keys); - } else { - mapped_keys = {}; - _ref = options.keys; - for (vm_key in _ref) { - mapping_info = _ref[vm_key]; - mapped_keys[_.isString(mapping_info) ? mapping_info : (mapping_info.key ? mapping_info.key : vm_key)] = true; - } - this.__kb.keys = _.keys(mapped_keys); - } - } else { - bb_model = model_watcher.model(); - if (bb_model && bb_model.attributes) { - attribute_keys = _.keys(bb_model.attributes); - keys = keys ? _.union(keys, attribute_keys) : attribute_keys; - } - } - if (keys && this.__kb.excludes) { - keys = _.difference(keys, this.__kb.excludes); - } - if (_.isObject(options.keys) && !_.isArray(options.keys)) { - this._mapObservables(model, options.keys); - } - if (_.isObject(options.requires) && !_.isArray(options.requires)) { - this._mapObservables(model, options.requires); - } - !options.mappings || this._mapObservables(model, options.mappings); - !keys || this._createObservables(model, keys); - !kb.statistics || kb.statistics.register('ViewModel', this); - } - - ViewModel.prototype.destroy = function() { - var vm_key; - if (this.__kb.view_model !== this) { - for (vm_key in this.__kb.vm_keys) { - this.__kb.view_model[vm_key] = null; - } - } - this.__kb.view_model = null; - kb.releaseKeys(this); - kb.utils.wrappedDestroy(this); - return !kb.statistics || kb.statistics.unregister('ViewModel', this); - }; - - ViewModel.prototype.shareOptions = function() { - return { - store: kb.utils.wrappedStore(this), - factory: kb.utils.wrappedFactory(this) - }; - }; - - ViewModel.prototype.model = function(new_model) { - var missing, model, model_watcher; - model = kb.utils.wrappedObject(this); - if ((arguments.length === 0) || (model === new_model)) { - return model; - } - if (this.__kb_null) { - !new_model || throwUnexpected(this, 'model set on shared null'); - return; - } - kb.utils.wrappedObject(this, new_model); - model_watcher = kb.utils.wrappedModelWatcher(this); - if (!model_watcher) { - return; - } - model_watcher.model(new_model); - if (this.__kb.keys || !new_model || !new_model.attributes) { - return; - } - missing = _.difference(_.keys(new_model.attributes), _.keys(this.__kb.model_keys)); - if (missing) { - return this._createObservables(new_model, missing); - } - }; - - ViewModel.prototype._createObservables = function(model, keys) { - var create_options, key, vm_key, _i, _len; - create_options = { - store: kb.utils.wrappedStore(this), - factory: kb.utils.wrappedFactory(this), - path: this.__kb.path, - model_watcher: kb.utils.wrappedModelWatcher(this) - }; - for (_i = 0, _len = keys.length; _i < _len; _i++) { - key = keys[_i]; - vm_key = this.__kb.internals && _.contains(this.__kb.internals, key) ? "_" + key : key; - if (this[vm_key]) { - continue; - } - this.__kb.vm_keys[vm_key] = true; - this.__kb.model_keys[key] = true; - create_options.key = key; - this[vm_key] = this.__kb.view_model[vm_key] = kb.observable(model, create_options, this); - } - return this; - }; - - ViewModel.prototype._mapObservables = function(model, mappings) { - var create_options, mapping_info, vm_key; - create_options = { - store: kb.utils.wrappedStore(this), - factory: kb.utils.wrappedFactory(this), - path: this.__kb.path, - model_watcher: kb.utils.wrappedModelWatcher(this) - }; - for (vm_key in mappings) { - mapping_info = mappings[vm_key]; - if (this[vm_key]) { - continue; - } - mapping_info = _.isString(mapping_info) ? { - key: mapping_info - } : _.clone(mapping_info); - mapping_info.key || (mapping_info.key = vm_key); - this.__kb.vm_keys[vm_key] = true; - this.__kb.model_keys[mapping_info.key] = true; - this[vm_key] = this.__kb.view_model[vm_key] = kb.observable(model, _.defaults(mapping_info, create_options), this); - } - return this; - }; - - return ViewModel; - -})(); - -kb.viewModel = function(model, options, view_model) { - return new kb.ViewModel(model, options, view_model); -}; - -kb.observables = function(model, binding_info, view_model) { - legacyWarning('kb.observables', '0.16.4', 'Please use kb.viewModel instead'); - return new kb.ViewModel(model, binding_info, view_model); -}; - -/* - knockback-collection-observable.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.CollectionObservable is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -kb.CollectionObservable = (function() { - - CollectionObservable.extend = Backbone.Model.extend; - - function CollectionObservable(collection, options) { - var create_options, observable, - _this = this; - !collection || (collection instanceof Backbone.Collection) || throwUnexpected(this, 'not a collection'); - options || (options = {}); - observable = kb.utils.wrappedObservable(this, ko.observableArray([])); - observable.__kb_is_co = true; - this.in_edit = 0; - this.__kb || (this.__kb = {}); - this.__kb._onCollectionChange = _.bind(this._onCollectionChange, this); - options = collapseOptions(options); - if (options.sort_attribute) { - this.sorted_index_fn = ko.observable(this._sortAttributeFn(options.sort_attribute)); - } else { - if (options.sorted_index) { - legacyWarning(this, '0.16.4', 'use sorted_index_fn instead'); - options.sorted_index_fn = options.sorted_index; - } - this.sorted_index_fn = ko.observable(options.sorted_index_fn); - } - if (options.filters) { - this.filters = ko.observableArray(_.isArray(options.filters) ? options.filters : options.filters ? [options.filters] : void 0); - } else { - this.filters = ko.observableArray([]); - } - create_options = this.create_options = { - store: kb.Store.useOptionsOrCreate(options, collection, observable) - }; - this.path = options.path; - create_options.factory = kb.utils.wrappedFactory(observable, this._shareOrCreateFactory(options)); - create_options.path = kb.utils.pathJoin(options.path, 'models'); - create_options.creator = create_options.factory.creatorForPath(null, create_options.path); - if (create_options.creator) { - this.models_only = create_options.creator.models_only; - } - observable.destroy = _.bind(this.destroy, this); - observable.shareOptions = _.bind(this.shareOptions, this); - observable.collection = _.bind(this.collection, this); - observable.viewModelByModel = _.bind(this.viewModelByModel, this); - observable.sortedIndex = _.bind(this.sortedIndex, this); - observable.sortAttribute = _.bind(this.sortAttribute, this); - observable.hasViewModels = _.bind(this.hasViewModels, this); - this._col = ko.observable(); - this.collection(collection); - this._mapper = ko.dependentObservable(function() { - var filters, model, models, sorted_index_fn, view_model, view_models, _i, _len; - if (_this.in_edit) { - return; - } - observable = kb.utils.wrappedObservable(_this); - collection = _this._col(); - if (collection) { - models = collection.models; - } - sorted_index_fn = _this.sorted_index_fn(); - filters = _this.filters(); - if (!models || (collection.models.length === 0)) { - view_models = []; - } else { - if (filters.length) { - models = _.filter(models, function(model) { - return !_this._modelIsFiltered(model); - }); - } - if (sorted_index_fn) { - view_models = []; - for (_i = 0, _len = models.length; _i < _len; _i++) { - model = models[_i]; - view_model = _this._createViewModel(model); - view_models.splice(sorted_index_fn(view_models, view_model), 0, view_model); - } - } else { - if (_this.models_only) { - view_models = filters.length ? models : models.slice(); - } else { - view_models = _.map(models, function(model) { - return _this._createViewModel(model); - }); - } - } - } - _this.in_edit++; - observable(view_models); - return _this.in_edit--; - }); - observable.subscribe(_.bind(this._onObservableArrayChange, this)); - !kb.statistics || kb.statistics.register('CollectionObservable', this); - return observable; - } - - CollectionObservable.prototype.destroy = function() { - var array, collection, observable; - observable = kb.utils.wrappedObservable(this); - collection = this._col(); - if (collection) { - collection.unbind('all', this.__kb._onCollectionChange); - array = observable(); - array.splice(0, array.length); - } - kb.release(this.filters); - this.filters = this._col = this.sorted_index_fn = this._mapper = this.create_options = null; - kb.utils.wrappedDestroy(this); - return !kb.statistics || kb.statistics.unregister('CollectionObservable', this); - }; - - CollectionObservable.prototype.shareOptions = function() { - var observable; - observable = kb.utils.wrappedObservable(this); - return { - store: kb.utils.wrappedStore(observable), - factory: kb.utils.wrappedFactory(observable) - }; - }; - - CollectionObservable.prototype.collection = function(collection) { - var observable, previous_collection; - observable = kb.utils.wrappedObservable(this); - previous_collection = this._col(); - if ((arguments.length === 0) || (collection === previous_collection)) { - observable(); - return previous_collection; - } - if (previous_collection) { - previous_collection.unbind('all', this.__kb._onCollectionChange); - } - if (collection) { - collection.bind('all', this.__kb._onCollectionChange); - } - this._col(collection); - return collection; - }; - - CollectionObservable.prototype.filters = function(filters) { - if (filters) { - return this.filters(_.isArray(filters) ? filters : [filters]); - } else { - return this.filters([]); - } - }; - - CollectionObservable.prototype.sortedIndex = function(sorted_index_fn) { - return this.sorted_index_fn(sorted_index_fn); - }; - - CollectionObservable.prototype.sortAttribute = function(sort_attribute) { - return this.sorted_index_fn(sort_attribute ? this._sortAttributeFn(sort_attribute) : null); - }; - - CollectionObservable.prototype.viewModelByModel = function(model) { - var id_attribute; - if (this.models_only) { - return null; - } - id_attribute = model.hasOwnProperty(model.idAttribute) ? model.idAttribute : 'cid'; - return _.find(kb.utils.wrappedObservable(this)(), function(test) { - return test.__kb.object[id_attribute] === model[id_attribute]; - }); - }; - - CollectionObservable.prototype.hasViewModels = function() { - return !this.models_only; - }; - - CollectionObservable.prototype._shareOrCreateFactory = function(options) { - var absolute_models_path, existing_creator, factories, factory; - absolute_models_path = kb.utils.pathJoin(options.path, 'models'); - factories = options.factories; - if ((factory = options.factory)) { - if ((existing_creator = factory.creatorForPath(null, absolute_models_path)) && (!factories || (factories['models'] === existing_creator))) { - if (!factories) { - return factory; - } - if (factory.hasPathMappings(factories, options.path)) { - return factory; - } - } - } - factory = new kb.Factory(options.factory); - if (factories) { - factory.addPathMappings(factories, options.path); - } - if (!factory.creatorForPath(null, absolute_models_path)) { - if (options.hasOwnProperty('models_only')) { - if (options.models_only) { - factory.addPathMapping(absolute_models_path, { - models_only: true - }); - } else { - factory.addPathMapping(absolute_models_path, kb.ViewModel); - } - } else if (options.view_model) { - factory.addPathMapping(absolute_models_path, options.view_model); - } else if (options.create) { - factory.addPathMapping(absolute_models_path, { - create: options.create - }); - } else { - factory.addPathMapping(absolute_models_path, kb.ViewModel); - } - } - return factory; - }; - - CollectionObservable.prototype._onCollectionChange = function(event, arg) { - var add_index, collection, observable, sorted_index_fn, view_model; - if (this.in_edit) { - return; - } - switch (event) { - case 'reset': - case 'resort': - if (event === 'resort' && !this.sorted_index_fn()) { - return; - } - return this._col.notifySubscribers(this._col()); - case 'new': - case 'add': - if (this._modelIsFiltered(arg)) { - return; - } - observable = kb.utils.wrappedObservable(this); - collection = this._col(); - view_model = this._createViewModel(arg); - if ((sorted_index_fn = this.sorted_index_fn())) { - add_index = sorted_index_fn(observable(), view_model); - } else { - add_index = collection.indexOf(arg); - } - this.in_edit++; - observable.splice(add_index, 0, view_model); - return this.in_edit--; - case 'remove': - case 'destroy': - return this._onModelRemove(arg); - case 'change': - if (this._modelIsFiltered(arg)) { - return this._onModelRemove(arg); - } else if (this.sorted_index_fn()) { - return this._onModelResort(arg); - } - } - }; - - CollectionObservable.prototype._onModelRemove = function(model) { - var observable, view_model; - view_model = this.models_only ? model : this.viewModelByModel(model); - if (!view_model) { - return; - } - observable = kb.utils.wrappedObservable(this); - this.in_edit++; - observable.remove(view_model); - return this.in_edit--; - }; - - CollectionObservable.prototype._onModelResort = function(model) { - var new_index, observable, previous_index, sorted_index_fn, sorted_view_models, view_model; - observable = kb.utils.wrappedObservable(this); - view_model = this.models_only ? model : this.viewModelByModel(model); - previous_index = observable.indexOf(view_model); - if ((sorted_index_fn = this.sorted_index_fn())) { - sorted_view_models = _.clone(observable()); - sorted_view_models.splice(previous_index, 1); - new_index = sorted_index_fn(sorted_view_models, view_model); - } else { - new_index = this._col().indexOf(model); - } - if (previous_index === new_index) { - return; - } - this.in_edit++; - observable.splice(previous_index, 1); - observable.splice(new_index, 0, view_model); - return this.in_edit--; - }; - - CollectionObservable.prototype._onObservableArrayChange = function(values) { - var collection, has_view_model, models, observable, value, _i, _j, _len, _len1, - _this = this; - if (this.in_edit) { - return; - } - observable = kb.utils.wrappedObservable(this); - collection = this._col(); - if (!collection) { - return; - } - if (!this.models_only) { - for (_i = 0, _len = values.length; _i < _len; _i++) { - value = values[_i]; - if (value && !(value instanceof Backbone.Model)) { - has_view_model = true; - break; - } - } - if (has_view_model) { - for (_j = 0, _len1 = values.length; _j < _len1; _j++) { - value = values[_j]; - this.create_options.store.findOrReplace(kb.utils.wrappedObject(value), this.create_options.creator, value); - } - } - } - models = _.map(values, function(test) { - return kb.utils.wrappedModel(test); - }); - if (this.filters().length) { - models = _.filter(models, function(model) { - return !_this._modelIsFiltered(model); - }); - } - this.in_edit++; - collection.reset(models); - this.in_edit--; - return this; - }; - - CollectionObservable.prototype._sortAttributeFn = function(sort_attribute) { - if (this.models_only) { - return function(models, model) { - var attribute_name; - attribute_name = ko.utils.unwrapObservable(sort_attribute); - return _.sortedIndex(models, model, function(test) { - return test.get(attribute_name); - }); - }; - } else { - return function(view_models, model) { - var attribute_name; - attribute_name = ko.utils.unwrapObservable(sort_attribute); - return _.sortedIndex(view_models, model, function(test) { - return kb.utils.wrappedModel(test).get(attribute_name); - }); - }; - } - }; - - CollectionObservable.prototype._createViewModel = function(model) { - if (this.models_only) { - return model; - } else { - return this.create_options.store.findOrCreate(model, this.create_options); - } - }; - - CollectionObservable.prototype._modelIsFiltered = function(model) { - var filter, filters, _i, _len; - filters = this.filters(); - for (_i = 0, _len = filters.length; _i < _len; _i++) { - filter = filters[_i]; - filter = ko.utils.unwrapObservable(filter); - if (((typeof filter === 'function') && filter(model)) || (model && (model.id === filter))) { - return true; - } - } - return false; - }; - - return CollectionObservable; - -})(); - -kb.collectionObservable = function(collection, options) { - return new kb.CollectionObservable(collection, options); -}; - -kb.sortedIndexWrapAttr = kb.siwa = function(attribute_name, wrapper_constructor) { - return function(models, model) { - return _.sortedIndex(models, model, function(test) { - return new wrapper_constructor(kb.utils.wrappedModel(test).get(attribute_name)); - }); - }; -}; - -/* - knockback-inject.js - (c) 2011, 2012 Kevin Malakoff. - Knockback.Inject is freely distributable under the MIT license. - See the following for full license details: - https://github.com/kmalakoff/knockback/blob/master/LICENSE -*/ - - -ko.bindingHandlers['inject'] = { - 'init': function(element, value_accessor, all_bindings_accessor, view_model) { - var data, wrapper; - data = ko.utils.unwrapObservable(value_accessor()); - wrapper = ko.dependentObservable(function() { - var key, value, _results; - if (_.isFunction(data)) { - return data(view_model, element, value_accessor, all_bindings_accessor); - } else if (_.isObject(data)) { - _results = []; - for (key in data) { - value = data[key]; - if (_.isObject(value) && value.resolve && _.isFunction(value.resolve)) { - _results.push(view_model[key] = value.resolve(view_model, element, value_accessor, all_bindings_accessor)); - } else { - _results.push(view_model[key] = value); - } - } - return _results; - } - }); - return wrapper.dispose(); - } -}; - -kb.injectApps = function(root) { - var app, apps, getAppElements, options, _i, _len; - apps = []; - getAppElements = function(el) { - var attr, child_el, _i, _len, _ref; - if (!el.__kb_injected) { - if (el.attributes && (attr = _.find(el.attributes, function(attr) { - return attr.name === 'kb-app'; - }))) { - el.__kb_injected = true; - apps.push([el, attr]); - } - } - _ref = el.childNodes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child_el = _ref[_i]; - getAppElements(child_el); - } - }; - getAppElements(root || document); - for (_i = 0, _len = apps.length; _i < _len; _i++) { - app = apps[_i]; - if (app[1].value) { - options = ko.utils.buildEvalWithinScopeFunction("{" + app[1].value + "}", 0)(); - } - options || (options = {}); - options.view_model || (options.view_model = {}); - if (options.beforeBinding) { - options.beforeBinding(options.view_model, app[0], options); - } - kb.applyBindings(options.view_model, app[0], {}); - if (options.afterBinding) { - options.afterBinding(options.view_model, app[0], options); - } - } -}; - -(onReady = function() { - if (!document.body) { - return setTimeout(onReady, 1); - } - kb.injectApps(); -})(); -; return kb;}); -}).call(this); \ No newline at end of file diff --git a/test/vendor/knockback-core-stack-0.16.4.js b/test/vendor/knockback-core-stack-0.16.4.js deleted file mode 100644 index e1da736..0000000 --- a/test/vendor/knockback-core-stack-0.16.4.js +++ /dev/null @@ -1,7727 +0,0 @@ -// Underscore.js 1.3.3 -// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Underscore is freely distributable under the MIT license. -// Portions of Underscore are inspired or borrowed from Prototype, -// Oliver Steele's Functional, and John Resig's Micro-Templating. -// For all details and documentation: -// http://documentcloud.github.com/underscore - -(function() { - - // Baseline setup - // -------------- - - // Establish the root object, `window` in the browser, or `global` on the server. - var root = this; - - // Save the previous value of the `_` variable. - var previousUnderscore = root._; - - // Establish the object that gets returned to break out of a loop iteration. - var breaker = {}; - - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; - - // Create quick reference variables for speed access to core prototypes. - var slice = ArrayProto.slice, - unshift = ArrayProto.unshift, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; - - // All **ECMAScript 5** native function implementations that we hope to use - // are declared here. - var - nativeForEach = ArrayProto.forEach, - nativeMap = ArrayProto.map, - nativeReduce = ArrayProto.reduce, - nativeReduceRight = ArrayProto.reduceRight, - nativeFilter = ArrayProto.filter, - nativeEvery = ArrayProto.every, - nativeSome = ArrayProto.some, - nativeIndexOf = ArrayProto.indexOf, - nativeLastIndexOf = ArrayProto.lastIndexOf, - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind; - - // Create a safe reference to the Underscore object for use below. - var _ = function(obj) { return new wrapper(obj); }; - - // Export the Underscore object for **Node.js**, with - // backwards-compatibility for the old `require()` API. If we're in - // the browser, add `_` as a global object via a string identifier, - // for Closure Compiler "advanced" mode. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = _; - } - exports._ = _; - } else { - root['_'] = _; - } - - // Current version. - _.VERSION = '1.3.3'; - - // Collection Functions - // -------------------- - - // The cornerstone, an `each` implementation, aka `forEach`. - // Handles objects with the built-in `forEach`, arrays, and raw objects. - // Delegates to **ECMAScript 5**'s native `forEach` if available. - var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; - if (nativeForEach && obj.forEach === nativeForEach) { - obj.forEach(iterator, context); - } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return; - } - } else { - for (var key in obj) { - if (_.has(obj, key)) { - if (iterator.call(context, obj[key], key, obj) === breaker) return; - } - } - } - }; - - // Return the results of applying the iterator to each element. - // Delegates to **ECMAScript 5**'s native `map` if available. - _.map = _.collect = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); - each(obj, function(value, index, list) { - results[results.length] = iterator.call(context, value, index, list); - }); - if (obj.length === +obj.length) results.length = obj.length; - return results; - }; - - // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. - _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduce && obj.reduce === nativeReduce) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); - } - each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator.call(context, memo, value, index, list); - } - }); - if (!initial) throw new TypeError('Reduce of empty array with no initial value'); - return memo; - }; - - // The right-associative version of reduce, also known as `foldr`. - // Delegates to **ECMAScript 5**'s native `reduceRight` if available. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); - } - var reversed = _.toArray(obj).reverse(); - if (context && !initial) iterator = _.bind(iterator, context); - return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator); - }; - - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { - var result; - any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { - result = value; - return true; - } - }); - return result; - }; - - // Return all the elements that pass a truth test. - // Delegates to **ECMAScript 5**'s native `filter` if available. - // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); - each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results[results.length] = value; - }); - return results; - }; - - // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - each(obj, function(value, index, list) { - if (!iterator.call(context, value, index, list)) results[results.length] = value; - }); - return results; - }; - - // Determine whether all of the elements match a truth test. - // Delegates to **ECMAScript 5**'s native `every` if available. - // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - var result = true; - if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); - each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if at least one element in the object matches a truth test. - // Delegates to **ECMAScript 5**'s native `some` if available. - // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if a given value is included in the array or object using `===`. - // Aliased as `contains`. - _.include = _.contains = function(obj, target) { - var found = false; - if (obj == null) return found; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - found = any(obj, function(value) { - return value === target; - }); - return found; - }; - - // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - return _.map(obj, function(value) { - return (_.isFunction(method) ? method || value : value[method]).apply(value, args); - }); - }; - - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); - }; - - // Return the maximum element or (element-based computation). - _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj); - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed >= result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj); - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Shuffle an array. - _.shuffle = function(obj) { - var shuffled = [], rand; - each(obj, function(value, index, list) { - rand = Math.floor(Math.random() * (index + 1)); - shuffled[index] = shuffled[rand]; - shuffled[rand] = value; - }); - return shuffled; - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, val, context) { - var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; - return _.pluck(_.map(obj, function(value, index, list) { - return { - value : value, - criteria : iterator.call(context, value, index, list) - }; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - if (a === void 0) return 1; - if (b === void 0) return -1; - return a < b ? -1 : a > b ? 1 : 0; - }), 'value'); - }; - - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = function(obj, val) { - var result = {}; - var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; - each(obj, function(value, index) { - var key = iterator(value, index); - (result[key] || (result[key] = [])).push(value); - }); - return result; - }; - - // Use a comparator function to figure out at what index an object should - // be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator) { - iterator || (iterator = _.identity); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >> 1; - iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; - } - return low; - }; - - // Safely convert anything iterable into a real, live array. - _.toArray = function(obj) { - if (!obj) return []; - if (_.isArray(obj)) return slice.call(obj); - if (_.isArguments(obj)) return slice.call(obj); - if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray(); - return _.values(obj); - }; - - // Return the number of elements in an object. - _.size = function(obj) { - return _.isArray(obj) ? obj.length : _.keys(obj).length; - }; - - // Array Functions - // --------------- - - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head` and `take`. The **guard** check - // allows it to work with `_.map`. - _.first = _.head = _.take = function(array, n, guard) { - return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; - }; - - // Returns everything but the last entry of the array. Especcialy useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. - _.initial = function(array, n, guard) { - return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); - }; - - // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. - _.last = function(array, n, guard) { - if ((n != null) && !guard) { - return slice.call(array, Math.max(array.length - n, 0)); - } else { - return array[array.length - 1]; - } - }; - - // Returns everything but the first entry of the array. Aliased as `tail`. - // Especially useful on the arguments object. Passing an **index** will return - // the rest of the values in the array from that index onward. The **guard** - // check allows it to work with `_.map`. - _.rest = _.tail = function(array, index, guard) { - return slice.call(array, (index == null) || guard ? 1 : index); - }; - - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, function(value){ return !!value; }); - }; - - // Return a completely flattened version of an array. - _.flatten = function(array, shallow) { - return _.reduce(array, function(memo, value) { - if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value)); - memo[memo.length] = value; - return memo; - }, []); - }; - - // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; - - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator) { - var initial = iterator ? _.map(array, iterator) : array; - var results = []; - // The `isSorted` flag is irrelevant if the array only contains two elements. - if (array.length < 3) isSorted = true; - _.reduce(initial, function (memo, value, index) { - if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) { - memo.push(value); - results.push(array[index]); - } - return memo; - }, []); - return results; - }; - - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = function() { - return _.uniq(_.flatten(arguments, true)); - }; - - // Produce an array that contains every item shared between all the - // passed-in arrays. (Aliased as "intersect" for back-compat.) - _.intersection = _.intersect = function(array) { - var rest = slice.call(arguments, 1); - return _.filter(_.uniq(array), function(item) { - return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; - }); - }); - }; - - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = _.flatten(slice.call(arguments, 1), true); - return _.filter(array, function(value){ return !_.include(rest, value); }); - }; - - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = function() { - var args = slice.call(arguments); - var length = _.max(_.pluck(args, 'length')); - var results = new Array(length); - for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); - return results; - }; - - // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), - // we need this function. Return the position of the first occurrence of an - // item in an array, or -1 if the item is not included in the array. - // Delegates to **ECMAScript 5**'s native `indexOf` if available. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i, l; - if (isSorted) { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; - } - if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); - for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i; - return -1; - }; - - // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. - _.lastIndexOf = function(array, item) { - if (array == null) return -1; - if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); - var i = array.length; - while (i--) if (i in array && array[i] === item) return i; - return -1; - }; - - // Generate an integer Array containing an arithmetic progression. A port of - // the native Python `range()` function. See - // [the Python documentation](http://docs.python.org/library/functions.html#range). - _.range = function(start, stop, step) { - if (arguments.length <= 1) { - stop = start || 0; - start = 0; - } - step = arguments[2] || 1; - - var len = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = new Array(len); - - while(idx < len) { - range[idx++] = start; - start += step; - } - - return range; - }; - - // Function (ahem) Functions - // ------------------ - - // Reusable constructor function for prototype setting. - var ctor = function(){}; - - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Binding with arguments is also known as `curry`. - // Delegates to **ECMAScript 5**'s native `Function.bind` if available. - // We check for `func.bind` first, to fail fast when `func` is undefined. - _.bind = function bind(func, context) { - var bound, args; - if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw new TypeError; - args = slice.call(arguments, 2); - return bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - ctor.prototype = func.prototype; - var self = new ctor; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; - }; - }; - - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. - _.bindAll = function(obj) { - var funcs = slice.call(arguments, 1); - if (funcs.length == 0) funcs = _.functions(obj); - each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); - return obj; - }; - - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memo = {}; - hasher || (hasher = _.identity); - return function() { - var key = hasher.apply(this, arguments); - return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); - }; - }; - - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(null, args); }, wait); - }; - - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; - - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. - _.throttle = function(func, wait) { - var context, args, timeout, throttling, more, result; - var whenDone = _.debounce(function(){ more = throttling = false; }, wait); - return function() { - context = this; args = arguments; - var later = function() { - timeout = null; - if (more) func.apply(context, args); - whenDone(); - }; - if (!timeout) timeout = setTimeout(later, wait); - if (throttling) { - more = true; - } else { - result = func.apply(context, args); - } - whenDone(); - throttling = true; - return result; - }; - }; - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - _.debounce = function(func, wait, immediate) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - if (immediate && !timeout) func.apply(context, args); - clearTimeout(timeout); - timeout = setTimeout(later, wait); - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - return memo = func.apply(this, arguments); - }; - }; - - // Returns the first function passed as an argument to the second, - // allowing you to adjust arguments, run code before and after, and - // conditionally execute the original function. - _.wrap = function(func, wrapper) { - return function() { - var args = [func].concat(slice.call(arguments, 0)); - return wrapper.apply(this, args); - }; - }; - - // Returns a function that is the composition of a list of functions, each - // consuming the return value of the function that follows. - _.compose = function() { - var funcs = arguments; - return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; - } - return args[0]; - }; - }; - - // Returns a function that will only be executed after being called N times. - _.after = function(times, func) { - if (times <= 0) return func(); - return function() { - if (--times < 1) { return func.apply(this, arguments); } - }; - }; - - // Object Functions - // ---------------- - - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var keys = []; - for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; - return keys; - }; - - // Retrieve the values of an object's properties. - _.values = function(obj) { - return _.map(obj, _.identity); - }; - - // Return a sorted list of the function names available on the object. - // Aliased as `methods` - _.functions = _.methods = function(obj) { - var names = []; - for (var key in obj) { - if (_.isFunction(obj[key])) names.push(key); - } - return names.sort(); - }; - - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - each(slice.call(arguments, 1), function(source) { - for (var prop in source) { - obj[prop] = source[prop]; - } - }); - return obj; - }; - - // Return a copy of the object only containing the whitelisted properties. - _.pick = function(obj) { - var result = {}; - each(_.flatten(slice.call(arguments, 1)), function(key) { - if (key in obj) result[key] = obj[key]; - }); - return result; - }; - - // Fill in a given object with default properties. - _.defaults = function(obj) { - each(slice.call(arguments, 1), function(source) { - for (var prop in source) { - if (obj[prop] == null) obj[prop] = source[prop]; - } - }); - return obj; - }; - - // Create a (shallow-cloned) duplicate of an object. - _.clone = function(obj) { - if (!_.isObject(obj)) return obj; - return _.isArray(obj) ? obj.slice() : _.extend({}, obj); - }; - - // Invokes interceptor with the obj, and then returns obj. - // The primary purpose of this method is to "tap into" a method chain, in - // order to perform operations on intermediate results within the chain. - _.tap = function(obj, interceptor) { - interceptor(obj); - return obj; - }; - - // Internal recursive comparison function. - function eq(a, b, stack) { - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. - if (a === b) return a !== 0 || 1 / a == 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; - // Unwrap any wrapped objects. - if (a._chain) a = a._wrapped; - if (b._chain) b = b._wrapped; - // Invoke a custom `isEqual` method if one is provided. - if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b); - if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className != toString.call(b)) return false; - switch (className) { - // Strings, numbers, dates, and booleans are compared by value. - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return a == String(b); - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - return +a == +b; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') return false; - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = stack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (stack[length] == a) return true; - } - // Add the first object to the stack of traversed objects. - stack.push(a); - var size = 0, result = true; - // Recursively compare objects and arrays. - if (className == '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - // Ensure commutative equality for sparse arrays. - if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break; - } - } - } else { - // Objects with different constructors are not equivalent. - if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false; - // Deep compare objects. - for (var key in a) { - if (_.has(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (_.has(b, key) && !(size--)) break; - } - result = !size; - } - } - // Remove the first object from the stack of traversed objects. - stack.pop(); - return result; - } - - // Perform a deep comparison to check if two objects are equal. - _.isEqual = function(a, b) { - return eq(a, b, []); - }; - - // Is a given array, string, or object empty? - // An "empty" object has no enumerable own-properties. - _.isEmpty = function(obj) { - if (obj == null) return true; - if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; - }; - - // Is a given value a DOM element? - _.isElement = function(obj) { - return !!(obj && obj.nodeType == 1); - }; - - // Is a given value an array? - // Delegates to ECMA5's native Array.isArray - _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; - }; - - // Is a given variable an object? - _.isObject = function(obj) { - return obj === Object(obj); - }; - - // Is a given variable an arguments object? - _.isArguments = function(obj) { - return toString.call(obj) == '[object Arguments]'; - }; - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return !!(obj && _.has(obj, 'callee')); - }; - } - - // Is a given value a function? - _.isFunction = function(obj) { - return toString.call(obj) == '[object Function]'; - }; - - // Is a given value a string? - _.isString = function(obj) { - return toString.call(obj) == '[object String]'; - }; - - // Is a given value a number? - _.isNumber = function(obj) { - return toString.call(obj) == '[object Number]'; - }; - - // Is a given object a finite number? - _.isFinite = function(obj) { - return _.isNumber(obj) && isFinite(obj); - }; - - // Is the given value `NaN`? - _.isNaN = function(obj) { - // `NaN` is the only value for which `===` is not reflexive. - return obj !== obj; - }; - - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; - }; - - // Is a given value a date? - _.isDate = function(obj) { - return toString.call(obj) == '[object Date]'; - }; - - // Is the given value a regular expression? - _.isRegExp = function(obj) { - return toString.call(obj) == '[object RegExp]'; - }; - - // Is a given value equal to null? - _.isNull = function(obj) { - return obj === null; - }; - - // Is a given variable undefined? - _.isUndefined = function(obj) { - return obj === void 0; - }; - - // Has own property? - _.has = function(obj, key) { - return hasOwnProperty.call(obj, key); - }; - - // Utility Functions - // ----------------- - - // Run Underscore.js in *noConflict* mode, returning the `_` variable to its - // previous owner. Returns a reference to the Underscore object. - _.noConflict = function() { - root._ = previousUnderscore; - return this; - }; - - // Keep the identity function around for default iterators. - _.identity = function(value) { - return value; - }; - - // Run a function **n** times. - _.times = function (n, iterator, context) { - for (var i = 0; i < n; i++) iterator.call(context, i); - }; - - // Escape a string for HTML interpolation. - _.escape = function(string) { - return (''+string).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/'); - }; - - // If the value of the named property is a function then invoke it; - // otherwise, return it. - _.result = function(object, property) { - if (object == null) return null; - var value = object[property]; - return _.isFunction(value) ? value.call(object) : value; - }; - - // Add your own custom functions to the Underscore object, ensuring that - // they're correctly added to the OOP wrapper as well. - _.mixin = function(obj) { - each(_.functions(obj), function(name){ - addToWrapper(name, _[name] = obj[name]); - }); - }; - - // Generate a unique integer id (unique within the entire client session). - // Useful for temporary DOM ids. - var idCounter = 0; - _.uniqueId = function(prefix) { - var id = idCounter++; - return prefix ? prefix + id : id; - }; - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /.^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - '\\': '\\', - "'": "'", - 'r': '\r', - 'n': '\n', - 't': '\t', - 'u2028': '\u2028', - 'u2029': '\u2029' - }; - - for (var p in escapes) escapes[escapes[p]] = p; - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g; - - // Within an interpolation, evaluation, or escaping, remove HTML escaping - // that had been previously added. - var unescape = function(code) { - return code.replace(unescaper, function(match, escape) { - return escapes[escape]; - }); - }; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - settings = _.defaults(settings || {}, _.templateSettings); - - // Compile the template source, taking care to escape characters that - // cannot be included in a string literal and then unescape them in code - // blocks. - var source = "__p+='" + text - .replace(escaper, function(match) { - return '\\' + escapes[match]; - }) - .replace(settings.escape || noMatch, function(match, code) { - return "'+\n_.escape(" + unescape(code) + ")+\n'"; - }) - .replace(settings.interpolate || noMatch, function(match, code) { - return "'+\n(" + unescape(code) + ")+\n'"; - }) - .replace(settings.evaluate || noMatch, function(match, code) { - return "';\n" + unescape(code) + "\n;__p+='"; - }) + "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __p='';" + - "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + - source + "return __p;\n"; - - var render = new Function(settings.variable || 'obj', '_', source); - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for build time - // precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + - source + '}'; - - return template; - }; - - // Add a "chain" function, which will delegate to the wrapper. - _.chain = function(obj) { - return _(obj).chain(); - }; - - // The OOP Wrapper - // --------------- - - // If Underscore is called as a function, it returns a wrapped object that - // can be used OO-style. This wrapper holds altered versions of all the - // underscore functions. Wrapped objects may be chained. - var wrapper = function(obj) { this._wrapped = obj; }; - - // Expose `wrapper.prototype` as `_.prototype` - _.prototype = wrapper.prototype; - - // Helper function to continue chaining intermediate results. - var result = function(obj, chain) { - return chain ? _(obj).chain() : obj; - }; - - // A method to easily add functions to the OOP wrapper. - var addToWrapper = function(name, func) { - wrapper.prototype[name] = function() { - var args = slice.call(arguments); - unshift.call(args, this._wrapped); - return result(func.apply(_, args), this._chain); - }; - }; - - // Add all of the Underscore functions to the wrapper object. - _.mixin(_); - - // Add all mutator Array functions to the wrapper. - each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { - var method = ArrayProto[name]; - wrapper.prototype[name] = function() { - var wrapped = this._wrapped; - method.apply(wrapped, arguments); - var length = wrapped.length; - if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0]; - return result(wrapped, this._chain); - }; - }); - - // Add all accessor Array functions to the wrapper. - each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - wrapper.prototype[name] = function() { - return result(method.apply(this._wrapped, arguments), this._chain); - }; - }); - - // Start chaining a wrapped Underscore object. - wrapper.prototype.chain = function() { - this._chain = true; - return this; - }; - - // Extracts the result from a wrapped and chained object. - wrapper.prototype.value = function() { - return this._wrapped; - }; - -}).call(this); -// Backbone.js 0.9.2 - -// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org - -(function(){ - - // Initial Setup - // ------------- - - // Save a reference to the global object (`window` in the browser, `global` - // on the server). - var root = this; - - // Save the previous value of the `Backbone` variable, so that it can be - // restored later on, if `noConflict` is used. - var previousBackbone = root.Backbone; - - // Create a local reference to slice/splice. - var slice = Array.prototype.slice; - var splice = Array.prototype.splice; - - // The top-level namespace. All public Backbone classes and modules will - // be attached to this. Exported for both CommonJS and the browser. - var Backbone; - if (typeof exports !== 'undefined') { - Backbone = exports; - } else { - Backbone = root.Backbone = {}; - } - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '0.9.2'; - - // Require Underscore, if we're on the server, and it's not already present. - var _ = root._; - if (!_ && (typeof require !== 'undefined')) _ = require('underscore'); - - // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. - var $ = root.jQuery || root.Zepto || root.ender; - - // Set the JavaScript library that will be used for DOM manipulation and - // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery, - // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an - // alternate JavaScript library (or a mock library for testing your views - // outside of a browser). - Backbone.setDomLibrary = function(lib) { - $ = lib; - }; - - // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable - // to its previous owner. Returns a reference to this Backbone object. - Backbone.noConflict = function() { - root.Backbone = previousBackbone; - return this; - }; - - // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option - // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and - // set a `X-Http-Method-Override` header. - Backbone.emulateHTTP = false; - - // Turn on `emulateJSON` to support legacy servers that can't deal with direct - // `application/json` requests ... will encode the body as - // `application/x-www-form-urlencoded` instead and will send the model in a - // form param named `model`. - Backbone.emulateJSON = false; - - // Backbone.Events - // ----------------- - - // Regular expression used to split event strings - var eventSplitter = /\s+/; - - // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback functions - // to an event; trigger`-ing an event fires all callbacks in succession. - // - // var object = {}; - // _.extend(object, Backbone.Events); - // object.on('expand', function(){ alert('expanded'); }); - // object.trigger('expand'); - // - var Events = Backbone.Events = { - - // Bind one or more space separated events, `events`, to a `callback` - // function. Passing `"all"` will bind the callback to all events fired. - on: function(events, callback, context) { - - var calls, event, node, tail, list; - if (!callback) return this; - events = events.split(eventSplitter); - calls = this._callbacks || (this._callbacks = {}); - - // Create an immutable callback list, allowing traversal during - // modification. The tail is an empty object that will always be used - // as the next node. - while (event = events.shift()) { - list = calls[event]; - node = list ? list.tail : {}; - node.next = tail = {}; - node.context = context; - node.callback = callback; - calls[event] = {tail: tail, next: list ? list.next : node}; - } - - return this; - }, - - // Remove one or many callbacks. If `context` is null, removes all callbacks - // with that function. If `callback` is null, removes all callbacks for the - // event. If `events` is null, removes all bound callbacks for all events. - off: function(events, callback, context) { - var event, calls, node, tail, cb, ctx; - - // No events, or removing *all* events. - if (!(calls = this._callbacks)) return; - if (!(events || callback || context)) { - delete this._callbacks; - return this; - } - - // Loop through the listed events and contexts, splicing them out of the - // linked list of callbacks if appropriate. - events = events ? events.split(eventSplitter) : _.keys(calls); - while (event = events.shift()) { - node = calls[event]; - delete calls[event]; - if (!node || !(callback || context)) continue; - // Create a new list, omitting the indicated callbacks. - tail = node.tail; - while ((node = node.next) !== tail) { - cb = node.callback; - ctx = node.context; - if ((callback && cb !== callback) || (context && ctx !== context)) { - this.on(event, cb, ctx); - } - } - } - - return this; - }, - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function(events) { - var event, node, calls, tail, args, all, rest; - if (!(calls = this._callbacks)) return this; - all = calls.all; - events = events.split(eventSplitter); - rest = slice.call(arguments, 1); - - // For each event, walk through the linked list of callbacks twice, - // first to trigger the event, then to trigger any `"all"` callbacks. - while (event = events.shift()) { - if (node = calls[event]) { - tail = node.tail; - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, rest); - } - } - if (node = all) { - tail = node.tail; - args = [event].concat(rest); - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, args); - } - } - } - - return this; - } - - }; - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Backbone.Model - // -------------- - - // Create a new model, with defined attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var defaults; - attributes || (attributes = {}); - if (options && options.parse) attributes = this.parse(attributes); - if (defaults = getValue(this, 'defaults')) { - attributes = _.extend({}, defaults, attributes); - } - if (options && options.collection) this.collection = options.collection; - this.attributes = {}; - this._escapedAttributes = {}; - this.cid = _.uniqueId('c'); - this.changed = {}; - this._silent = {}; - this._pending = {}; - this.set(attributes, {silent: true}); - // Reset change tracking. - this.changed = {}; - this._silent = {}; - this._pending = {}; - this._previousAttributes = _.clone(this.attributes); - this.initialize.apply(this, arguments); - }; - - // Attach all inheritable methods to the Model prototype. - _.extend(Model.prototype, Events, { - - // A hash of attributes whose current and previous value differ. - changed: null, - - // A hash of attributes that have silently changed since the last time - // `change` was called. Will become pending attributes on the next call. - _silent: null, - - // A hash of attributes that have changed since the last `'change'` event - // began. - _pending: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Return a copy of the model's `attributes` object. - toJSON: function(options) { - return _.clone(this.attributes); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - var html; - if (html = this._escapedAttributes[attr]) return html; - var val = this.get(attr); - return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Set a hash of model attributes on the object, firing `"change"` unless - // you choose to silence it. - set: function(key, value, options) { - var attrs, attr, val; - - // Handle both - if (_.isObject(key) || key == null) { - attrs = key; - options = value; - } else { - attrs = {}; - attrs[key] = value; - } - - // Extract attributes and options. - options || (options = {}); - if (!attrs) return this; - if (attrs instanceof Model) attrs = attrs.attributes; - if (options.unset) for (attr in attrs) attrs[attr] = void 0; - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Check for changes of `id`. - if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - - var changes = options.changes = {}; - var now = this.attributes; - var escaped = this._escapedAttributes; - var prev = this._previousAttributes || {}; - - // For each `set` attribute... - for (attr in attrs) { - val = attrs[attr]; - - // If the new and current value differ, record the change. - if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) { - delete escaped[attr]; - (options.silent ? this._silent : changes)[attr] = true; - } - - // Update or delete the current value. - options.unset ? delete now[attr] : now[attr] = val; - - // If the new and previous value differ, record the change. If not, - // then remove changes for this attribute. - if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) { - this.changed[attr] = val; - if (!options.silent) this._pending[attr] = true; - } else { - delete this.changed[attr]; - delete this._pending[attr]; - } - } - - // Fire the `"change"` events. - if (!options.silent) this.change(options); - return this; - }, - - // Remove an attribute from the model, firing `"change"` unless you choose - // to silence it. `unset` is a noop if the attribute doesn't exist. - unset: function(attr, options) { - (options || (options = {})).unset = true; - return this.set(attr, null, options); - }, - - // Clear all attributes on the model, firing `"change"` unless you choose - // to silence it. - clear: function(options) { - (options || (options = {})).unset = true; - return this.set(_.clone(this.attributes), options); - }, - - // Fetch the model from the server. If the server's representation of the - // model differs from its current attributes, they will be overriden, - // triggering a `"change"` event. - fetch: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - if (!model.set(model.parse(resp, xhr), options)) return false; - if (success) success(model, resp); - }; - options.error = Backbone.wrapError(options.error, model, options); - return (this.sync || Backbone.sync).call(this, 'read', this, options); - }, - - // Set a hash of model attributes, and sync the model to the server. - // If the server returns an attributes hash that differs, the model's - // state will be `set` again. - save: function(key, value, options) { - var attrs, current; - - // Handle both `("key", value)` and `({key: value})` -style calls. - if (_.isObject(key) || key == null) { - attrs = key; - options = value; - } else { - attrs = {}; - attrs[key] = value; - } - options = options ? _.clone(options) : {}; - - // If we're "wait"-ing to set changed attributes, validate early. - if (options.wait) { - if (!this._validate(attrs, options)) return false; - current = _.clone(this.attributes); - } - - // Regular saves `set` attributes before persisting to the server. - var silentOptions = _.extend({}, options, {silent: true}); - if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) { - return false; - } - - // After a successful server-side save, the client is (optionally) - // updated with the server-side state. - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - var serverAttrs = model.parse(resp, xhr); - if (options.wait) { - delete options.wait; - serverAttrs = _.extend(attrs || {}, serverAttrs); - } - if (!model.set(serverAttrs, options)) return false; - if (success) { - success(model, resp); - } else { - model.trigger('sync', model, resp, options); - } - }; - - // Finish configuring and sending the Ajax request. - options.error = Backbone.wrapError(options.error, model, options); - var method = this.isNew() ? 'create' : 'update'; - var xhr = (this.sync || Backbone.sync).call(this, method, this, options); - if (options.wait) this.set(current, silentOptions); - return xhr; - }, - - // Destroy this model on the server if it was already persisted. - // Optimistically removes the model from its collection, if it has one. - // If `wait: true` is passed, waits for the server to respond before removal. - destroy: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - - var triggerDestroy = function() { - model.trigger('destroy', model, model.collection, options); - }; - - if (this.isNew()) { - triggerDestroy(); - return false; - } - - options.success = function(resp) { - if (options.wait) triggerDestroy(); - if (success) { - success(model, resp); - } else { - model.trigger('sync', model, resp, options); - } - }; - - options.error = Backbone.wrapError(options.error, model, options); - var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options); - if (!options.wait) triggerDestroy(); - return xhr; - }, - - // Default URL for the model's representation on the server -- if you're - // using Backbone's restful methods, override this to change the endpoint - // that will be called. - url: function() { - var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); - if (this.isNew()) return base; - return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); - }, - - // **parse** converts a response into the hash of attributes to be `set` on - // the model. The default implementation is just to pass the response along. - parse: function(resp, xhr) { - return resp; - }, - - // Create a new model with identical attributes to this one. - clone: function() { - return new this.constructor(this.attributes); - }, - - // A model is new if it has never been saved to the server, and lacks an id. - isNew: function() { - return this.id == null; - }, - - // Call this method to manually fire a `"change"` event for this model and - // a `"change:attribute"` event for each changed attribute. - // Calling this will cause all objects observing the model to update. - change: function(options) { - options || (options = {}); - var changing = this._changing; - this._changing = true; - - // Silent changes become pending changes. - for (var attr in this._silent) this._pending[attr] = true; - - // Silent changes are triggered. - var changes = _.extend({}, options.changes, this._silent); - this._silent = {}; - for (var attr in changes) { - this.trigger('change:' + attr, this, this.get(attr), options); - } - if (changing) return this; - - // Continue firing `"change"` events while there are pending changes. - while (!_.isEmpty(this._pending)) { - this._pending = {}; - this.trigger('change', this, options); - // Pending and silent changes still remain. - for (var attr in this.changed) { - if (this._pending[attr] || this._silent[attr]) continue; - delete this.changed[attr]; - } - this._previousAttributes = _.clone(this.attributes); - } - - this._changing = false; - return this; - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (!arguments.length) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var val, changed = false, old = this._previousAttributes; - for (var attr in diff) { - if (_.isEqual(old[attr], (val = diff[attr]))) continue; - (changed || (changed = {}))[attr] = val; - } - return changed; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (!arguments.length || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); - }, - - // Check if the model is currently in a valid state. It's only possible to - // get into an *invalid* state if you're using silent changes. - isValid: function() { - return !this.validate(this.attributes); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. If a specific `error` callback has - // been passed, call that instead of firing the general `"error"` event. - _validate: function(attrs, options) { - if (options.silent || !this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validate(attrs, options); - if (!error) return true; - if (options && options.error) { - options.error(this, error, options); - } else { - this.trigger('error', this, error, options); - } - return false; - } - - }); - - // Backbone.Collection - // ------------------- - - // Provides a standard collection class for our sets of models, ordered - // or unordered. If a `comparator` is specified, the Collection will maintain - // its models in sort order, as they're added and removed. - var Collection = Backbone.Collection = function(models, options) { - options || (options = {}); - if (options.model) this.model = options.model; - if (options.comparator) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, {silent: true, parse: options.parse}); - }; - - // Define the Collection's inheritable methods. - _.extend(Collection.prototype, Events, { - - // The default model for a collection is just a **Backbone.Model**. - // This should be overridden in most cases. - model: Model, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // The JSON representation of a Collection is an array of the - // models' attributes. - toJSON: function(options) { - return this.map(function(model){ return model.toJSON(options); }); - }, - - // Add a model, or list of models to the set. Pass **silent** to avoid - // firing the `add` event for every new model. - add: function(models, options) { - var i, index, length, model, cid, id, cids = {}, ids = {}, dups = []; - options || (options = {}); - models = _.isArray(models) ? models.slice() : [models]; - - // Begin by turning bare objects into model references, and preventing - // invalid models or duplicate models from being added. - for (i = 0, length = models.length; i < length; i++) { - if (!(model = models[i] = this._prepareModel(models[i], options))) { - throw new Error("Can't add an invalid model to a collection"); - } - cid = model.cid; - id = model.id; - if (cids[cid] || this._byCid[cid] || ((id != null) && (ids[id] || this._byId[id]))) { - dups.push(i); - continue; - } - cids[cid] = ids[id] = model; - } - - // Remove duplicates. - i = dups.length; - while (i--) { - models.splice(dups[i], 1); - } - - // Listen to added models' events, and index models for lookup by - // `id` and by `cid`. - for (i = 0, length = models.length; i < length; i++) { - (model = models[i]).on('all', this._onModelEvent, this); - this._byCid[model.cid] = model; - if (model.id != null) this._byId[model.id] = model; - } - - // Insert models into the collection, re-sorting if needed, and triggering - // `add` events unless silenced. - this.length += length; - index = options.at != null ? options.at : this.models.length; - splice.apply(this.models, [index, 0].concat(models)); - if (this.comparator) this.sort({silent: true}); - if (options.silent) return this; - for (i = 0, length = this.models.length; i < length; i++) { - if (!cids[(model = this.models[i]).cid]) continue; - options.index = i; - model.trigger('add', model, this, options); - } - return this; - }, - - // Remove a model, or a list of models from the set. Pass silent to avoid - // firing the `remove` event for every model removed. - remove: function(models, options) { - var i, l, index, model; - options || (options = {}); - models = _.isArray(models) ? models.slice() : [models]; - for (i = 0, l = models.length; i < l; i++) { - model = this.getByCid(models[i]) || this.get(models[i]); - if (!model) continue; - delete this._byId[model.id]; - delete this._byCid[model.cid]; - index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - this._removeReference(model); - } - return this; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - model = this._prepareModel(model, options); - this.add(model, options); - return model; - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - this.remove(model, options); - return model; - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - model = this._prepareModel(model, options); - this.add(model, _.extend({at: 0}, options)); - return model; - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - this.remove(model, options); - return model; - }, - - // Get a model from the set by id. - get: function(id) { - if (id == null) return void 0; - return this._byId[id.id != null ? id.id : id]; - }, - - // Get a model from the set by client id. - getByCid: function(cid) { - return cid && this._byCid[cid.cid || cid]; - }, - - // Get the model at the given index. - at: function(index) { - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of `filter`. - where: function(attrs) { - if (_.isEmpty(attrs)) return []; - return this.filter(function(model) { - for (var key in attrs) { - if (attrs[key] !== model.get(key)) return false; - } - return true; - }); - }, - - // Force the collection to re-sort itself. You don't need to call this under - // normal circumstances, as the set will maintain sort order as each item - // is added. - sort: function(options) { - options || (options = {}); - if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); - var boundComparator = _.bind(this.comparator, this); - if (this.comparator.length == 1) { - this.models = this.sortBy(boundComparator); - } else { - this.models.sort(boundComparator); - } - if (!options.silent) this.trigger('reset', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return _.map(this.models, function(model){ return model.get(attr); }); - }, - - // When you have more items than you want to add or remove individually, - // you can reset the entire set with a new list of models, without firing - // any `add` or `remove` events. Fires `reset` when finished. - reset: function(models, options) { - models || (models = []); - options || (options = {}); - for (var i = 0, l = this.models.length; i < l; i++) { - this._removeReference(this.models[i]); - } - this._reset(); - this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return this; - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `add: true` is passed, appends the - // models to the collection instead of resetting. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === undefined) options.parse = true; - var collection = this; - var success = options.success; - options.success = function(resp, status, xhr) { - collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); - if (success) success(collection, resp); - }; - options.error = Backbone.wrapError(options.error, collection, options); - return (this.sync || Backbone.sync).call(this, 'read', this, options); - }, - - // Create a new instance of a model in this collection. Add the model to the - // collection immediately, unless `wait: true` is passed, in which case we - // wait for the server to agree. - create: function(model, options) { - var coll = this; - options = options ? _.clone(options) : {}; - model = this._prepareModel(model, options); - if (!model) return false; - if (!options.wait) coll.add(model, options); - var success = options.success; - options.success = function(nextModel, resp, xhr) { - if (options.wait) coll.add(nextModel, options); - if (success) { - success(nextModel, resp); - } else { - nextModel.trigger('sync', model, resp, options); - } - }; - model.save(null, options); - return model; - }, - - // **parse** converts a response into a list of models to be added to the - // collection. The default implementation is just to pass it through. - parse: function(resp, xhr) { - return resp; - }, - - // Proxy to _'s chain. Can't be proxied the same way the rest of the - // underscore methods are proxied because it relies on the underscore - // constructor. - chain: function () { - return _(this.models).chain(); - }, - - // Reset all internal state. Called when the collection is reset. - _reset: function(options) { - this.length = 0; - this.models = []; - this._byId = {}; - this._byCid = {}; - }, - - // Prepare a model or hash of attributes to be added to this collection. - _prepareModel: function(model, options) { - options || (options = {}); - if (!(model instanceof Model)) { - var attrs = model; - options.collection = this; - model = new this.model(attrs, options); - if (!model._validate(model.attributes, options)) model = false; - } else if (!model.collection) { - model.collection = this; - } - return model; - }, - - // Internal method to remove a model's ties to a collection. - _removeReference: function(model) { - if (this == model.collection) { - delete model.collection; - } - model.off('all', this._onModelEvent, this); - }, - - // Internal method called every time a model in the set fires an event. - // Sets need to update their indexes when models change ids. All other - // events simply proxy through. "add" and "remove" events that originate - // in other collections are ignored. - _onModelEvent: function(event, model, collection, options) { - if ((event == 'add' || event == 'remove') && collection != this) return; - if (event == 'destroy') { - this.remove(model, options); - } - if (model && event === 'change:' + model.idAttribute) { - delete this._byId[model.previous(model.idAttribute)]; - this._byId[model.id] = model; - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', - 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', - 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', - 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', - 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; - - // Mix in each Underscore method as a proxy to `Collection#models`. - _.each(methods, function(method) { - Collection.prototype[method] = function() { - return _[method].apply(_, [this.models].concat(_.toArray(arguments))); - }; - }); - - // Backbone.Router - // ------------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = Backbone.Router = function(options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this.initialize.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var namedParam = /:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; - - // Set up all inheritable **Backbone.Router** properties and methods. - _.extend(Router.prototype, Events, { - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function(route, name, callback) { - Backbone.history || (Backbone.history = new History); - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (!callback) callback = this[name]; - Backbone.history.route(route, _.bind(function(fragment) { - var args = this._extractParameters(route, fragment); - callback && callback.apply(this, args); - this.trigger.apply(this, ['route:' + name].concat(args)); - Backbone.history.trigger('route', this, name, args); - }, this)); - return this; - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - }, - - // Bind all defined routes to `Backbone.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function() { - if (!this.routes) return; - var routes = []; - for (var route in this.routes) { - routes.unshift([route, this.routes[route]]); - } - for (var i = 0, l = routes.length; i < l; i++) { - this.route(routes[i][0], routes[i][1], this[routes[i][1]]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(namedParam, '([^\/]+)') - .replace(splatParam, '(.*?)'); - return new RegExp('^' + route + '$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted parameters. - _extractParameters: function(route, fragment) { - return route.exec(fragment).slice(1); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on URL fragments. If the - // browser does not support `onhashchange`, falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - _.bindAll(this, 'checkUrl'); - }; - - // Cached regex for cleaning leading hashes and slashes . - var routeStripper = /^[#\/]/; - - // Cached regex for detecting MSIE. - var isExplorer = /msie [\w.]+/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **Backbone.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(windowOverride) { - var loc = windowOverride ? windowOverride.location : window.location; - var match = loc.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the cross-browser normalized URL fragment, either from the URL, - // the hash, or the override. - getFragment: function(fragment, forcePushState) { - if (fragment == null) { - if (this._hasPushState || forcePushState) { - fragment = window.location.pathname; - var search = window.location.search; - if (search) fragment += search; - } else { - fragment = this.getHash(); - } - } - if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length); - return fragment.replace(routeStripper, ''); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function(options) { - if (History.started) throw new Error("Backbone.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({}, {root: '/'}, this.options, options); - this._wantsHashChange = this.options.hashChange !== false; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); - var fragment = this.getFragment(); - var docMode = document.documentMode; - var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - - if (oldIE) { - this.iframe = $('