Commit 52c9d10d authored by Aaron Boushley's avatar Aaron Boushley

Moved files into new folder structure.

parent 9857710c
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
<head> <head>
<title>Knockback Demo: Todos</title> <title>Knockback Demo: Todos</title>
<!-- Demo Dependencies --> <!-- Demo Dependencies -->
<script src="../vendor/json2.js"></script> <script src="libs/json2.js"></script>
<script src="../vendor/jquery-1.7.min.js"></script> <script src="libs/jquery-1.7.min.js"></script>
<script src="../vendor/jquery.tmpl.min.js"></script> <script src="libs/jquery.tmpl.min.js"></script>
<!-- Knockback Dependencies --> <!-- Knockback Dependencies -->
<script src="dependencies/underscore.js"></script> <script src="dependencies/underscore.js"></script>
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
<script src="dependencies/knockback.js"></script> <script src="dependencies/knockback.js"></script>
<!-- The Demo --> <!-- The Demo -->
<link href="../css/todos.css" media="all" rel="stylesheet" type="text/css"/> <link href="css/todos.css" media="all" rel="stylesheet" type="text/css"/>
<script src="../vendor/backbone-localstorage.js"></script> <script src="libs/backbone-localstorage.js"></script>
<script src="todos_classic.js"></script> <script src="todos_classic.js"></script>
</head> </head>
......
// Backbone.js 0.5.3
// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://documentcloud.github.com/backbone
(function(){
// Initial Setup
// -------------
// Save a reference to the global object.
var root = this;
// Save the previous value of the `Backbone` variable.
var previousBackbone = root.Backbone;
// 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.5.3';
// 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 or Zepto owns the `$` variable.
var $ = root.jQuery || root.Zepto;
// 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
// -----------------
// A module that can be mixed in to *any object* in order to provide it with
// custom events. You may `bind` or `unbind` a callback function to an event;
// `trigger`-ing an event fires all callbacks in succession.
//
// var object = {};
// _.extend(object, Backbone.Events);
// object.bind('expand', function(){ alert('expanded'); });
// object.trigger('expand');
//
Backbone.Events = {
// Bind an event, specified by a string name, `ev`, to a `callback` function.
// Passing `"all"` will bind the callback to all events fired.
bind : function(ev, callback, context) {
var calls = this._callbacks || (this._callbacks = {});
var list = calls[ev] || (calls[ev] = []);
list.push([callback, context]);
return this;
},
// Remove one or many callbacks. If `callback` is null, removes all
// callbacks for the event. If `ev` is null, removes all bound callbacks
// for all events.
unbind : function(ev, callback) {
var calls;
if (!ev) {
this._callbacks = {};
} else if (calls = this._callbacks) {
if (!callback) {
calls[ev] = [];
} else {
var list = calls[ev];
if (!list) return this;
for (var i = 0, l = list.length; i < l; i++) {
if (list[i] && callback === list[i][0]) {
list[i] = null;
break;
}
}
}
}
return this;
},
// Trigger an event, firing all bound callbacks. Callbacks are passed the
// same arguments as `trigger` is, apart from the event name.
// Listening for `"all"` passes the true event name as the first argument.
trigger : function(eventName) {
var list, calls, ev, callback, args;
var both = 2;
if (!(calls = this._callbacks)) return this;
while (both--) {
ev = both ? eventName : 'all';
if (list = calls[ev]) {
for (var i = 0, l = list.length; i < l; i++) {
if (!(callback = list[i])) {
list.splice(i, 1); i--; l--;
} else {
args = both ? Array.prototype.slice.call(arguments, 1) : arguments;
callback[0].apply(callback[1] || this, args);
}
}
}
}
return this;
}
};
// Backbone.Model
// --------------
// Create a new model, with defined attributes. A client id (`cid`)
// is automatically generated and assigned for you.
Backbone.Model = function(attributes, options) {
var defaults;
attributes || (attributes = {});
if (defaults = this.defaults) {
if (_.isFunction(defaults)) defaults = defaults.call(this);
attributes = _.extend({}, defaults, attributes);
}
this.attributes = {};
this._escapedAttributes = {};
this.cid = _.uniqueId('c');
this.set(attributes, {silent : true});
this._changed = false;
this._previousAttributes = _.clone(this.attributes);
if (options && options.collection) this.collection = options.collection;
this.initialize(attributes, options);
};
// Attach all inheritable methods to the Model prototype.
_.extend(Backbone.Model.prototype, Backbone.Events, {
// A snapshot of the model's previous attributes, taken immediately
// after the last `"change"` event was fired.
_previousAttributes : null,
// Has the item been changed since the last `"change"` event?
_changed : false,
// 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() {
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.attributes[attr];
return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val);
},
// Returns `true` if the attribute contains a value that is not null
// or undefined.
has : function(attr) {
return this.attributes[attr] != null;
},
// Set a hash of model attributes on the object, firing `"change"` unless you
// choose to silence it.
set : function(attrs, options) {
// Extract attributes and options.
options || (options = {});
if (!attrs) return this;
if (attrs.attributes) attrs = attrs.attributes;
var now = this.attributes, escaped = this._escapedAttributes;
// Run validation.
if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
// Check for changes of `id`.
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
// We're about to start triggering change events.
var alreadyChanging = this._changing;
this._changing = true;
// Update attributes.
for (var attr in attrs) {
var val = attrs[attr];
if (!_.isEqual(now[attr], val)) {
now[attr] = val;
delete escaped[attr];
this._changed = true;
if (!options.silent) this.trigger('change:' + attr, this, val, options);
}
}
// Fire the `"change"` event, if the model has been changed.
if (!alreadyChanging && !options.silent && this._changed) this.change(options);
this._changing = false;
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) {
if (!(attr in this.attributes)) return this;
options || (options = {});
var value = this.attributes[attr];
// Run validation.
var validObj = {};
validObj[attr] = void 0;
if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
// Remove the attribute.
delete this.attributes[attr];
delete this._escapedAttributes[attr];
if (attr == this.idAttribute) delete this.id;
this._changed = true;
if (!options.silent) {
this.trigger('change:' + attr, this, void 0, options);
this.change(options);
}
return this;
},
// Clear all attributes on the model, firing `"change"` unless you choose
// to silence it.
clear : function(options) {
options || (options = {});
var attr;
var old = this.attributes;
// Run validation.
var validObj = {};
for (attr in old) validObj[attr] = void 0;
if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
this.attributes = {};
this._escapedAttributes = {};
this._changed = true;
if (!options.silent) {
for (attr in old) {
this.trigger('change:' + attr, this, void 0, options);
}
this.change(options);
}
return this;
},
// 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 = {});
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 = 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(attrs, options) {
options || (options = {});
if (attrs && !this.set(attrs, options)) return false;
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, xhr);
};
options.error = wrapError(options.error, model, options);
var method = this.isNew() ? 'create' : 'update';
return (this.sync || Backbone.sync).call(this, method, this, options);
},
// Destroy this model on the server if it was already persisted. Upon success, the model is removed
// from its collection, if it has one.
destroy : function(options) {
options || (options = {});
if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
var model = this;
var success = options.success;
options.success = function(resp) {
model.trigger('destroy', model, model.collection, options);
if (success) success(model, resp);
};
options.error = wrapError(options.error, model, options);
return (this.sync || Backbone.sync).call(this, 'delete', this, options);
},
// 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 = getUrl(this.collection) || this.urlRoot || 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);
},
// 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.
// Calling this will cause all objects observing the model to update.
change : function(options) {
this.trigger('change', this, options);
this._previousAttributes = _.clone(this.attributes);
this._changed = false;
},
// 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 (attr) return this._previousAttributes[attr] != this.attributes[attr];
return this._changed;
},
// 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.
changedAttributes : function(now) {
now || (now = this.attributes);
var old = this._previousAttributes;
var changed = false;
for (var attr in now) {
if (!_.isEqual(old[attr], now[attr])) {
changed = changed || {};
changed[attr] = now[attr];
}
}
return changed;
},
// Get the previous value of an attribute, recorded at the time the last
// `"change"` event was fired.
previous : function(attr) {
if (!attr || !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);
},
// Run validation against a set of incoming attributes, returning `true`
// if all is well. If a specific `error` callback has been passed,
// call that instead of firing the general `"error"` event.
_performValidation : function(attrs, options) {
var error = this.validate(attrs);
if (error) {
if (options.error) {
options.error(this, error, options);
} else {
this.trigger('error', this, error, options);
}
return false;
}
return true;
}
});
// 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.
Backbone.Collection = function(models, options) {
options || (options = {});
if (options.comparator) this.comparator = options.comparator;
_.bindAll(this, '_onModelEvent', '_removeReference');
this._reset();
if (models) this.reset(models, {silent: true});
this.initialize.apply(this, arguments);
};
// Define the Collection's inheritable methods.
_.extend(Backbone.Collection.prototype, Backbone.Events, {
// The default model for a collection is just a **Backbone.Model**.
// This should be overridden in most cases.
model : Backbone.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() {
return this.map(function(model){ return model.toJSON(); });
},
// Add a model, or list of models to the set. Pass **silent** to avoid
// firing the `added` event for every new model.
add : function(models, options) {
if (_.isArray(models)) {
for (var i = 0, l = models.length; i < l; i++) {
this._add(models[i], options);
}
} else {
this._add(models, options);
}
return this;
},
// Remove a model, or a list of models from the set. Pass silent to avoid
// firing the `removed` event for every model removed.
remove : function(models, options) {
if (_.isArray(models)) {
for (var i = 0, l = models.length; i < l; i++) {
this._remove(models[i], options);
}
} else {
this._remove(models, options);
}
return this;
},
// Get a model from the set by id.
get : function(id) {
if (id == null) return null;
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];
},
// 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');
this.models = this.sortBy(this.comparator);
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 `added` or `removed` events. Fires `reset` when finished.
reset : function(models, options) {
models || (models = []);
options || (options = {});
this.each(this._removeReference);
this._reset();
this.add(models, {silent: true});
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 = {});
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 = 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. After the model
// has been created on the server, it will be added to the collection.
// Returns the model, or 'false' if validation on a new model fails.
create : function(model, options) {
var coll = this;
options || (options = {});
model = this._prepareModel(model, options);
if (!model) return false;
var success = options.success;
options.success = function(nextModel, resp, xhr) {
coll.add(nextModel, options);
if (success) success(nextModel, resp, xhr);
};
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 to be added to this collection
_prepareModel: function(model, options) {
if (!(model instanceof Backbone.Model)) {
var attrs = model;
model = new this.model(attrs, {collection: this});
if (model.validate && !model._performValidation(attrs, options)) model = false;
} else if (!model.collection) {
model.collection = this;
}
return model;
},
// Internal implementation of adding a single model to the set, updating
// hash indexes for `id` and `cid` lookups.
// Returns the model, or 'false' if validation on a new model fails.
_add : function(model, options) {
options || (options = {});
model = this._prepareModel(model, options);
if (!model) return false;
var already = this.getByCid(model);
if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
this._byId[model.id] = model;
this._byCid[model.cid] = model;
var index = options.at != null ? options.at :
this.comparator ? this.sortedIndex(model, this.comparator) :
this.length;
this.models.splice(index, 0, model);
model.bind('all', this._onModelEvent);
this.length++;
if (!options.silent) model.trigger('add', model, this, options);
return model;
},
// Internal implementation of removing a single model from the set, updating
// hash indexes for `id` and `cid` lookups.
_remove : function(model, options) {
options || (options = {});
model = this.getByCid(model) || this.get(model);
if (!model) return null;
delete this._byId[model.id];
delete this._byCid[model.cid];
this.models.splice(this.indexOf(model), 1);
this.length--;
if (!options.silent) model.trigger('remove', model, this, options);
this._removeReference(model);
return model;
},
// Internal method to remove a model's ties to a collection.
_removeReference : function(model) {
if (this == model.collection) {
delete model.collection;
}
model.unbind('all', this._onModelEvent);
},
// 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(ev, model, collection, options) {
if ((ev == 'add' || ev == 'remove') && collection != this) return;
if (ev == 'destroy') {
this._remove(model, options);
}
if (model && ev === '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', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'groupBy'];
// Mix in each Underscore method as a proxy to `Collection#models`.
_.each(methods, function(method) {
Backbone.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.
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\d]+)/g;
var splatParam = /\*([\w\d]+)/g;
var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;
// Set up all inheritable **Backbone.Router** properties and methods.
_.extend(Backbone.Router.prototype, Backbone.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 Backbone.History);
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
Backbone.history.route(route, _.bind(function(fragment) {
var args = this._extractParameters(route, fragment);
callback.apply(this, args);
this.trigger.apply(this, ['route:' + name].concat(args));
}, this));
},
// Simple proxy to `Backbone.history` to save a fragment into the history.
navigate : function(fragment, triggerRoute) {
Backbone.history.navigate(fragment, triggerRoute);
},
// 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.
Backbone.History = function() {
this.handlers = [];
_.bindAll(this, 'checkUrl');
};
// Cached regex for cleaning hashes.
var hashStrip = /^#*/;
// Cached regex for detecting MSIE.
var isExplorer = /msie [\w.]+/;
// Has the history handling already been started?
var historyStarted = false;
// Set up all inheritable **Backbone.History** properties and methods.
_.extend(Backbone.History.prototype, {
// The default interval to poll for hash changes, if necessary, is
// twenty times a second.
interval: 50,
// 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;
if (fragment.indexOf(this.options.root) == 0) fragment = fragment.substr(this.options.root.length);
} else {
fragment = window.location.hash;
}
}
return decodeURIComponent(fragment.replace(hashStrip, ''));
},
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start : function(options) {
// Figure out the initial configuration. Do we need an iframe?
// Is pushState desired ... is it available?
if (historyStarted) throw new Error("Backbone.history has already been started");
this.options = _.extend({}, {root: '/'}, this.options, options);
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 = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
this.navigate(fragment);
}
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if (this._hasPushState) {
$(window).bind('popstate', this.checkUrl);
} else if ('onhashchange' in window && !oldIE) {
$(window).bind('hashchange', this.checkUrl);
} else {
setInterval(this.checkUrl, this.interval);
}
// Determine if we need to change the base url, for a pushState link
// opened by a non-pushState browser.
this.fragment = fragment;
historyStarted = true;
var loc = window.location;
var atRoot = loc.pathname == this.options.root;
if (this._wantsPushState && !this._hasPushState && !atRoot) {
this.fragment = this.getFragment(null, true);
window.location.replace(this.options.root + '#' + this.fragment);
// Return immediately as browser will do redirect to new url
return true;
} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
this.fragment = loc.hash.replace(hashStrip, '');
window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
}
if (!this.options.silent) {
return this.loadUrl();
}
},
// Add a route to be tested when the fragment changes. Routes added later may
// override previous routes.
route : function(route, callback) {
this.handlers.unshift({route : route, callback : callback});
},
// Checks the current URL to see if it has changed, and if it has,
// calls `loadUrl`, normalizing across the hidden iframe.
checkUrl : function(e) {
var current = this.getFragment();
if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash);
if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false;
if (this.iframe) this.navigate(current);
this.loadUrl() || this.loadUrl(window.location.hash);
},
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
loadUrl : function(fragmentOverride) {
var fragment = this.fragment = this.getFragment(fragmentOverride);
var matched = _.any(this.handlers, function(handler) {
if (handler.route.test(fragment)) {
handler.callback(fragment);
return true;
}
});
return matched;
},
// Save a fragment into the hash history. You are responsible for properly
// URL-encoding the fragment in advance. This does not trigger
// a `hashchange` event.
navigate : function(fragment, triggerRoute) {
var frag = (fragment || '').replace(hashStrip, '');
if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return;
if (this._hasPushState) {
var loc = window.location;
if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
this.fragment = frag;
window.history.pushState({}, document.title, loc.protocol + '//' + loc.host + frag);
} else {
window.location.hash = this.fragment = frag;
if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) {
this.iframe.document.open().close();
this.iframe.location.hash = frag;
}
}
if (triggerRoute) this.loadUrl(fragment);
}
});
// Backbone.View
// -------------
// Creating a Backbone.View creates its initial element outside of the DOM,
// if an existing element is not provided...
Backbone.View = function(options) {
this.cid = _.uniqueId('view');
this._configure(options || {});
this._ensureElement();
this.delegateEvents();
this.initialize.apply(this, arguments);
};
// Element lookup, scoped to DOM elements within the current view.
// This should be prefered to global lookups, if you're dealing with
// a specific view.
var selectorDelegate = function(selector) {
return $(selector, this.el);
};
// Cached regex to split keys for `delegate`.
var eventSplitter = /^(\S+)\s*(.*)$/;
// List of view options to be merged as properties.
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
// Set up all inheritable **Backbone.View** properties and methods.
_.extend(Backbone.View.prototype, Backbone.Events, {
// The default `tagName` of a View's element is `"div"`.
tagName : 'div',
// Attach the `selectorDelegate` function as the `$` property.
$ : selectorDelegate,
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize : function(){},
// **render** is the core function that your view should override, in order
// to populate its element (`this.el`), with the appropriate HTML. The
// convention is for **render** to always return `this`.
render : function() {
return this;
},
// Remove this view from the DOM. Note that the view isn't present in the
// DOM by default, so calling this method may be a no-op.
remove : function() {
$(this.el).remove();
return this;
},
// For small amounts of DOM Elements, where a full-blown template isn't
// needed, use **make** to manufacture elements, one at a time.
//
// var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
//
make : function(tagName, attributes, content) {
var el = document.createElement(tagName);
if (attributes) $(el).attr(attributes);
if (content) $(el).html(content);
return el;
},
// Set callbacks, where `this.callbacks` is a hash of
//
// *{"event selector": "callback"}*
//
// {
// 'mousedown .title': 'edit',
// 'click .button': 'save'
// }
//
// pairs. Callbacks will be bound to the view, with `this` set properly.
// Uses event delegation for efficiency.
// Omitting the selector binds the event to `this.el`.
// This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents : function(events) {
if (!(events || (events = this.events))) return;
if (_.isFunction(events)) events = events.call(this);
$(this.el).unbind('.delegateEvents' + this.cid);
for (var key in events) {
var method = this[events[key]];
if (!method) throw new Error('Event "' + events[key] + '" does not exist');
var match = key.match(eventSplitter);
var eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.delegateEvents' + this.cid;
if (selector === '') {
$(this.el).bind(eventName, method);
} else {
$(this.el).delegate(selector, eventName, method);
}
}
},
// Performs the initial configuration of a View with a set of options.
// Keys with special meaning *(model, collection, id, className)*, are
// attached directly to the view.
_configure : function(options) {
if (this.options) options = _.extend({}, this.options, options);
for (var i = 0, l = viewOptions.length; i < l; i++) {
var attr = viewOptions[i];
if (options[attr]) this[attr] = options[attr];
}
this.options = options;
},
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
// an element from the `id`, `className` and `tagName` proeprties.
_ensureElement : function() {
if (!this.el) {
var attrs = this.attributes || {};
if (this.id) attrs.id = this.id;
if (this.className) attrs['class'] = this.className;
this.el = this.make(this.tagName, attrs);
} else if (_.isString(this.el)) {
this.el = $(this.el).get(0);
}
}
});
// The self-propagating extend function that Backbone classes use.
var extend = function (protoProps, classProps) {
var child = inherits(this, protoProps, classProps);
child.extend = this.extend;
return child;
};
// Set up inheritance for the model, collection, and view.
Backbone.Model.extend = Backbone.Collection.extend =
Backbone.Router.extend = Backbone.View.extend = extend;
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var methodMap = {
'create': 'POST',
'update': 'PUT',
'delete': 'DELETE',
'read' : 'GET'
};
// Backbone.sync
// -------------
// Override this function to change the manner in which Backbone persists
// models to the server. You will be passed the type of request, and the
// model in question. By default, uses makes a RESTful Ajax request
// to the model's `url()`. Some possible customizations could be:
//
// * Use `setTimeout` to batch rapid-fire updates into a single request.
// * Send up the models as XML instead of JSON.
// * Persist models via WebSockets instead of Ajax.
//
// Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
// as `POST`, with a `_method` parameter containing the true HTTP method,
// as well as all requests with the body as `application/x-www-form-urlencoded` instead of
// `application/json` with the model in a param named `model`.
// Useful when interfacing with server-side languages like **PHP** that make
// it difficult to read the body of `PUT` requests.
Backbone.sync = function(method, model, options) {
var type = methodMap[method];
// Default JSON-request options.
var params = _.extend({
type: type,
dataType: 'json'
}, options);
// Ensure that we have a URL.
if (!params.url) {
params.url = getUrl(model) || urlError();
}
// Ensure that we have the appropriate request data.
if (!params.data && model && (method == 'create' || method == 'update')) {
params.contentType = 'application/json';
params.data = JSON.stringify(model.toJSON());
}
// For older servers, emulate JSON by encoding the request into an HTML-form.
if (Backbone.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.data = params.data ? {model : params.data} : {};
}
// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (Backbone.emulateHTTP) {
if (type === 'PUT' || type === 'DELETE') {
if (Backbone.emulateJSON) params.data._method = type;
params.type = 'POST';
params.beforeSend = function(xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', type);
};
}
}
// Don't process data on a non-GET request.
if (params.type !== 'GET' && !Backbone.emulateJSON) {
params.processData = false;
}
// Make the request.
return $.ajax(params);
};
// Helpers
// -------
// Shared empty constructor function to aid in prototype-chain creation.
var ctor = function(){};
// Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
var inherits = function(parent, protoProps, staticProps) {
var child;
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call `super()`.
if (protoProps && protoProps.hasOwnProperty('constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}
// Inherit class (static) properties from parent.
_.extend(child, parent);
// Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function.
ctor.prototype = parent.prototype;
child.prototype = new ctor();
// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) _.extend(child.prototype, protoProps);
// Add static properties to the constructor function, if supplied.
if (staticProps) _.extend(child, staticProps);
// Correctly set child's `prototype.constructor`.
child.prototype.constructor = child;
// Set a convenience property in case the parent's prototype is needed later.
child.__super__ = parent.prototype;
return child;
};
// Helper function to get a URL from a Model or Collection as a property
// or as a function.
var getUrl = function(object) {
if (!(object && object.url)) return null;
return _.isFunction(object.url) ? object.url() : object.url;
};
// Throw an error when a URL is needed, and none is supplied.
var urlError = function() {
throw new Error('A "url" property or function must be specified');
};
// Wrap an optional error callback with a fallback error event.
var wrapError = function(onError, model, options) {
return function(resp) {
if (onError) {
onError(model, resp, options);
} else {
model.trigger('error', model, resp, options);
}
};
};
// Helper function to escape a string for HTML rendering.
var escapeHTML = function(string) {
return string.replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
};
}).call(this);
/*
knockback.js 0.12.0
(c) 2011 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.
*/if (!this.ko) {
throw new Error('Knockback: Dependency alert! Knockout.js must be included before this file');
}
if (!this.Backbone) {
throw new Error('Knockback: Dependency alert! Backbone.js must be included before this file');
}
if (!this._ || !this._.VERSION) {
throw new Error('Knockback: Dependency alert! Underscore.js must be included before this file');
}
this.Knockback || (this.Knockback = {});
this.kb || (this.kb = this.Knockback);
Knockback.VERSION = '0.12.0';
Knockback.locale_manager;
Knockback.wrappedObservable = function(instance) {
if (!instance._kb_observable) {
throw new Error('Knockback: _kb_observable missing from your instance');
}
return instance._kb_observable;
};
Knockback.setToDefault = function(observable) {
if (observable && observable.setToDefault) {
return observable.setToDefault();
}
};
Knockback.vmSetToDefault = function(view_model) {
var key, observable, _results;
_results = [];
for (key in view_model) {
observable = view_model[key];
_results.push(kb.setToDefault(observable));
}
return _results;
};
Knockback.vmRelease = function(view_model) {
if (view_model instanceof kb.ViewModel_RCBase) {
view_model.release();
return;
}
return Knockback.vmReleaseObservables(view_model);
};
Knockback.vmReleaseObservables = function(view_model, keys) {
var key, value, _results;
_results = [];
for (key in view_model) {
value = view_model[key];
if (!value) {
continue;
}
if (!(ko.isObservable(value) || (value instanceof kb.Observables) || (value instanceof kb.ViewModel_RCBase))) {
continue;
}
if (keys && !_.contains(keys, key)) {
continue;
}
view_model[key] = null;
_results.push(kb.vmReleaseObservable(value));
}
return _results;
};
Knockback.vmReleaseObservable = function(observable) {
if (!(ko.isObservable(observable) || (observable instanceof kb.Observables) || (observable instanceof kb.ViewModel_RCBase))) {
return;
}
if (observable.destroy) {
return observable.destroy();
} else if (observable.dispose) {
return observable.dispose();
} else if (observable.release) {
return observable.release();
}
};
/*
knockback_collection_observable.js
(c) 2011 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
*/
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.CollectionObservable = (function() {
function CollectionObservable(collection, vm_observable_array, options) {
var defer, event, _i, _j, _len, _len2, _ref, _ref2;
this.vm_observable_array = vm_observable_array;
if (options == null) {
options = {};
}
if (!collection) {
throw new Error('CollectionObservable: collection is missing');
}
if (options.hasOwnProperty('view_model')) {
options.view_model_constructor = options['view_model'];
delete options['view_model'];
}
if (this.vm_observable_array || options.view_model_create || options.view_model_constructor) {
if (!this.vm_observable_array) {
throw new Error('CollectionObservable: vm_observable_array is missing');
}
}
if (options.hasOwnProperty('defer')) {
defer = options.defer;
delete options['defer'];
}
this.options = _.clone(options);
_.bindAll(this, 'destroy', 'collection', 'sortedIndex', 'sortAttribute', 'viewModelByModel', 'eachViewModel', 'bind', 'unbind', 'trigger');
_.bindAll(this, '_onGetValue', '_onCollectionReset', '_onCollectionResort', '_onModelAdd', '_onModelRemove', '_onModelChanged');
this._kb_collection = collection;
if (this._kb_collection.retain) {
this._kb_collection.retain();
}
this._kb_collection.bind('reset', this._onCollectionReset);
if (!this.options.sorted_index) {
this._kb_collection.bind('resort', this._onCollectionResort);
}
_ref = ['new', 'add'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
event = _ref[_i];
this._kb_collection.bind(event, this._onModelAdd);
}
_ref2 = ['remove', 'destroy'];
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
event = _ref2[_j];
this._kb_collection.bind(event, this._onModelRemove);
}
this._kb_collection.bind('change', this._onModelChanged);
this._kb_value_observable = ko.observableArray([]);
this._kb_observable = ko.dependentObservable(this._onGetValue);
this._kb_observable.destroy = this.destroy;
this._kb_observable.collection = this.collection;
this._kb_observable.viewModelByModel = this.viewModelByModel;
this._kb_observable.eachViewModel = this.eachViewModel;
this._kb_observable.sortedIndex = this.sortedIndex;
this._kb_observable.sortAttribute = this.sortAttribute;
this._kb_observable.bind = this.bind;
this._kb_observable.unbind = this.unbind;
this._kb_observable.trigger = this.trigger;
this.sortedIndex(this.options.sorted_index, this.options.sort_attribute, {
silent: true,
defer: defer
});
return kb.wrappedObservable(this);
}
CollectionObservable.prototype.destroy = function() {
var event, _i, _j, _len, _len2, _ref, _ref2;
this._clearViewModels();
this._kb_collection.unbind('reset', this._onCollectionReset);
if (!this.options.sorted_index) {
this._kb_collection.unbind('resort', this._onCollectionResort);
}
_ref = ['new', 'add'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
event = _ref[_i];
this._kb_collection.unbind(event, this._onModelAdd);
}
_ref2 = ['remove', 'destroy'];
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
event = _ref2[_j];
this._kb_collection.unbind(event, this._onModelRemove);
}
this._kb_collection.unbind('change', this._onModelChanged);
if (this._kb_collection.release) {
this._kb_collection.release();
}
this._kb_collection = null;
this._kb_value_observable = null;
this._kb_observable.dispose();
this._kb_observable = null;
return this.options = null;
};
CollectionObservable.prototype.collection = function() {
this._kb_value_observable();
return this._kb_collection;
};
CollectionObservable.prototype.sortedIndex = function(sorted_index, sort_attribute, options) {
var _resync;
if (options == null) {
options = {};
}
if (sorted_index) {
this.options.sorted_index = sorted_index;
this.options.sort_attribute = sort_attribute;
} else if (sort_attribute) {
this.options.sort_attribute = sort_attribute;
this.options.sorted_index = this._sortAttributeFn(sort_attribute);
} else {
this.options.sort_attribute = null;
this.options.sorted_index = null;
}
_resync = __bind(function() {
if ((this._kb_collection.models.length === 0) && (this._kb_value_observable().length === 0)) {
return;
}
this._collectionResync(true);
if (!options.silent) {
return this.trigger('resort', this.vm_observable_array());
}
}, this);
if (options.defer) {
_.defer(_resync);
} else {
_resync();
}
return this;
};
CollectionObservable.prototype.sortAttribute = function(sort_attribute, sorted_index, silent) {
return this.sortedIndex(sorted_index, sort_attribute, silent);
};
CollectionObservable.prototype._sortAttributeFn = function(sort_attribute) {
return function(models, model) {
return _.sortedIndex(models, model, function(test) {
return test.get(sort_attribute);
});
};
};
CollectionObservable.prototype.viewModelByModel = function(model) {
var id_attribute;
if (!this.vm_observable_array) {
throw new Error("CollectionObservable: cannot get a view model if vm_observable_array was not supplied");
}
id_attribute = model.hasOwnProperty(model.idAttribute) ? model.idAttribute : 'cid';
return _.find(this.vm_observable_array(), function(test) {
return test.__kb_model[id_attribute] === model[id_attribute];
});
};
CollectionObservable.prototype.eachViewModel = function(iterator) {
var view_model, _i, _len, _ref, _results;
_ref = this.vm_observable_array();
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
view_model = _ref[_i];
_results.push(iterator(view_model));
}
return _results;
};
CollectionObservable.prototype._onGetValue = function() {
return this._kb_value_observable();
};
CollectionObservable.prototype._onCollectionReset = function() {
return this._collectionResync();
};
CollectionObservable.prototype._onCollectionResort = function(model_or_models) {
if (this.options.sorted_index) {
throw new Error("CollectionObservable: collection sorted_index unexpected");
}
if (_.isArray(model_or_models)) {
this._collectionResync(true);
return this.trigger('resort', this.vm_observable_array());
} else {
return this._onModelResort(model_or_models);
}
};
CollectionObservable.prototype._onModelAdd = function(model) {
var add_index, sorted_models, view_model;
if (this.options.sorted_index) {
sorted_models = _.pluck(this.vm_observable_array(), '__kb_model');
add_index = this.options.sorted_index(sorted_models, model);
} else {
add_index = this._kb_collection.indexOf(model);
}
if (this.vm_observable_array) {
view_model = this._viewModelCreate(model);
this.vm_observable_array.splice(add_index, 0, view_model);
}
this._kb_value_observable.splice(add_index, 0, model);
if (this.vm_observable_array) {
return this.trigger('add', view_model, this.vm_observable_array());
}
};
CollectionObservable.prototype._onModelRemove = function(model) {
var view_model;
this._kb_value_observable.remove(model);
if (this.vm_observable_array) {
view_model = this.viewModelByModel(model);
if (!view_model) {
return;
}
this.vm_observable_array.remove(view_model);
this.trigger('remove', view_model, this.vm_observable_array());
kb.vmRelease(view_model);
return view_model.__kb_model = null;
}
};
CollectionObservable.prototype._onModelChanged = function(model) {
if (this.options.sorted_index && (!this.options.sort_attribute || model.hasChanged(this.options.sort_attribute))) {
this._onModelResort(model);
}
return this._kb_value_observable.valueHasMutated();
};
CollectionObservable.prototype._onModelResort = function(model) {
var new_index, previous_index, sorted_models, view_model;
previous_index = this._kb_value_observable.indexOf(model);
if (this.options.sorted_index) {
sorted_models = _.clone(this._kb_value_observable());
sorted_models.splice(previous_index, 1);
new_index = this.options.sorted_index(sorted_models, model);
} else {
new_index = this._kb_collection.indexOf(model);
}
if (previous_index === new_index) {
return;
}
if (this.vm_observable_array) {
view_model = this.viewModelByModel(model);
this.vm_observable_array.splice(previous_index, 1);
this.vm_observable_array.splice(new_index, 0, view_model);
}
this._kb_value_observable.splice(previous_index, 1);
this._kb_value_observable.splice(new_index, 0, model);
if (this.vm_observable_array) {
return this.trigger('resort', view_model, this.vm_observable_array(), new_index);
}
};
CollectionObservable.prototype._clearViewModels = function(silent) {
var view_model, view_models, _i, _len, _results;
if (this.vm_observable_array) {
if (!silent) {
this.trigger('remove', this.vm_observable_array());
}
view_models = this.vm_observable_array.removeAll();
_results = [];
for (_i = 0, _len = view_models.length; _i < _len; _i++) {
view_model = view_models[_i];
_results.push(kb.vmRelease(view_model));
}
return _results;
}
};
CollectionObservable.prototype._collectionResync = function(silent) {
var add_index, model, models, view_models, _i, _j, _len, _len2, _ref;
this._clearViewModels(silent);
this._kb_value_observable.removeAll();
if (this.options.sorted_index) {
models = [];
_ref = this._kb_collection.models;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
model = _ref[_i];
add_index = this.options.sorted_index(models, model);
models.splice(add_index, 0, model);
}
} else {
models = _.clone(this._kb_collection.models);
}
if (this.vm_observable_array) {
view_models = [];
for (_j = 0, _len2 = models.length; _j < _len2; _j++) {
model = models[_j];
view_models.push(this._viewModelCreate(model));
}
this.vm_observable_array(view_models);
}
this._kb_value_observable(models);
if (this.vm_observable_array) {
if (!silent) {
return this.trigger('add', this.vm_observable_array());
}
}
};
CollectionObservable.prototype._viewModelCreate = function(model) {
var view_model;
if (this.options.view_model_create) {
view_model = this.options.view_model_create(model);
} else if (this.options.view_model_constructor) {
view_model = new this.options.view_model_constructor(model);
} else {
view_model = kb.viewModel(model);
}
view_model.__kb_model = model;
return view_model;
};
return CollectionObservable;
})();
__extends(Knockback.CollectionObservable.prototype, Backbone.Events);
Knockback.collectionObservable = function(collection, vm_observable_array, options) {
return new Knockback.CollectionObservable(collection, vm_observable_array, options);
};
Knockback.viewModelGetModel = Knockback.vmModel = function(view_model) {
return view_model.__kb_model;
};
Knockback.sortedIndexWrapAttr = Knockback.siwa = function(attribute_name, wrapper_constructor) {
return function(models, model) {
return _.sortedIndex(models, model, function(test) {
return new wrapper_constructor(test.get(attribute_name));
});
};
};
/*
knockback_default_wrapper.js
(c) 2011 Kevin Malakoff.
Knockback.DefaultWrapper is freely distributable under the MIT license.
See the following for full license details:
https://github.com/kmalakoff/knockback/blob/master/LICENSE
*/
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.DefaultWrapper = (function() {
function DefaultWrapper(observable, default_value) {
this.default_value = default_value;
_.bindAll(this, 'destroy', 'setToDefault');
this._kb_observable = ko.dependentObservable({
read: __bind(function() {
var value;
value = ko.utils.unwrapObservable(observable());
if (!value) {
return ko.utils.unwrapObservable(this.default_value);
} else {
return value;
}
}, this),
write: function(value) {
return observable(value);
},
owner: {}
});
this._kb_observable.destroy = this.destroy;
this._kb_observable.setToDefault = this.setToDefault;
return kb.wrappedObservable(this);
}
DefaultWrapper.prototype.destroy = function() {
this._kb_observable = null;
return this.default_value = null;
};
DefaultWrapper.prototype.setToDefault = function() {
return this._kb_observable(this.default_value);
};
return DefaultWrapper;
})();
Knockback.defaultWrapper = function(observable, default_value) {
return new Knockback.DefaultWrapper(observable, default_value);
};
/*
knockback_formatted_observable.js
(c) 2011 Kevin Malakoff.
Knockback.FormattedObservable is freely distributable under the MIT license.
See the following for full license details:
https://github.com/kmalakoff/knockback/blob/master/LICENSE
*/if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.toFormattedString = function(format) {
var arg, args, index, parameter_index, result, value;
result = format.slice();
args = Array.prototype.slice.call(arguments, 1);
for (index in args) {
arg = args[index];
value = ko.utils.unwrapObservable(arg);
if (!value) {
value = '';
}
parameter_index = format.indexOf("\{" + index + "\}");
while (parameter_index >= 0) {
result = result.replace("{" + index + "}", value);
parameter_index = format.indexOf("\{" + index + "\}", parameter_index + 1);
}
}
return result;
};
Knockback.parseFormattedString = function(string, format) {
var count, format_indices_to_matched_indices, index, match_index, matches, parameter_count, parameter_index, positions, regex, regex_string, results, sorted_positions, _i, _results;
regex_string = format.slice();
index = 0;
parameter_count = 0;
positions = {};
while (regex_string.search("\\{" + index + "\\}") >= 0) {
parameter_index = format.indexOf("\{" + index + "\}");
while (parameter_index >= 0) {
regex_string = regex_string.replace("\{" + index + "\}", '(.*)');
positions[parameter_index] = index;
parameter_count++;
parameter_index = format.indexOf("\{" + index + "\}", parameter_index + 1);
}
index++;
}
count = index;
regex = new RegExp(regex_string);
matches = regex.exec(string);
if (matches) {
matches.shift();
}
if (!matches || (matches.length !== parameter_count)) {
return _.map((function() {
_results = [];
for (var _i = 1; 1 <= count ? _i <= count : _i >= count; 1 <= count ? _i++ : _i--){ _results.push(_i); }
return _results;
}).apply(this), function() {
return '';
});
}
sorted_positions = _.sortBy(_.keys(positions), function(parameter_index, format_index) {
return parseInt(parameter_index, 10);
});
format_indices_to_matched_indices = {};
for (match_index in sorted_positions) {
parameter_index = sorted_positions[match_index];
index = positions[parameter_index];
if (format_indices_to_matched_indices.hasOwnProperty(index)) {
continue;
}
format_indices_to_matched_indices[index] = match_index;
}
results = [];
index = 0;
while (index < count) {
results.push(matches[format_indices_to_matched_indices[index]]);
index++;
}
return results;
};
Knockback.formattedObservable = function(format, args) {
var observable_args, result;
observable_args = Array.prototype.slice.call(arguments, 1);
result = ko.dependentObservable({
read: function() {
var arg, _i, _len;
args = [ko.utils.unwrapObservable(format)];
for (_i = 0, _len = observable_args.length; _i < _len; _i++) {
arg = observable_args[_i];
args.push(ko.utils.unwrapObservable(arg));
}
return kb.toFormattedString.apply(null, args);
},
write: function(value) {
var index, matches, max_count, _results;
matches = kb.parseFormattedString(value, ko.utils.unwrapObservable(format));
max_count = Math.min(observable_args.length, matches.length);
index = 0;
_results = [];
while (index < max_count) {
observable_args[index](matches[index]);
_results.push(index++);
}
return _results;
},
owner: {}
});
return result;
};
/*
knockback_localized_observable.js
(c) 2011 Kevin Malakoff.
Knockback.LocalizedObservable is freely distributable under the MIT license.
See the following for full license details:
https://github.com/kmalakoff/knockback/blob/master/LICENSE
*/if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.LocalizedObservable = (function() {
function LocalizedObservable(value, options, view_model) {
this.value = value;
this.options = options != null ? options : {};
this.view_model = view_model;
if (!(this.options.read || this.read)) {
throw new Error('LocalizedObservable: options.read is missing');
}
if (this.options.read && this.read) {
throw new Error('LocalizedObservable: options.read and read class function exist. You need to choose one.');
}
if (this.options.write && this.write) {
throw new Error('LocalizedObservable: options.write and write class function exist. You need to choose one.');
}
if (!kb.locale_manager) {
throw new Error('LocalizedObservable: Knockback.locale_manager is not defined');
}
_.bindAll(this, 'destroy', 'setToDefault', 'resetToCurrent', 'observedValue', '_onGetValue', '_onSetValue', '_onLocaleChange');
this._kb_read = this.options.read ? this.options.read : this.read;
this._kb_write = this.options.write ? this.options.write : this.write;
this._kb_default = this.options["default"] ? this.options["default"] : this["default"];
if (this.value) {
value = ko.utils.unwrapObservable(this.value);
}
this._kb_value_observable = ko.observable(!value ? this._getDefaultValue() : this._kb_read.call(this, value, null));
if (this._kb_write) {
if (!this.view_model) {
this.view_model = {};
}
if (!_.isFunction(this._kb_write)) {
throw new Error('LocalizedObservable: options.write is not a function for read_write model attribute');
}
this._kb_observable = ko.dependentObservable({
read: this._onGetValue,
write: this._onSetValue,
owner: this.view_model
});
} else {
this._kb_observable = ko.dependentObservable(this._onGetValue);
}
this._kb_observable.destroy = this.destroy;
this._kb_observable.observedValue = this.observedValue;
this._kb_observable.setToDefault = this.setToDefault;
this._kb_observable.resetToCurrent = this.resetToCurrent;
kb.locale_manager.bind('change', this._onLocaleChange);
return kb.wrappedObservable(this);
}
LocalizedObservable.prototype.destroy = function() {
kb.locale_manager.unbind('change', this._onLocaleChange);
this._kb_value_observable = null;
this._kb_observable.dispose();
this._kb_observable = null;
this.options = {};
return this.view_model = null;
};
LocalizedObservable.prototype.setToDefault = function() {
var current_value, default_value;
if (!this._kb_default) {
return;
}
default_value = this._getDefaultValue();
current_value = this._kb_value_observable();
if (current_value !== default_value) {
return this._onSetValue(default_value);
} else {
return this._kb_value_observable.valueHasMutated();
}
};
LocalizedObservable.prototype.resetToCurrent = function() {
this._kb_value_observable(null);
return this._onSetValue(this._getCurrentValue());
};
LocalizedObservable.prototype.observedValue = function(value) {
if (arguments.length === 0) {
return this.value;
}
this.value = value;
this._onLocaleChange();
return this;
};
LocalizedObservable.prototype._getDefaultValue = function() {
if (!this._kb_default) {
return '';
}
if (_.isFunction(this._kb_default)) {
return this._kb_default();
} else {
return this._kb_default;
}
};
LocalizedObservable.prototype._getCurrentValue = function() {
if (!(this.value && this._kb_observable)) {
return this._getDefaultValue();
}
return this._kb_read.call(this, ko.utils.unwrapObservable(this.value), this._kb_observable);
};
LocalizedObservable.prototype._onGetValue = function() {
if (this.value) {
ko.utils.unwrapObservable(this.value);
}
return this._kb_value_observable();
};
LocalizedObservable.prototype._onSetValue = function(value) {
this._kb_write.call(this, value, ko.utils.unwrapObservable(this.value), this._kb_observable);
value = this._kb_read.call(this, ko.utils.unwrapObservable(this.value), this._kb_observable);
this._kb_value_observable(value);
if (this.options.onChange) {
return this.options.onChange(value);
}
};
LocalizedObservable.prototype._onLocaleChange = function() {
var value;
value = this._kb_read.call(this, ko.utils.unwrapObservable(this.value), this._kb_observable);
this._kb_value_observable(value);
if (this.options.onChange) {
return this.options.onChange(value);
}
};
return LocalizedObservable;
})();
Knockback.localizedObservable = function(value, options, view_model) {
return new Knockback.LocalizedObservable(value, options, view_model);
};
/*
knockback_observable.js
(c) 2011 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
*/if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.Observable = (function() {
function Observable(model, options, view_model) {
this.model = model;
this.options = options;
this.view_model = view_model;
if (!this.model) {
throw new Error('Observable: model is missing');
}
if (!this.options) {
throw new Error('Observable: options is missing');
}
if (_.isString(this.options) || ko.isObservable(this.options)) {
this.options = {
key: this.options
};
}
if (!this.options.key) {
throw new Error('Observable: options.key is missing');
}
_.bindAll(this, 'destroy', 'setToDefault', '_onGetValue', '_onSetValue', '_onModelChange', '_onModelLoaded', '_onModelUnloaded');
if (Backbone.ModelRef && (this.model instanceof Backbone.ModelRef)) {
this.model_ref = this.model;
this.model_ref.retain();
this.model_ref.bind('loaded', this._onModelLoaded);
this.model_ref.bind('unloaded', this._onModelUnloaded);
this.model = this.model_ref.getModel();
}
this._kb_value_observable = ko.observable();
if (this.options.localizer) {
this._kb_localizer = new this.options.localizer(this._getCurrentValue());
}
if (this.options.write) {
if (!this.view_model) {
throw new Error('Observable: view_model is missing for read_write model attribute');
}
this._kb_observable = ko.dependentObservable({
read: this._onGetValue,
write: this._onSetValue,
owner: this.view_model
});
} else {
this._kb_observable = ko.dependentObservable(this._onGetValue);
}
this._kb_observable.destroy = this.destroy;
this._kb_observable.setToDefault = this.setToDefault;
if (!this.model_ref || this.model_ref.isLoaded()) {
this.model.bind('change', this._onModelChange);
}
return kb.wrappedObservable(this);
}
Observable.prototype.destroy = function() {
this._kb_value_observable = null;
this._kb_observable.dispose();
this._kb_observable = null;
if (this.model) {
this._onModelUnloaded(this.model);
}
if (this.model_ref) {
this.model_ref.unbind('loaded', this._onModelLoaded);
this.model_ref.unbind('unloaded', this._onModelUnloaded);
this.model_ref.release();
this.model_ref = null;
}
this.options = null;
return this.view_model = null;
};
Observable.prototype.setToDefault = function() {
var value;
value = this._getDefaultValue();
if (this._kb_localizer) {
this._kb_localizer.observedValue(value);
value = this._kb_localizer();
}
return this._kb_value_observable(value);
};
Observable.prototype._getDefaultValue = function() {
if (!this.options.hasOwnProperty('default')) {
return '';
}
if (_.isFunction(this.options["default"])) {
return this.options["default"]();
} else {
return this.options["default"];
}
};
Observable.prototype._getCurrentValue = function() {
var arg, args, key, _i, _len, _ref;
if (!this.model) {
return this._getDefaultValue();
}
key = ko.utils.unwrapObservable(this.options.key);
args = [key];
if (!_.isUndefined(this.options.args)) {
if (_.isArray(this.options.args)) {
_ref = this.options.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.options.args));
}
}
if (this.options.read) {
return this.options.read.apply(this.view_model, args);
} else {
return this.model.get.apply(this.model, args);
}
};
Observable.prototype._onGetValue = function() {
var arg, value, _i, _len, _ref;
this._kb_value_observable();
ko.utils.unwrapObservable(this.options.key);
if (!_.isUndefined(this.options.args)) {
if (_.isArray(this.options.args)) {
_ref = this.options.args;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
arg = _ref[_i];
ko.utils.unwrapObservable(arg);
}
} else {
ko.utils.unwrapObservable(this.options.args);
}
}
value = this._getCurrentValue();
if (this._kb_localizer) {
this._kb_localizer.observedValue(value);
value = this._kb_localizer();
}
return value;
};
Observable.prototype._onSetValue = function(value) {
var arg, args, set_info, _i, _len, _ref;
if (this._kb_localizer) {
this._kb_localizer(value);
value = this._kb_localizer.observedValue();
}
if (this.model) {
set_info = {};
set_info[ko.utils.unwrapObservable(this.options.key)] = value;
args = _.isFunction(this.options.write) ? [value] : [set_info];
if (!_.isUndefined(this.options.args)) {
if (_.isArray(this.options.args)) {
_ref = this.options.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.options.args));
}
}
if (_.isFunction(this.options.write)) {
this.options.write.apply(this.view_model, args);
} else {
this.model.set.apply(this.model, args);
}
}
if (this._kb_localizer) {
return this._kb_value_observable(this._kb_localizer());
} else {
return this._kb_value_observable(value);
}
};
Observable.prototype._onModelLoaded = function(model) {
this.model = model;
this.model.bind('change', this._onModelChange);
return this._updateValue();
};
Observable.prototype._onModelUnloaded = function(model) {
if (this._kb_localizer && this._kb_localizer.destroy) {
this._kb_localizer.destroy();
this._kb_localizer = null;
}
this.model.unbind('change', this._onModelChange);
return this.model = null;
};
Observable.prototype._onModelChange = function() {
if ((this.model && this.model.hasChanged) && !this.model.hasChanged(ko.utils.unwrapObservable(this.options.key))) {
return;
}
return this._updateValue();
};
Observable.prototype._updateValue = function() {
var value;
value = this._getCurrentValue();
if (this._kb_localizer) {
this._kb_localizer.observedValue(value);
value = this._kb_localizer();
}
return this._kb_value_observable(value);
};
return Observable;
})();
Knockback.observable = function(model, options, view_model) {
return new Knockback.Observable(model, options, view_model);
};
/*
knockback_observables.js
(c) 2011 Kevin Malakoff.
Knockback.Observables is freely distributable under the MIT license.
See the following for full license details:
https://github.com/kmalakoff/knockback/blob/master/LICENSE
*/if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.Observables = (function() {
function Observables(model, mappings_info, view_model, options_or_writeable) {
var is_string, mapping_info, view_model_property_name, write, _ref, _ref2;
this.model = model;
this.mappings_info = mappings_info;
this.view_model = view_model;
if (!this.model) {
throw new Error('Observables: model is missing');
}
if (!this.mappings_info) {
throw new Error('Observables: mappings_info is missing');
}
if (!!options_or_writeable && ((_.isBoolean(options_or_writeable) && options_or_writeable) || !!options_or_writeable.write)) {
write = _.isBoolean(options_or_writeable) ? options_or_writeable : !!options_or_writeable.write;
_ref = this.mappings_info;
for (view_model_property_name in _ref) {
mapping_info = _ref[view_model_property_name];
is_string = _.isString(mapping_info);
if (is_string || !mapping_info.hasOwnProperty(write)) {
mapping_info = is_string ? {
key: mapping_info,
write: write
} : _.extend({
write: write
}, mapping_info);
}
this.view_model[view_model_property_name] = kb.observable(this.model, mapping_info, this.view_model);
}
} else {
_ref2 = this.mappings_info;
for (view_model_property_name in _ref2) {
mapping_info = _ref2[view_model_property_name];
this.view_model[view_model_property_name] = kb.observable(this.model, mapping_info, this.view_model);
}
}
}
Observables.prototype.destroy = function() {
var mapping_info, view_model_property_name, _ref;
_ref = this.mappings_info;
for (view_model_property_name in _ref) {
mapping_info = _ref[view_model_property_name];
if (this.view_model[view_model_property_name]) {
this.view_model[view_model_property_name].destroy();
}
this.view_model[view_model_property_name] = null;
}
this.view_model = null;
this.mappings_info = null;
return this.model = null;
};
Observables.prototype.setToDefault = function() {
var mapping_info, view_model_property_name, _ref, _results;
_ref = this.mappings_info;
_results = [];
for (view_model_property_name in _ref) {
mapping_info = _ref[view_model_property_name];
_results.push(this.view_model[view_model_property_name].setToDefault());
}
return _results;
};
return Observables;
})();
Knockback.observables = function(model, mappings_info, view_model, options) {
return new Knockback.Observables(model, mappings_info, view_model, options);
};
/*
knockback_triggered_observable.js
(c) 2011 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
*/if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
Knockback.TriggeredObservable = (function() {
function TriggeredObservable(model, event_name) {
this.model = model;
this.event_name = event_name;
if (!this.model) {
throw new Error('Observable: model is missing');
}
if (!this.event_name) {
throw new Error('Observable: event_name is missing');
}
_.bindAll(this, 'destroy', '_onGetValue', '_onValueChange', '_onModelLoaded', '_onModelUnloaded');
if (Backbone.ModelRef && (this.model instanceof Backbone.ModelRef)) {
this.model_ref = this.model;
this.model_ref.retain();
this.model_ref.bind('loaded', this._onModelLoaded);
this.model_ref.bind('unloaded', this._onModelUnloaded);
this.model = this.model_ref.getModel();
}
this._kb_value_observable = ko.observable();
this._kb_observable = ko.dependentObservable(this._onGetValue);
this._kb_observable.destroy = this.destroy;
if (!this.model_ref || this.model_ref.isLoaded()) {
this._onModelLoaded(this.model);
}
return kb.wrappedObservable(this);
}
TriggeredObservable.prototype.destroy = function() {
this._kb_observable.dispose();
this._kb_observable = null;
this._kb_value_observable = null;
if (this.model) {
this._onModelUnloaded(this.model);
}
if (this.model_ref) {
this.model_ref.unbind('loaded', this._onModelLoaded);
this.model_ref.unbind('unloaded', this._onModelUnloaded);
this.model_ref.release();
this.model_ref = null;
}
this.options = null;
return this.view_model = null;
};
TriggeredObservable.prototype._onGetValue = function() {
return this._kb_value_observable();
};
TriggeredObservable.prototype._onModelLoaded = function(model) {
this.model = model;
this.model.bind(this.event_name, this._onValueChange);
return this._onValueChange();
};
TriggeredObservable.prototype._onModelUnloaded = function() {
if (this._kb_localizer && this._kb_localizer.destroy) {
this._kb_localizer.destroy();
this._kb_localizer = null;
}
this.model.unbind(this.event_name, this._onValueChange);
return this.model = null;
};
TriggeredObservable.prototype._onValueChange = function() {
var current_value;
current_value = this._kb_value_observable();
if (current_value !== this.model) {
return this._kb_value_observable(this.model);
} else {
return this._kb_value_observable.valueHasMutated();
}
};
return TriggeredObservable;
})();
Knockback.triggeredObservable = function(model, event_name) {
return new Knockback.TriggeredObservable(model, event_name);
};
/*
knockback_view_model.js
(c) 2011 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
*/
var AttributeConnector;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
if (!this.Knockback) {
throw new Error('Knockback: Dependency alert! knockback_core.js must be included before this file');
}
AttributeConnector = (function() {
function AttributeConnector(model, key, read_only) {
this.key = key;
this.read_only = read_only;
_.bindAll(this, 'destroy', 'setModel');
this._kb_observable = ko.observable();
this._kb_observable.subscription = this._kb_observable.subscribe(__bind(function(value) {
var set_info;
if (this.read_only) {
if (this.model) {
value = this.model.get(this.key);
if (this._kb_observable() === value) {
return;
}
this._kb_observable(value);
}
throw "Cannot write a value to a dependentObservable unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.";
} else if (this.model) {
set_info = {};
set_info[this.key] = value;
return this.model.set(set_info);
}
}, this));
this._kb_observable.destroy = this.destroy;
this._kb_observable.setModel = this.setModel;
if (model) {
this.setModel(model);
}
return kb.wrappedObservable(this);
}
AttributeConnector.prototype.destroy = function() {
this.model = null;
return this._kb_observable = null;
};
AttributeConnector.prototype.setModel = function(model) {
if (model) {
this.model = model;
return this._kb_observable(this.model.get(this.key));
} else {
return this.model = null;
}
};
return AttributeConnector;
})();
Knockback.ViewModel_RCBase = (function() {
function ViewModel_RCBase() {
this.ref_count = 1;
}
ViewModel_RCBase.prototype._destroy = function() {
return kb.vmReleaseObservables(this);
};
ViewModel_RCBase.prototype.retain = function() {
if (this.ref_count <= 0) {
throw new Error("ViewModel: ref_count is corrupt: " + this.ref_count);
}
this.ref_count++;
return this;
};
ViewModel_RCBase.prototype.release = function() {
if (this.ref_count <= 0) {
throw new Error("ViewModel: ref_count is corrupt: " + this.ref_count);
}
this.ref_count--;
if (!this.ref_count) {
this._destroy();
}
return this;
};
ViewModel_RCBase.prototype.refCount = function() {
return this.ref_count;
};
return ViewModel_RCBase;
})();
Knockback.ViewModel = (function() {
__extends(ViewModel, kb.ViewModel_RCBase);
function ViewModel(model, options, view_model) {
var key, missing, _i, _len;
if (options == null) {
options = {};
}
this._kb_vm = {};
this._kb_vm.model = model;
this._kb_vm.options = options;
this._kb_vm.view_model = view_model;
if (!this._kb_vm.model) {
throw new Error('ViewModel: model is missing');
}
_.bindAll(this, '_kb_vm_onModelChange', '_kb_vm_onModelLoaded', '_kb_vm_onModelUnloaded');
if (!this._kb_vm.view_model) {
this._kb_vm.view_model = this;
} else {
this._kb_vm.observables = [];
}
if (Backbone.ModelRef && (this._kb_vm.model instanceof Backbone.ModelRef)) {
this._kb_vm.model_ref = this._kb_vm.model;
this._kb_vm.model_ref.retain();
this._kb_vm.model_ref.bind('loaded', this._kb_vm_onModelLoaded);
this._kb_vm.model_ref.bind('unloaded', this._kb_vm_onModelUnloaded);
this._kb_vm.model = this._kb_vm.model_ref.getModel();
}
if (!this._kb_vm.model_ref || this._kb_vm.model_ref.isLoaded()) {
this._kb_vm_onModelLoaded(this._kb_vm.model);
}
if (!this._kb_vm.options.internals && !this._kb_vm.options.requires) {
return this;
}
missing = _.union((this._kb_vm.options.internals ? this._kb_vm.options.internals : []), (this._kb_vm.options.requires ? this._kb_vm.options.requires : []));
if (!this._kb_vm.model_ref || this._kb_vm.model_ref.isLoaded()) {
missing = _.difference(missing, _.keys(this._kb_vm.model.attributes));
}
for (_i = 0, _len = missing.length; _i < _len; _i++) {
key = missing[_i];
this._updateAttributeObservor(this._kb_vm.model, key);
}
}
ViewModel.prototype._destroy = function() {
var view_model;
if (this._kb_vm.model) {
this._kb_vm.model.unbind('change', this._kb_vm_onModelChange);
this._kb_vm.model = null;
}
view_model = this._kb_vm.view_model;
this._kb_vm.view_model = null;
kb.vmReleaseObservables(view_model, this._kb_vm.observables);
if (this._kb_vm.observables) {
return this._kb_vm.observables = null;
}
};
ViewModel.prototype._kb_vm_onModelLoaded = function(model) {
var key, _results;
this._kb_vm.model = model;
this._kb_vm.model.bind('change', this._kb_vm_onModelChange);
_results = [];
for (key in this._kb_vm.model.attributes) {
_results.push(this._updateAttributeObservor(this._kb_vm.model, key));
}
return _results;
};
ViewModel.prototype._kb_vm_onModelUnloaded = function(model) {
var key, _results;
this._kb_vm.model.unbind('change', this._kb_vm_onModelChange);
model = this._kb_vm.model;
this._kb_vm.model = null;
_results = [];
for (key in model.attributes) {
_results.push(this._updateAttributeObservor(this._kb_vm.model, key));
}
return _results;
};
ViewModel.prototype._kb_vm_onModelChange = function() {
var key, _results;
if (!this._kb_vm.model._changed) {
return;
}
_results = [];
for (key in this._kb_vm.model.attributes) {
_results.push((this._kb_vm.model.hasChanged(key) ? this._updateAttributeObservor(this._kb_vm.model, key) : void 0));
}
return _results;
};
ViewModel.prototype._updateAttributeObservor = function(model, key) {
var vm_key;
vm_key = this._kb_vm.options.internals && _.contains(this._kb_vm.options.internals, key) ? '_' + key : key;
if (this._kb_vm.view_model.hasOwnProperty(vm_key)) {
if (this._kb_vm.view_model[vm_key]) {
return this._kb_vm.view_model[vm_key].setModel(model);
}
} else {
if (this._kb_vm.observables) {
this._kb_vm.observables.push(vm_key);
}
return this._kb_vm.view_model[vm_key] = new AttributeConnector(model, key, this._kb_vm.options.read_only);
}
};
return ViewModel;
})();
Knockback.viewModel = function(model, options, view_model) {
return new Knockback.ViewModel(model, options, view_model);
};
\ No newline at end of file
// Knockout JavaScript library v2.0.0
// (c) Steven Sanderson - http://knockoutjs.com/
// License: MIT (http://www.opensource.org/licenses/mit-license.php)
(function(window,undefined){
function c(a){throw a;}var l=void 0,m=!0,o=null,p=!1,r=window.ko={};r.b=function(a,b){for(var d=a.split("."),e=window,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=b};r.l=function(a,b,d){a[b]=d};
r.a=new function(){function a(a,e){if("INPUT"!=a.tagName||!a.type)return p;if("click"!=e.toLowerCase())return p;var b=a.type.toLowerCase();return"checkbox"==b||"radio"==b}var b=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,d={},e={};d[/Firefox\/2/i.test(navigator.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];d.MouseEvents="click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave".split(",");for(var f in d){var h=d[f];if(h.length)for(var g=0,i=h.length;g<i;g++)e[h[g]]=
f}var j=function(){for(var a=3,e=document.createElement("div"),b=e.getElementsByTagName("i");e.innerHTML="<\!--[if gt IE "+ ++a+"]><i></i><![endif]--\>",b[0];);return 4<a?a:l}();return{Ba:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],n:function(a,e){for(var b=0,f=a.length;b<f;b++)e(a[b])},k:function(a,e){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,e);for(var b=0,f=a.length;b<f;b++)if(a[b]===e)return b;return-1},Wa:function(a,e,b){for(var f=0,d=
a.length;f<d;f++)if(e.call(b,a[f]))return a[f];return o},ca:function(a,e){var b=r.a.k(a,e);0<=b&&a.splice(b,1)},ya:function(a){for(var a=a||[],e=[],b=0,f=a.length;b<f;b++)0>r.a.k(e,a[b])&&e.push(a[b]);return e},ba:function(a,e){for(var a=a||[],b=[],f=0,d=a.length;f<d;f++)b.push(e(a[f]));return b},aa:function(a,e){for(var a=a||[],b=[],f=0,d=a.length;f<d;f++)e(a[f])&&b.push(a[f]);return b},J:function(a,e){for(var b=0,f=e.length;b<f;b++)a.push(e[b]);return a},extend:function(a,e){for(var b in e)e.hasOwnProperty(b)&&
(a[b]=e[b]);return a},U:function(a){for(;a.firstChild;)r.removeNode(a.firstChild)},oa:function(a,e){r.a.U(a);e&&r.a.n(e,function(e){a.appendChild(e)})},Ja:function(a,e){var b=a.nodeType?[a]:a;if(0<b.length){for(var f=b[0],d=f.parentNode,h=0,g=e.length;h<g;h++)d.insertBefore(e[h],f);h=0;for(g=b.length;h<g;h++)r.removeNode(b[h])}},La:function(a,e){0<=navigator.userAgent.indexOf("MSIE 6")?a.setAttribute("selected",e):a.selected=e},z:function(a){return(a||"").replace(b,"")},Db:function(a,e){for(var b=
[],f=(a||"").split(e),d=0,h=f.length;d<h;d++){var g=r.a.z(f[d]);""!==g&&b.push(g)}return b},Cb:function(a,e){a=a||"";return e.length>a.length?p:a.substring(0,e.length)===e},hb:function(a){for(var e=Array.prototype.slice.call(arguments,1),b="return ("+a+")",f=0;f<e.length;f++)e[f]&&"object"==typeof e[f]&&(b="with(sc["+f+"]) { "+b+" } ");return(new Function("sc",b))(e)},fb:function(a,e){if(e.compareDocumentPosition)return 16==(e.compareDocumentPosition(a)&16);for(;a!=o;){if(a==e)return m;a=a.parentNode}return p},
ga:function(a){return r.a.fb(a,document)},s:function(e,b,f){if("undefined"!=typeof jQuery){if(a(e,b))var d=f,f=function(a,e){var b=this.checked;if(e)this.checked=e.Ya!==m;d.call(this,a);this.checked=b};jQuery(e).bind(b,f)}else"function"==typeof e.addEventListener?e.addEventListener(b,f,p):"undefined"!=typeof e.attachEvent?e.attachEvent("on"+b,function(a){f.call(e,a)}):c(Error("Browser doesn't support addEventListener or attachEvent"))},sa:function(b,f){(!b||!b.nodeType)&&c(Error("element must be a DOM node when calling triggerEvent"));
if("undefined"!=typeof jQuery){var d=[];a(b,f)&&d.push({Ya:b.checked});jQuery(b).trigger(f,d)}else if("function"==typeof document.createEvent)"function"==typeof b.dispatchEvent?(d=document.createEvent(e[f]||"HTMLEvents"),d.initEvent(f,m,m,window,0,0,0,0,0,p,p,p,p,0,b),b.dispatchEvent(d)):c(Error("The supplied element doesn't support dispatchEvent"));else if("undefined"!=typeof b.fireEvent){if("click"==f&&"INPUT"==b.tagName&&("checkbox"==b.type.toLowerCase()||"radio"==b.type.toLowerCase()))b.checked=
b.checked!==m;b.fireEvent("on"+f)}else c(Error("Browser doesn't support triggering events"))},d:function(a){return r.V(a)?a():a},eb:function(a,e){return 0<=r.a.k((a.className||"").split(/\s+/),e)},Qa:function(a,e,b){var f=r.a.eb(a,e);if(b&&!f)a.className=(a.className||"")+" "+e;else if(f&&!b){for(var b=(a.className||"").split(/\s+/),f="",d=0;d<b.length;d++)b[d]!=e&&(f+=b[d]+" ");a.className=r.a.z(f)}},outerHTML:function(a){if(j===l){var e=a.outerHTML;if("string"==typeof e)return e}e=window.document.createElement("div");
e.appendChild(a.cloneNode(m));return e.innerHTML},Ma:function(a,e){var b=r.a.d(e);if(b===o||b===l)b="";"innerText"in a?a.innerText=b:a.textContent=b;if(9<=j)a.innerHTML=a.innerHTML},yb:function(a,e){for(var a=r.a.d(a),e=r.a.d(e),b=[],f=a;f<=e;f++)b.push(f);return b},X:function(a){for(var e=[],b=0,f=a.length;b<f;b++)e.push(a[b]);return e},ob:6===j,pb:7===j,Ca:function(a,e){for(var b=r.a.X(a.getElementsByTagName("INPUT")).concat(r.a.X(a.getElementsByTagName("TEXTAREA"))),f="string"==typeof e?function(a){return a.name===
e}:function(a){return e.test(a.name)},d=[],h=b.length-1;0<=h;h--)f(b[h])&&d.push(b[h]);return d},vb:function(a){return"string"==typeof a&&(a=r.a.z(a))?window.JSON&&window.JSON.parse?window.JSON.parse(a):(new Function("return "+a))():o},qa:function(a){("undefined"==typeof JSON||"undefined"==typeof JSON.stringify)&&c(Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js"));
return JSON.stringify(r.a.d(a))},wb:function(a,e,b){var b=b||{},f=b.params||{},d=b.includeFields||this.Ba,h=a;if("object"==typeof a&&"FORM"==a.tagName)for(var h=a.action,g=d.length-1;0<=g;g--)for(var j=r.a.Ca(a,d[g]),i=j.length-1;0<=i;i--)f[j[i].name]=j[i].value;var e=r.a.d(e),u=document.createElement("FORM");u.style.display="none";u.action=h;u.method="post";for(var y in e)a=document.createElement("INPUT"),a.name=y,a.value=r.a.qa(r.a.d(e[y])),u.appendChild(a);for(y in f)a=document.createElement("INPUT"),
a.name=y,a.value=f[y],u.appendChild(a);document.body.appendChild(u);b.submitter?b.submitter(u):u.submit();setTimeout(function(){u.parentNode.removeChild(u)},0)}}};r.b("ko.utils",r.a);
r.a.n([["arrayForEach",r.a.n],["arrayFirst",r.a.Wa],["arrayFilter",r.a.aa],["arrayGetDistinctValues",r.a.ya],["arrayIndexOf",r.a.k],["arrayMap",r.a.ba],["arrayPushAll",r.a.J],["arrayRemoveItem",r.a.ca],["extend",r.a.extend],["fieldsIncludedWithJsonPost",r.a.Ba],["getFormFields",r.a.Ca],["postJson",r.a.wb],["parseJson",r.a.vb],["registerEventHandler",r.a.s],["stringifyJson",r.a.qa],["range",r.a.yb],["toggleDomNodeCssClass",r.a.Qa],["triggerEvent",r.a.sa],["unwrapObservable",r.a.d]],function(a){r.b("ko.utils."+
a[0],a[1])});Function.prototype.bind||(Function.prototype.bind=function(a){var b=this,d=Array.prototype.slice.call(arguments),a=d.shift();return function(){return b.apply(a,d.concat(Array.prototype.slice.call(arguments)))}});
r.a.e=new function(){var a=0,b="__ko__"+(new Date).getTime(),d={};return{get:function(a,b){var d=r.a.e.getAll(a,p);return d===l?l:d[b]},set:function(a,b,d){d===l&&r.a.e.getAll(a,p)===l||(r.a.e.getAll(a,m)[b]=d)},getAll:function(e,f){var h=e[b];if(!(h&&"null"!==h)){if(!f)return;h=e[b]="ko"+a++;d[h]={}}return d[h]},clear:function(a){var f=a[b];f&&(delete d[f],a[b]=o)}}};r.b("ko.utils.domData",r.a.e);r.b("ko.utils.domData.clear",r.a.e.clear);
r.a.A=new function(){function a(a,b){var h=r.a.e.get(a,d);h===l&&b&&(h=[],r.a.e.set(a,d,h));return h}function b(e){var b=a(e,p);if(b)for(var b=b.slice(0),d=0;d<b.length;d++)b[d](e);r.a.e.clear(e);"function"==typeof jQuery&&"function"==typeof jQuery.cleanData&&jQuery.cleanData([e])}var d="__ko_domNodeDisposal__"+(new Date).getTime();return{va:function(e,b){"function"!=typeof b&&c(Error("Callback must be a function"));a(e,m).push(b)},Ia:function(e,b){var h=a(e,p);h&&(r.a.ca(h,b),0==h.length&&r.a.e.set(e,
d,l))},F:function(a){if(!(1!=a.nodeType&&9!=a.nodeType)){b(a);var f=[];r.a.J(f,a.getElementsByTagName("*"));for(var a=0,d=f.length;a<d;a++)b(f[a])}},removeNode:function(a){r.F(a);a.parentNode&&a.parentNode.removeChild(a)}}};r.F=r.a.A.F;r.removeNode=r.a.A.removeNode;r.b("ko.cleanNode",r.F);r.b("ko.removeNode",r.removeNode);r.b("ko.utils.domNodeDisposal",r.a.A);r.b("ko.utils.domNodeDisposal.addDisposeCallback",r.a.A.va);r.b("ko.utils.domNodeDisposal.removeDisposeCallback",r.a.A.Ia);
r.a.ma=function(a){var b;if("undefined"!=typeof jQuery){if((b=jQuery.clean([a]))&&b[0]){for(a=b[0];a.parentNode&&11!==a.parentNode.nodeType;)a=a.parentNode;a.parentNode&&a.parentNode.removeChild(a)}}else{var d=r.a.z(a).toLowerCase();b=document.createElement("div");d=d.match(/^<(thead|tbody|tfoot)/)&&[1,"<table>","</table>"]||!d.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!d.indexOf("<td")||!d.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||[0,"",""];a="ignored<div>"+
d[1]+a+d[2]+"</div>";for("function"==typeof window.innerShiv?b.appendChild(window.innerShiv(a)):b.innerHTML=a;d[0]--;)b=b.lastChild;b=r.a.X(b.lastChild.childNodes)}return b};r.a.Z=function(a,b){r.a.U(a);if(b!==o&&b!==l)if("string"!=typeof b&&(b=b.toString()),"undefined"!=typeof jQuery)jQuery(a).html(b);else for(var d=r.a.ma(b),e=0;e<d.length;e++)a.appendChild(d[e])};r.b("ko.utils.parseHtmlFragment",r.a.ma);r.b("ko.utils.setHtml",r.a.Z);
r.r=function(){function a(){return(4294967296*(1+Math.random())|0).toString(16).substring(1)}function b(a,f){if(a)if(8==a.nodeType){var d=r.r.Ga(a.nodeValue);d!=o&&f.push({cb:a,tb:d})}else if(1==a.nodeType)for(var d=0,g=a.childNodes,i=g.length;d<i;d++)b(g[d],f)}var d={};return{ka:function(b){"function"!=typeof b&&c(Error("You can only pass a function to ko.memoization.memoize()"));var f=a()+a();d[f]=b;return"<\!--[ko_memo:"+f+"]--\>"},Ra:function(a,b){var h=d[a];h===l&&c(Error("Couldn't find any memo with ID "+
a+". Perhaps it's already been unmemoized."));try{return h.apply(o,b||[]),m}finally{delete d[a]}},Sa:function(a,f){var d=[];b(a,d);for(var g=0,i=d.length;g<i;g++){var j=d[g].cb,k=[j];f&&r.a.J(k,f);r.r.Ra(d[g].tb,k);j.nodeValue="";j.parentNode&&j.parentNode.removeChild(j)}},Ga:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:o}}}();r.b("ko.memoization",r.r);r.b("ko.memoization.memoize",r.r.ka);r.b("ko.memoization.unmemoize",r.r.Ra);r.b("ko.memoization.parseMemoText",r.r.Ga);
r.b("ko.memoization.unmemoizeDomNodeAndDescendants",r.r.Sa);r.Aa={throttle:function(a,b){a.throttleEvaluation=b;var d=o;return r.i({read:a,write:function(e){clearTimeout(d);d=setTimeout(function(){a(e)},b)}})},notify:function(a,b){a.equalityComparer="always"==b?function(){return p}:r.w.fn.equalityComparer;return a}};r.b("ko.extenders",r.Aa);r.Oa=function(a,b){this.da=a;this.bb=b;r.l(this,"dispose",this.v)};r.Oa.prototype.v=function(){this.nb=m;this.bb()};
r.R=function(){this.u={};r.a.extend(this,r.R.fn);r.l(this,"subscribe",this.ra);r.l(this,"extend",this.extend);r.l(this,"getSubscriptionsCount",this.kb)};
r.R.fn={ra:function(a,b,d){var d=d||"change",a=b?a.bind(b):a,e=new r.Oa(a,function(){r.a.ca(this.u[d],e)}.bind(this));this.u[d]||(this.u[d]=[]);this.u[d].push(e);return e},notifySubscribers:function(a,b){b=b||"change";this.u[b]&&r.a.n(this.u[b].slice(0),function(b){b&&b.nb!==m&&b.da(a)})},kb:function(){var a=0,b;for(b in this.u)this.u.hasOwnProperty(b)&&(a+=this.u[b].length);return a},extend:function(a){var b=this;if(a)for(var d in a){var e=r.Aa[d];"function"==typeof e&&(b=e(b,a[d]))}return b}};
r.Ea=function(a){return"function"==typeof a.ra&&"function"==typeof a.notifySubscribers};r.b("ko.subscribable",r.R);r.b("ko.isSubscribable",r.Ea);r.T=function(){var a=[];return{Xa:function(b){a.push({da:b,za:[]})},end:function(){a.pop()},Ha:function(b){r.Ea(b)||c("Only subscribable things can act as dependencies");if(0<a.length){var d=a[a.length-1];0<=r.a.k(d.za,b)||(d.za.push(b),d.da(b))}}}}();var B={undefined:m,"boolean":m,number:m,string:m};
r.w=function(a){function b(){if(0<arguments.length){if(!b.equalityComparer||!b.equalityComparer(d,arguments[0]))b.H(),d=arguments[0],b.G();return this}r.T.Ha(b);return d}var d=a;r.R.call(b);b.G=function(){b.notifySubscribers(d)};b.H=function(){b.notifySubscribers(d,"beforeChange")};r.a.extend(b,r.w.fn);r.l(b,"valueHasMutated",b.G);r.l(b,"valueWillMutate",b.H);return b};r.w.fn={B:r.w,equalityComparer:function(a,b){return a===o||typeof a in B?a===b:p}};
r.V=function(a){return a===o||a===l||a.B===l?p:a.B===r.w?m:r.V(a.B)};r.P=function(a){return"function"==typeof a&&a.B===r.w?m:"function"==typeof a&&a.B===r.i&&a.lb?m:p};r.b("ko.observable",r.w);r.b("ko.isObservable",r.V);r.b("ko.isWriteableObservable",r.P);
r.Q=function(a){0==arguments.length&&(a=[]);a!==o&&a!==l&&!("length"in a)&&c(Error("The argument passed when initializing an observable array must be an array, or null, or undefined."));var b=new r.w(a);r.a.extend(b,r.Q.fn);r.l(b,"remove",b.remove);r.l(b,"removeAll",b.zb);r.l(b,"destroy",b.fa);r.l(b,"destroyAll",b.ab);r.l(b,"indexOf",b.indexOf);r.l(b,"replace",b.replace);return b};
r.Q.fn={remove:function(a){for(var b=this(),d=[],e="function"==typeof a?a:function(b){return b===a},f=0;f<b.length;f++){var h=b[f];e(h)&&(0===d.length&&this.H(),d.push(h),b.splice(f,1),f--)}d.length&&this.G();return d},zb:function(a){if(a===l){var b=this(),d=b.slice(0);this.H();b.splice(0,b.length);this.G();return d}return!a?[]:this.remove(function(b){return 0<=r.a.k(a,b)})},fa:function(a){var b=this(),d="function"==typeof a?a:function(b){return b===a};this.H();for(var e=b.length-1;0<=e;e--)d(b[e])&&
(b[e]._destroy=m);this.G()},ab:function(a){return a===l?this.fa(function(){return m}):!a?[]:this.fa(function(b){return 0<=r.a.k(a,b)})},indexOf:function(a){var b=this();return r.a.k(b,a)},replace:function(a,b){var d=this.indexOf(a);0<=d&&(this.H(),this()[d]=b,this.G())}};r.a.n("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){r.Q.fn[a]=function(){var b=this();this.H();b=b[a].apply(b,arguments);this.G();return b}});
r.a.n(["slice"],function(a){r.Q.fn[a]=function(){var b=this();return b[a].apply(b,arguments)}});r.b("ko.observableArray",r.Q);function C(a,b){a&&"object"==typeof a?b=a:(b=b||{},b.read=a||b.read);"function"!=typeof b.read&&c("Pass a function that returns the value of the dependentObservable");return b}
r.i=function(a,b,d){function e(){r.a.n(q,function(a){a.v()});q=[]}function f(){var a=g.throttleEvaluation;a&&0<=a?(clearTimeout(v),v=setTimeout(h,a)):h()}function h(){if(j&&"function"==typeof d.disposeWhen&&d.disposeWhen())g.v();else{try{e();r.T.Xa(function(a){q.push(a.ra(f))});var a=d.read.call(d.owner||b);g.notifySubscribers(i,"beforeChange");i=a}finally{r.T.end()}g.notifySubscribers(i);j=m}}function g(){if(0<arguments.length)"function"===typeof d.write?d.write.apply(d.owner||b,arguments):c("Cannot write a value to a dependentObservable unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
else return j||h(),r.T.Ha(g),i}var i,j=p,d=C(a,d),k="object"==typeof d.disposeWhenNodeIsRemoved?d.disposeWhenNodeIsRemoved:o,n=o;if(k){n=function(){g.v()};r.a.A.va(k,n);var t=d.disposeWhen;d.disposeWhen=function(){return!r.a.ga(k)||"function"==typeof t&&t()}}var q=[],v=o;g.jb=function(){return q.length};g.lb="function"===typeof d.write;g.v=function(){k&&r.a.A.Ia(k,n);e()};r.R.call(g);r.a.extend(g,r.i.fn);d.deferEvaluation!==m&&h();r.l(g,"dispose",g.v);r.l(g,"getDependenciesCount",g.jb);return g};
r.i.fn={B:r.i};r.i.B=r.w;r.b("ko.dependentObservable",r.i);r.b("ko.computed",r.i);
(function(){function a(e,f,h){h=h||new d;e=f(e);if(!("object"==typeof e&&e!==o&&e!==l&&!(e instanceof Date)))return e;var g=e instanceof Array?[]:{};h.save(e,g);b(e,function(b){var d=f(e[b]);switch(typeof d){case "boolean":case "number":case "string":case "function":g[b]=d;break;case "object":case "undefined":var k=h.get(d);g[b]=k!==l?k:a(d,f,h)}});return g}function b(a,b){if(a instanceof Array)for(var d=0;d<a.length;d++)b(d);else for(d in a)b(d)}function d(){var a=[],b=[];this.save=function(d,g){var i=
r.a.k(a,d);0<=i?b[i]=g:(a.push(d),b.push(g))};this.get=function(d){d=r.a.k(a,d);return 0<=d?b[d]:l}}r.Pa=function(b){0==arguments.length&&c(Error("When calling ko.toJS, pass the object you want to convert."));return a(b,function(a){for(var b=0;r.V(a)&&10>b;b++)a=a();return a})};r.toJSON=function(a){a=r.Pa(a);return r.a.qa(a)}})();r.b("ko.toJS",r.Pa);r.b("ko.toJSON",r.toJSON);
r.h={q:function(a){return"OPTION"==a.tagName?a.__ko__hasDomDataOptionValue__===m?r.a.e.get(a,r.c.options.la):a.getAttribute("value"):"SELECT"==a.tagName?0<=a.selectedIndex?r.h.q(a.options[a.selectedIndex]):l:a.value},S:function(a,b){if("OPTION"==a.tagName)switch(typeof b){case "string":r.a.e.set(a,r.c.options.la,l);"__ko__hasDomDataOptionValue__"in a&&delete a.__ko__hasDomDataOptionValue__;a.value=b;break;default:r.a.e.set(a,r.c.options.la,b),a.__ko__hasDomDataOptionValue__=m,a.value="number"===typeof b?
b:""}else if("SELECT"==a.tagName)for(var d=a.options.length-1;0<=d;d--){if(r.h.q(a.options[d])==b){a.selectedIndex=d;break}}else{if(b===o||b===l)b="";a.value=b}}};r.b("ko.selectExtensions",r.h);r.b("ko.selectExtensions.readValue",r.h.q);r.b("ko.selectExtensions.writeValue",r.h.S);
r.j=function(){function a(a,e){for(var d=o;a!=d;)d=a,a=a.replace(b,function(a,b){return e[b]});return a}var b=/\@ko_token_(\d+)\@/g,d=/^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i,e=["true","false"];return{D:[],Y:function(b){var e=r.a.z(b);if(3>e.length)return[];"{"===e.charAt(0)&&(e=e.substring(1,e.length-1));for(var b=[],d=o,i,j=0;j<e.length;j++){var k=e.charAt(j);if(d===o)switch(k){case '"':case "'":case "/":d=j,i=k}else if(k==i&&"\\"!==e.charAt(j-1)){k=e.substring(d,j+
1);b.push(k);var n="@ko_token_"+(b.length-1)+"@",e=e.substring(0,d)+n+e.substring(j+1),j=j-(k.length-n.length),d=o}}i=d=o;for(var t=0,q=o,j=0;j<e.length;j++){k=e.charAt(j);if(d===o)switch(k){case "{":d=j;q=k;i="}";break;case "(":d=j;q=k;i=")";break;case "[":d=j,q=k,i="]"}k===q?t++:k===i&&(t--,0===t&&(k=e.substring(d,j+1),b.push(k),n="@ko_token_"+(b.length-1)+"@",e=e.substring(0,d)+n+e.substring(j+1),j-=k.length-n.length,d=o))}i=[];e=e.split(",");d=0;for(j=e.length;d<j;d++)t=e[d],q=t.indexOf(":"),
0<q&&q<t.length-1?(k=t.substring(q+1),i.push({key:a(t.substring(0,q),b),value:a(k,b)})):i.push({unknown:a(t,b)});return i},ia:function(a){for(var b="string"===typeof a?r.j.Y(a):a,g=[],a=[],i,j=0;i=b[j];j++)if(0<g.length&&g.push(","),i.key){var k;a:{k=i.key;var n=r.a.z(k);switch(n.length&&n.charAt(0)){case "'":case '"':break a;default:k="'"+n+"'"}}i=i.value;g.push(k);g.push(":");g.push(i);n=r.a.z(i);if(0<=r.a.k(e,r.a.z(n).toLowerCase())?0:n.match(d)!==o)0<a.length&&a.push(", "),a.push(k+" : function(__ko_value) { "+
i+" = __ko_value; }")}else i.unknown&&g.push(i.unknown);b=g.join("");0<a.length&&(b=b+", '_ko_property_writers' : { "+a.join("")+" } ");return b},rb:function(a,b){for(var e=0;e<a.length;e++)if(r.a.z(a[e].key)==b)return m;return p}}}();r.b("ko.jsonExpressionRewriting",r.j);r.b("ko.jsonExpressionRewriting.bindingRewriteValidators",r.j.D);r.b("ko.jsonExpressionRewriting.parseObjectLiteral",r.j.Y);r.b("ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson",r.j.ia);
(function(){function a(a){return 8==a.nodeType&&(f?a.text:a.nodeValue).match(h)}function b(a){return 8==a.nodeType&&(f?a.text:a.nodeValue).match(g)}function d(e,d){for(var f=e,g=1,h=[];f=f.nextSibling;){if(b(f)&&(g--,0===g))return h;h.push(f);a(f)&&g++}d||c(Error("Cannot find closing comment tag to match: "+e.nodeValue));return o}function e(a,b){var e=d(a,b);return e?0<e.length?e[e.length-1].nextSibling:a.nextSibling:o}var f="<\!--test--\>"===document.createComment("test").text,h=f?/^<\!--\s*ko\s+(.*\:.*)\s*--\>$/:
/^\s*ko\s+(.*\:.*)\s*$/,g=f?/^<\!--\s*\/ko\s*--\>$/:/^\s*\/ko\s*$/,i={ul:m,ol:m};r.f={C:{},childNodes:function(b){return a(b)?d(b):b.childNodes},ha:function(b){if(a(b))for(var b=r.f.childNodes(b),e=0,d=b.length;e<d;e++)r.removeNode(b[e]);else r.a.U(b)},oa:function(b,e){if(a(b)){r.f.ha(b);for(var d=b.nextSibling,f=0,g=e.length;f<g;f++)d.parentNode.insertBefore(e[f],d)}else r.a.oa(b,e)},xb:function(b,e){a(b)?b.parentNode.insertBefore(e,b.nextSibling):b.firstChild?b.insertBefore(e,b.firstChild):b.appendChild(e)},
mb:function(b,e,d){a(b)?b.parentNode.insertBefore(e,d.nextSibling):d.nextSibling?b.insertBefore(e,d.nextSibling):b.appendChild(e)},nextSibling:function(d){return a(d)?e(d).nextSibling:d.nextSibling&&b(d.nextSibling)?l:d.nextSibling},ta:function(b){return(b=a(b))?b[1]:o},ib:function(a){if(r.f.ta(a)){var b;b=r.f.childNodes(a);for(var e=[],d=0,f=b.length;d<f;d++)r.a.A.F(b[d]),e.push(r.a.outerHTML(b[d]));b=String.prototype.concat.apply("",e);r.f.ha(a);(new r.m.I(a)).text(b)}},Fa:function(d){if(i[d.tagName.toLowerCase()]){var f=
d.firstChild;if(f){do if(1===f.nodeType){var g;g=f.firstChild;var h=o;if(g){do if(h)h.push(g);else if(a(g)){var q=e(g,m);q?g=q:h=[g]}else b(g)&&(h=[g]);while(g=g.nextSibling)}if(g=h){h=f.nextSibling;for(q=0;q<g.length;q++)h?d.insertBefore(g[q],h):d.appendChild(g[q])}}while(f=f.nextSibling)}}}}})();r.L=function(){};
r.a.extend(r.L.prototype,{nodeHasBindings:function(a){switch(a.nodeType){case 1:return a.getAttribute("data-bind")!=o;case 8:return r.f.ta(a)!=o;default:return p}},getBindings:function(a,b){var d=this.getBindingsString(a,b);return d?this.parseBindingsString(d,b):o},getBindingsString:function(a){switch(a.nodeType){case 1:return a.getAttribute("data-bind");case 8:return r.f.ta(a);default:return o}},parseBindingsString:function(a,b){try{var d=b.$data,e=" { "+r.j.ia(a)+" } ";return r.a.hb(e,d===o?window:
d,b)}catch(f){c(Error("Unable to parse bindings.\nMessage: "+f+";\nBindings value: "+a))}}});r.L.instance=new r.L;r.b("ko.bindingProvider",r.L);
(function(){function a(a,d){for(var h,g=d.childNodes[0];h=g;)g=r.f.nextSibling(h),b(a,h,p)}function b(b,f,h){var g=m,i=1==f.nodeType;i&&r.f.Fa(f);if(i&&h||r.L.instance.nodeHasBindings(f))g=d(f,o,b,h).Bb;i&&g&&a(b,f)}function d(a,b,d,g){function i(a){return function(){return n[a]}}function j(){return n}var k=0;r.f.ib(a);var n,t;new r.i(function(){var q=d&&d instanceof r.K?d:new r.K(r.a.d(d)),v=q.$data;g&&r.Na(a,q);if(n=("function"==typeof b?b():b)||r.L.instance.getBindings(a,q)){if(0===k){k=1;for(var s in n){var w=
r.c[s];w&&8===a.nodeType&&!r.f.C[s]&&c(Error("The binding '"+s+"' cannot be used with virtual elements"));if(w&&"function"==typeof w.init&&(w=(0,w.init)(a,i(s),j,v,q))&&w.controlsDescendantBindings)t!==l&&c(Error("Multiple bindings ("+t+" and "+s+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.")),t=s}k=2}if(2===k)for(s in n)(w=r.c[s])&&"function"==typeof w.update&&(0,w.update)(a,i(s),j,v,q)}},o,{disposeWhenNodeIsRemoved:a});
return{Bb:t===l}}r.c={};r.K=function(a,b){this.$data=a;b?(this.$parent=b.$data,this.$parents=(b.$parents||[]).slice(0),this.$parents.unshift(this.$parent),this.$root=b.$root):(this.$parents=[],this.$root=a)};r.K.prototype.createChildContext=function(a){return new r.K(a,this)};r.Na=function(a,b){if(2==arguments.length)r.a.e.set(a,"__ko_bindingContext__",b);else return r.a.e.get(a,"__ko_bindingContext__")};r.xa=function(a,b,h){1===a.nodeType&&r.f.Fa(a);return d(a,b,h,m)};r.Ta=function(b,d){1===d.nodeType&&
a(b,d)};r.wa=function(a,d){d&&1!==d.nodeType&&8!==d.nodeType&&c(Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node"));d=d||window.document.body;b(a,d,m)};r.ea=function(a){switch(a.nodeType){case 1:case 8:var b=r.Na(a);if(b)return b;if(a.parentNode)return r.ea(a.parentNode)}};r.$a=function(a){return(a=r.ea(a))?a.$data:l};r.b("ko.bindingHandlers",r.c);r.b("ko.applyBindings",r.wa);r.b("ko.applyBindingsToDescendants",r.Ta);r.b("ko.applyBindingsToNode",
r.xa);r.b("ko.contextFor",r.ea);r.b("ko.dataFor",r.$a)})();r.a.n(["click"],function(a){r.c[a]={init:function(b,d,e,f){return r.c.event.init.call(this,b,function(){var b={};b[a]=d();return b},e,f)}}});
r.c.event={init:function(a,b,d,e){var f=b()||{},h;for(h in f)(function(){var f=h;"string"==typeof f&&r.a.s(a,f,function(a){var h,k=b()[f];if(k){var n=d();try{var t=r.a.X(arguments);t.unshift(e);h=k.apply(e,t)}finally{if(h!==m)a.preventDefault?a.preventDefault():a.returnValue=p}if(n[f+"Bubble"]===p)a.cancelBubble=m,a.stopPropagation&&a.stopPropagation()}})})()}};
r.c.submit={init:function(a,b,d,e){"function"!=typeof b()&&c(Error("The value for a submit binding must be a function"));r.a.s(a,"submit",function(d){var h,g=b();try{h=g.call(e,a)}finally{if(h!==m)d.preventDefault?d.preventDefault():d.returnValue=p}})}};r.c.visible={update:function(a,b){var d=r.a.d(b()),e="none"!=a.style.display;if(d&&!e)a.style.display="";else if(!d&&e)a.style.display="none"}};
r.c.enable={update:function(a,b){var d=r.a.d(b());if(d&&a.disabled)a.removeAttribute("disabled");else if(!d&&!a.disabled)a.disabled=m}};r.c.disable={update:function(a,b){r.c.enable.update(a,function(){return!r.a.d(b())})}};function D(a,b,d){d&&b!==r.h.q(a)&&r.h.S(a,b);b!==r.h.q(a)&&r.a.sa(a,"change")}
r.c.value={init:function(a,b,d){var e=["change"],f=d().valueUpdate;f&&("string"==typeof f&&(f=[f]),r.a.J(e,f),e=r.a.ya(e));r.a.n(e,function(e){var f=p;r.a.Cb(e,"after")&&(f=m,e=e.substring(5));var i=f?function(a){setTimeout(a,0)}:function(a){a()};r.a.s(a,e,function(){i(function(){var e=b(),f=r.h.q(a);r.P(e)?e(f):(e=d(),e._ko_property_writers&&e._ko_property_writers.value&&e._ko_property_writers.value(f))})})})},update:function(a,b){var d=r.a.d(b()),e=r.h.q(a),f=d!=e;0===d&&0!==e&&"0"!==e&&(f=m);f&&
(e=function(){r.h.S(a,d)},e(),"SELECT"==a.tagName&&setTimeout(e,0));"SELECT"==a.tagName&&0<a.length&&D(a,d,p)}};
r.c.options={update:function(a,b,d){"SELECT"!=a.tagName&&c(Error("options binding applies only to SELECT elements"));var e=0==a.length,f=r.a.ba(r.a.aa(a.childNodes,function(a){return a.tagName&&"OPTION"==a.tagName&&a.selected}),function(a){return r.h.q(a)||a.innerText||a.textContent}),h=a.scrollTop;a.scrollTop=0;for(var g=r.a.d(b());0<a.length;)r.F(a.options[0]),a.remove(0);if(g){d=d();"number"!=typeof g.length&&(g=[g]);if(d.optionsCaption){var i=document.createElement("OPTION");r.a.Z(i,d.optionsCaption);
r.h.S(i,l);a.appendChild(i)}for(var b=0,j=g.length;b<j;b++){var i=document.createElement("OPTION"),k="string"==typeof d.optionsValue?g[b][d.optionsValue]:g[b],k=r.a.d(k);r.h.S(i,k);var n=d.optionsText,k="function"==typeof n?n(g[b]):"string"==typeof n?g[b][n]:k;if(k===o||k===l)k="";r.a.Ma(i,k);a.appendChild(i)}g=a.getElementsByTagName("OPTION");b=i=0;for(j=g.length;b<j;b++)0<=r.a.k(f,r.h.q(g[b]))&&(r.a.La(g[b],m),i++);if(h)a.scrollTop=h;e&&"value"in d&&D(a,r.a.d(d.value),m)}}};r.c.options.la="__ko.optionValueDomData__";
r.c.selectedOptions={Da:function(a){for(var b=[],a=a.childNodes,d=0,e=a.length;d<e;d++){var f=a[d];"OPTION"==f.tagName&&f.selected&&b.push(r.h.q(f))}return b},init:function(a,b,d){r.a.s(a,"change",function(){var a=b();r.P(a)?a(r.c.selectedOptions.Da(this)):(a=d(),a._ko_property_writers&&a._ko_property_writers.value&&a._ko_property_writers.value(r.c.selectedOptions.Da(this)))})},update:function(a,b){"SELECT"!=a.tagName&&c(Error("values binding applies only to SELECT elements"));var d=r.a.d(b());if(d&&
"number"==typeof d.length)for(var e=a.childNodes,f=0,h=e.length;f<h;f++){var g=e[f];"OPTION"==g.tagName&&r.a.La(g,0<=r.a.k(d,r.h.q(g)))}}};r.c.text={update:function(a,b){r.a.Ma(a,b())}};r.c.html={init:function(){return{controlsDescendantBindings:m}},update:function(a,b){var d=r.a.d(b());r.a.Z(a,d)}};r.c.css={update:function(a,b){var d=r.a.d(b()||{}),e;for(e in d)if("string"==typeof e){var f=r.a.d(d[e]);r.a.Qa(a,e,f)}}};
r.c.style={update:function(a,b){var d=r.a.d(b()||{}),e;for(e in d)if("string"==typeof e){var f=r.a.d(d[e]);a.style[e]=f||""}}};r.c.uniqueName={init:function(a,b){if(b())a.name="ko_unique_"+ ++r.c.uniqueName.Za,(r.a.ob||r.a.pb)&&a.mergeAttributes(document.createElement("<input name='"+a.name+"'/>"),p)}};r.c.uniqueName.Za=0;
r.c.checked={init:function(a,b,d){r.a.s(a,"click",function(){var e;if("checkbox"==a.type)e=a.checked;else if("radio"==a.type&&a.checked)e=a.value;else return;var f=b();"checkbox"==a.type&&r.a.d(f)instanceof Array?(e=r.a.k(r.a.d(f),a.value),a.checked&&0>e?f.push(a.value):!a.checked&&0<=e&&f.splice(e,1)):r.P(f)?f()!==e&&f(e):(f=d(),f._ko_property_writers&&f._ko_property_writers.checked&&f._ko_property_writers.checked(e))});"radio"==a.type&&!a.name&&r.c.uniqueName.init(a,function(){return m})},update:function(a,
b){var d=r.a.d(b());if("checkbox"==a.type)a.checked=d instanceof Array?0<=r.a.k(d,a.value):d;else if("radio"==a.type)a.checked=a.value==d}};r.c.attr={update:function(a,b){var d=r.a.d(b())||{},e;for(e in d)if("string"==typeof e){var f=r.a.d(d[e]);f===p||f===o||f===l?a.removeAttribute(e):a.setAttribute(e,f.toString())}}};
r.c.hasfocus={init:function(a,b,d){function e(a){var e=b();a!=r.a.d(e)&&(r.P(e)?e(a):(e=d(),e._ko_property_writers&&e._ko_property_writers.hasfocus&&e._ko_property_writers.hasfocus(a)))}r.a.s(a,"focus",function(){e(m)});r.a.s(a,"focusin",function(){e(m)});r.a.s(a,"blur",function(){e(p)});r.a.s(a,"focusout",function(){e(p)})},update:function(a,b){var d=r.a.d(b());d?a.focus():a.blur();r.a.sa(a,d?"focusin":"focusout")}};
r.c["with"]={o:function(a){return function(){var b=a();return{"if":b,data:b,templateEngine:r.p.M}}},init:function(a,b){return r.c.template.init(a,r.c["with"].o(b))},update:function(a,b,d,e,f){return r.c.template.update(a,r.c["with"].o(b),d,e,f)}};r.j.D["with"]=p;r.f.C["with"]=m;r.c["if"]={o:function(a){return function(){return{"if":a(),templateEngine:r.p.M}}},init:function(a,b){return r.c.template.init(a,r.c["if"].o(b))},update:function(a,b,d,e,f){return r.c.template.update(a,r.c["if"].o(b),d,e,f)}};
r.j.D["if"]=p;r.f.C["if"]=m;r.c.ifnot={o:function(a){return function(){return{ifnot:a(),templateEngine:r.p.M}}},init:function(a,b){return r.c.template.init(a,r.c.ifnot.o(b))},update:function(a,b,d,e,f){return r.c.template.update(a,r.c.ifnot.o(b),d,e,f)}};r.j.D.ifnot=p;r.f.C.ifnot=m;
r.c.foreach={o:function(a){return function(){var b=r.a.d(a());return!b||"number"==typeof b.length?{foreach:b,templateEngine:r.p.M}:{foreach:b.data,includeDestroyed:b.includeDestroyed,afterAdd:b.afterAdd,beforeRemove:b.beforeRemove,afterRender:b.afterRender,templateEngine:r.p.M}}},init:function(a,b){return r.c.template.init(a,r.c.foreach.o(b))},update:function(a,b,d,e,f){return r.c.template.update(a,r.c.foreach.o(b),d,e,f)}};r.j.D.foreach=p;r.f.C.foreach=m;r.b("ko.allowedVirtualElementBindings",r.f.C);
r.t=function(){};r.t.prototype.renderTemplateSource=function(){c("Override renderTemplateSource")};r.t.prototype.createJavaScriptEvaluatorBlock=function(){c("Override createJavaScriptEvaluatorBlock")};r.t.prototype.makeTemplateSource=function(a){if("string"==typeof a){var b=document.getElementById(a);b||c(Error("Cannot find template with ID "+a));return new r.m.g(b)}if(1==a.nodeType||8==a.nodeType)return new r.m.I(a);c(Error("Unknown template type: "+a))};
r.t.prototype.renderTemplate=function(a,b,d){return this.renderTemplateSource(this.makeTemplateSource(a),b,d)};r.t.prototype.isTemplateRewritten=function(a){return this.allowTemplateRewriting===p?m:this.W&&this.W[a]?m:this.makeTemplateSource(a).data("isRewritten")};r.t.prototype.rewriteTemplate=function(a,b){var d=this.makeTemplateSource(a),e=b(d.text());d.text(e);d.data("isRewritten",m);if("string"==typeof a)this.W=this.W||{},this.W[a]=m};r.b("ko.templateEngine",r.t);
r.$=function(){function a(a,b,d){for(var a=r.j.Y(a),g=r.j.D,i=0;i<a.length;i++){var j=a[i].key;if(g.hasOwnProperty(j)){var k=g[j];"function"===typeof k?(j=k(a[i].value))&&c(Error(j)):k||c(Error("This template engine does not support the '"+j+"' binding within its templates"))}}a="ko.templateRewriting.applyMemoizedBindingsToNextSibling(function() { return (function() { return { "+r.j.ia(a)+" } })() })";return d.createJavaScriptEvaluatorBlock(a)+b}var b=/(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi,
d=/<\!--\s*ko\b\s*([\s\S]*?)\s*--\>/g;return{gb:function(a,b){b.isTemplateRewritten(a)||b.rewriteTemplate(a,function(a){return r.$.ub(a,b)})},ub:function(e,f){return e.replace(b,function(b,e,d,j,k,n,t){return a(t,e,f)}).replace(d,function(b,e){return a(e,"<\!-- ko --\>",f)})},Ua:function(a){return r.r.ka(function(b,d){b.nextSibling&&r.xa(b.nextSibling,a,d)})}}}();r.b("ko.templateRewriting",r.$);r.b("ko.templateRewriting.applyMemoizedBindingsToNextSibling",r.$.Ua);r.m={};r.m.g=function(a){this.g=a};
r.m.g.prototype.text=function(){if(0==arguments.length)return"script"==this.g.tagName.toLowerCase()?this.g.text:this.g.innerHTML;var a=arguments[0];"script"==this.g.tagName.toLowerCase()?this.g.text=a:r.a.Z(this.g,a)};r.m.g.prototype.data=function(a){if(1===arguments.length)return r.a.e.get(this.g,"templateSourceData_"+a);r.a.e.set(this.g,"templateSourceData_"+a,arguments[1])};r.m.I=function(a){this.g=a};r.m.I.prototype=new r.m.g;
r.m.I.prototype.text=function(){if(0==arguments.length)return r.a.e.get(this.g,"__ko_anon_template__");r.a.e.set(this.g,"__ko_anon_template__",arguments[0])};r.b("ko.templateSources",r.m);r.b("ko.templateSources.domElement",r.m.g);r.b("ko.templateSources.anonymousTemplate",r.m.I);
(function(){function a(a,b,d){for(var g=0;node=a[g];g++)node.parentNode===b&&(1===node.nodeType||8===node.nodeType)&&d(node)}function b(a,b,h,g,i){var i=i||{},j=i.templateEngine||d;r.$.gb(h,j);h=j.renderTemplate(h,g,i);("number"!=typeof h.length||0<h.length&&"number"!=typeof h[0].nodeType)&&c("Template engine must return an array of DOM nodes");j=p;switch(b){case "replaceChildren":r.f.oa(a,h);j=m;break;case "replaceNode":r.a.Ja(a,h);j=m;break;case "ignoreTargetNode":break;default:c(Error("Unknown renderMode: "+
b))}j&&(r.ua(h,g),i.afterRender&&i.afterRender(h,g.$data));return h}var d;r.pa=function(a){a!=l&&!(a instanceof r.t)&&c("templateEngine must inherit from ko.templateEngine");d=a};r.ua=function(b,d){var h=r.a.J([],b),g=0<b.length?b[0].parentNode:o;a(h,g,function(a){r.wa(d,a)});a(h,g,function(a){r.r.Sa(a,[d])})};r.na=function(a,f,h,g,i){h=h||{};(h.templateEngine||d)==l&&c("Set a template engine before calling renderTemplate");i=i||"replaceChildren";if(g){var j=g.nodeType?g:0<g.length?g[0]:o;return new r.i(function(){var d=
f&&f instanceof r.K?f:new r.K(r.a.d(f)),n="function"==typeof a?a(d.$data):a,d=b(g,i,n,d,h);"replaceNode"==i&&(g=d,j=g.nodeType?g:0<g.length?g[0]:o)},o,{disposeWhen:function(){return!j||!r.a.ga(j)},disposeWhenNodeIsRemoved:j&&"replaceNode"==i?j.parentNode:j})}return r.r.ka(function(b){r.na(a,f,h,b,"replaceNode")})};r.Ab=function(a,d,h,g,i){function j(a,b){var d=k(a);r.ua(b,d);h.afterRender&&h.afterRender(b,d.$data)}function k(a){return i.createChildContext(r.a.d(a))}return new r.i(function(){var i=
r.a.d(d)||[];"undefined"==typeof i.length&&(i=[i]);i=r.a.aa(i,function(a){return h.includeDestroyed||a===l||a===o||!r.a.d(a._destroy)});r.a.Ka(g,i,function(d){var f="function"==typeof a?a(d):a;return b(o,"ignoreTargetNode",f,k(d),h)},h,j)},o,{disposeWhenNodeIsRemoved:g})};r.c.template={init:function(a,b){var d=r.a.d(b());"string"!=typeof d&&!d.name&&1==a.nodeType&&((new r.m.I(a)).text(a.innerHTML),r.a.U(a));return{controlsDescendantBindings:m}},update:function(a,b,d,g,i){b=r.a.d(b());g=m;"string"==
typeof b?d=b:(d=b.name,"if"in b&&(g=g&&r.a.d(b["if"])),"ifnot"in b&&(g=g&&!r.a.d(b.ifnot)));var j=o;"object"===typeof b&&"foreach"in b?j=r.Ab(d||a,g&&b.foreach||[],b,a,i):g?(i="object"==typeof b&&"data"in b?i.createChildContext(r.a.d(b.data)):i,j=r.na(d||a,i,b,a)):r.f.ha(a);i=j;(b=r.a.e.get(a,"__ko__templateSubscriptionDomDataKey__"))&&"function"==typeof b.v&&b.v();r.a.e.set(a,"__ko__templateSubscriptionDomDataKey__",i)}};r.j.D.template=function(a){a=r.j.Y(a);return 1==a.length&&a[0].unknown?o:r.j.rb(a,
"name")?o:"This template engine does not support anonymous templates nested within its templates"};r.f.C.template=m})();r.b("ko.setTemplateEngine",r.pa);r.b("ko.renderTemplate",r.na);
r.a.N=function(a,b,d){if(d===l)return r.a.N(a,b,1)||r.a.N(a,b,10)||r.a.N(a,b,Number.MAX_VALUE);for(var a=a||[],b=b||[],e=a,f=b,h=[],g=0;g<=f.length;g++)h[g]=[];for(var g=0,i=Math.min(e.length,d);g<=i;g++)h[0][g]=g;g=1;for(i=Math.min(f.length,d);g<=i;g++)h[g][0]=g;for(var i=e.length,j,k=f.length,g=1;g<=i;g++){j=Math.max(1,g-d);for(var n=Math.min(k,g+d);j<=n;j++)h[j][g]=e[g-1]===f[j-1]?h[j-1][g-1]:Math.min(h[j-1][g]===l?Number.MAX_VALUE:h[j-1][g]+1,h[j][g-1]===l?Number.MAX_VALUE:h[j][g-1]+1)}d=a.length;
e=b.length;f=[];g=h[e][d];if(g===l)h=o;else{for(;0<d||0<e;){i=h[e][d];k=0<e?h[e-1][d]:g+1;n=0<d?h[e][d-1]:g+1;j=0<e&&0<d?h[e-1][d-1]:g+1;if(k===l||k<i-1)k=g+1;if(n===l||n<i-1)n=g+1;j<i-1&&(j=g+1);k<=n&&k<j?(f.push({status:"added",value:b[e-1]}),e--):(n<k&&n<j?f.push({status:"deleted",value:a[d-1]}):(f.push({status:"retained",value:a[d-1]}),e--),d--)}h=f.reverse()}return h};r.b("ko.utils.compareArrays",r.a.N);
(function(){function a(a){if(2<a.length){for(var b=a[0],f=a[a.length-1],h=[b];b!==f;){b=b.nextSibling;if(!b)return;h.push(b)}Array.prototype.splice.apply(a,[0,a.length].concat(h))}}function b(b,e,f,h){var g=[],b=r.i(function(){var b=e(f)||[];0<g.length&&(a(g),r.a.Ja(g,b),h&&h(f,b));g.splice(0,g.length);r.a.J(g,b)},o,{disposeWhenNodeIsRemoved:b,disposeWhen:function(){return 0==g.length||!r.a.ga(g[0])}});return{sb:g,i:b}}r.a.Ka=function(d,e,f,h,g){for(var e=e||[],h=h||{},i=r.a.e.get(d,"setDomNodeChildrenFromArrayMapping_lastMappingResult")===
l,j=r.a.e.get(d,"setDomNodeChildrenFromArrayMapping_lastMappingResult")||[],k=r.a.ba(j,function(a){return a.Va}),n=r.a.N(k,e),e=[],t=0,q=[],k=[],v=o,s=0,w=n.length;s<w;s++)switch(n[s].status){case "retained":var x=j[t];e.push(x);0<x.O.length&&(v=x.O[x.O.length-1]);t++;break;case "deleted":j[t].i.v();a(j[t].O);r.a.n(j[t].O,function(a){q.push({element:a,index:s,value:n[s].value});v=a});t++;break;case "added":var x=n[s].value,z=b(d,f,x,g),u=z.sb;e.push({Va:n[s].value,O:u,i:z.i});for(var z=0,y=u.length;z<
y;z++){var A=u[z];k.push({element:A,index:s,value:n[s].value});v==o?r.f.xb(d,A):r.f.mb(d,A,v);v=A}g&&g(x,u)}r.a.n(q,function(a){r.F(a.element)});f=p;if(!i){if(h.afterAdd)for(s=0;s<k.length;s++)h.afterAdd(k[s].element,k[s].index,k[s].value);if(h.beforeRemove){for(s=0;s<q.length;s++)h.beforeRemove(q[s].element,q[s].index,q[s].value);f=m}}f||r.a.n(q,function(a){r.removeNode(a.element)});r.a.e.set(d,"setDomNodeChildrenFromArrayMapping_lastMappingResult",e)}})();
r.b("ko.utils.setDomNodeChildrenFromArrayMapping",r.a.Ka);r.p=function(){this.allowTemplateRewriting=p};r.p.prototype=new r.t;r.p.prototype.renderTemplateSource=function(a){a=a.text();return r.a.ma(a)};r.p.M=new r.p;r.pa(r.p.M);r.b("ko.nativeTemplateEngine",r.p);
(function(){r.ja=function(){var a=this.qb=function(){if("undefined"==typeof jQuery||!jQuery.tmpl)return 0;try{if(0<=jQuery.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(d,e,f){f=f||{};2>a&&c(Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later."));var h=d.data("precompiled");h||(h=d.text()||"",h=jQuery.template(o,"{{ko_with $item.koBindingContext}}"+h+"{{/ko_with}}"),d.data("precompiled",h));
d=[e.$data];e=jQuery.extend({koBindingContext:e},f.templateOptions);e=jQuery.tmpl(h,d,e);e.appendTo(document.createElement("div"));jQuery.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){document.write("<script type='text/html' id='"+a+"'>"+b+"<\/script>")};if(0<a)jQuery.tmpl.tag.ko_code={open:"__.push($1 || '');"},jQuery.tmpl.tag.ko_with={open:"with($1) {",close:"} "}};r.ja.prototype=new r.t;
var a=new r.ja;0<a.qb&&r.pa(a);r.b("ko.jqueryTmplTemplateEngine",r.ja)})();
})(window);
/*
backbone-modelref.js 0.1.0
(c) 2011 Kevin Malakoff.
Backbone-ModelRef.js is freely distributable under the MIT license.
See the following for full license details:
https://github.com/kmalakoff/backbone-modelref/blob/master/LICENSE
Dependencies: Backbone.js and Underscore.js.
*/
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
if (!this.Backbone || !this.Backbone.Model) {
throw new Error('Backbone.ModelRef: Dependency alert! Backbone.js must be included before this file');
}
Backbone.ModelRef = (function() {
var MODEL_EVENTS_WHEN_LOADED, MODEL_EVENTS_WHEN_UNLOADED;
MODEL_EVENTS_WHEN_LOADED = ['reset', 'remove'];
MODEL_EVENTS_WHEN_UNLOADED = ['reset', 'add'];
function ModelRef(collection, model_id, cached_model) {
var event, _i, _j, _len, _len2;
this.collection = collection;
this.model_id = model_id;
this.cached_model = cached_model != null ? cached_model : null;
_.bindAll(this, '_checkForLoad', '_checkForUnload');
if (!this.collection) {
throw new Error("Backbone.ModelRef: collection is missing");
}
if (!(this.model_id || this.cached_model)) {
throw new Error("Backbone.ModelRef: model_id and cached_model missing");
}
if (this.collection.retain) {
this.collection.retain();
}
this.cached_model = this.cached_model || this.collection.get(this.model_id);
if (this.cached_model) {
for (_i = 0, _len = MODEL_EVENTS_WHEN_LOADED.length; _i < _len; _i++) {
event = MODEL_EVENTS_WHEN_LOADED[_i];
this.collection.bind(event, this._checkForUnload);
}
} else {
for (_j = 0, _len2 = MODEL_EVENTS_WHEN_UNLOADED.length; _j < _len2; _j++) {
event = MODEL_EVENTS_WHEN_UNLOADED[_j];
this.collection.bind(event, this._checkForLoad);
}
}
this.ref_count = 1;
}
ModelRef.prototype.retain = function() {
this.ref_count++;
return this;
};
ModelRef.prototype.release = function() {
var event, _i, _j, _len, _len2;
if (this.ref_count <= 0) {
throw new Error("Backbone.ModelRef.release(): ref count is corrupt");
}
this.ref_count--;
if (this.ref_count > 0) {
return;
}
if (this.cached_model) {
for (_i = 0, _len = MODEL_EVENTS_WHEN_LOADED.length; _i < _len; _i++) {
event = MODEL_EVENTS_WHEN_LOADED[_i];
this.collection.unbind(event, this._checkForUnload);
}
} else {
for (_j = 0, _len2 = MODEL_EVENTS_WHEN_UNLOADED.length; _j < _len2; _j++) {
event = MODEL_EVENTS_WHEN_UNLOADED[_j];
this.collection.unbind(event, this._checkForLoad);
}
}
if (this.collection.release) {
this.collection.release();
}
this.collection = null;
return this;
};
ModelRef.prototype.get = function(attribute_name) {
if (attribute_name !== 'id') {
throw new Error("Backbone.ModelRef.get(): only id is permitted");
}
if (this.cached_model && !this.cached_model.isNew()) {
this.model_id = this.cached_model.id;
}
return this.model_id;
};
ModelRef.prototype.getModel = function() {
if (this.cached_model && !this.cached_model.isNew()) {
this.model_id = this.cached_model.id;
}
if (this.cached_model) {
return this.cached_model;
}
return this.cached_model = this.collection.get(this.model_id);
};
ModelRef.prototype.isLoaded = function() {
var model;
model = this.getModel();
if (!model) {
return false;
}
if (model.isLoaded) {
return model.isLoaded();
} else {
return true;
}
};
ModelRef.prototype._checkForLoad = function() {
var event, model, _i, _j, _len, _len2;
model = this.collection.get(this.model_id);
if (!model) {
return;
}
if (this.cached_model) {
return;
}
for (_i = 0, _len = MODEL_EVENTS_WHEN_UNLOADED.length; _i < _len; _i++) {
event = MODEL_EVENTS_WHEN_UNLOADED[_i];
this.collection.unbind(event, this._checkForLoad);
}
for (_j = 0, _len2 = MODEL_EVENTS_WHEN_LOADED.length; _j < _len2; _j++) {
event = MODEL_EVENTS_WHEN_LOADED[_j];
this.collection.bind(event, this._checkForUnload);
}
this.cached_model = model;
return this.trigger('loaded', this.cached_model);
};
ModelRef.prototype._checkForUnload = function() {
var event, model, _i, _j, _len, _len2;
model = this.collection.get(this.model_id);
if (model) {
return;
}
if (!this.cached_model) {
return;
}
for (_i = 0, _len = MODEL_EVENTS_WHEN_LOADED.length; _i < _len; _i++) {
event = MODEL_EVENTS_WHEN_LOADED[_i];
this.collection.unbind(event, this._checkForUnload);
}
for (_j = 0, _len2 = MODEL_EVENTS_WHEN_UNLOADED.length; _j < _len2; _j++) {
event = MODEL_EVENTS_WHEN_UNLOADED[_j];
this.collection.bind(event, this._checkForLoad);
}
model = this.cached_model;
this.cached_model = null;
return this.trigger('unloaded', model);
};
return ModelRef;
})();
Backbone.ModelRef.VERSION = '0.1.0';
__extends(Backbone.ModelRef.prototype, Backbone.Events);
\ No newline at end of file
// Underscore.js 1.2.1
// (c) 2011 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** and **"CommonJS"**, with
// backwards-compatibility for the old `require()` API. If we're not in
// CommonJS, add `_` to the global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else if (typeof define === 'function' && define.amd) {
// Register as a named module with AMD.
define('underscore', function() {
return _;
});
} else {
// Exported as a string, for Closure Compiler "advanced" mode.
root['_'] = _;
}
// Current version.
_.VERSION = '1.2.1';
// 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 (hasOwnProperty.call(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 = 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);
});
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 = memo !== void 0;
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) {
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
return _.reduce(reversed, iterator, memo, context);
};
// 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 |= 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) {
if (value === target) return true;
});
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 (method.call ? 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)) 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)) 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) {
if (index == 0) {
shuffled[0] = value;
} else {
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, iterator, context) {
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;
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(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return slice.call(iterable);
if (_.isArguments(iterable)) return slice.call(iterable);
return _.values(iterable);
};
// Return the number of elements in an object.
_.size = function(obj) {
return _.toArray(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`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = 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) {
return (n != null) && !guard ? slice.call(array, array.length - n) : 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 result = [];
_.reduce(initial, function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
memo[memo.length] = el;
result[result.length] = array[i];
}
return memo;
}, []);
return result;
};
// 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 another.
// Only the elements present in just the first array will remain.
_.difference = function(array, other) {
return _.filter(array, function(value){ return !_.include(other, 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 (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 (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 hasOwnProperty.call(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(func, 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 timeout, context, args, throttling, finishThrottle;
finishThrottle = _.debounce(function(){ throttling = false; }, wait);
return function() {
context = this; args = arguments;
var throttler = function() {
timeout = null;
func.apply(context, args);
finishThrottle();
};
if (!timeout) timeout = setTimeout(throttler, wait);
if (!throttling) func.apply(context, args);
if (finishThrottle) finishThrottle();
throttling = true;
};
};
// 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.
_.debounce = function(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
var throttler = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(throttler, 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));
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 = slice.call(arguments);
return function() {
var args = slice.call(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) {
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 (hasOwnProperty.call(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) {
if (source[prop] !== void 0) obj[prop] = source[prop];
}
});
return obj;
};
// 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 (_.isFunction(a.isEqual)) return a.isEqual(b);
if (_.isFunction(b.isEqual)) return b.isEqual(a);
// Compare object types.
var typeA = typeof a;
if (typeA != typeof b) return false;
// Optimization; ensure that both values are truthy or falsy.
if (!a != !b) return false;
// `NaN` values are equal.
if (_.isNaN(a)) return _.isNaN(b);
// Compare string objects by value.
var isStringA = _.isString(a), isStringB = _.isString(b);
if (isStringA || isStringB) return isStringA && isStringB && String(a) == String(b);
// Compare number objects by value.
var isNumberA = _.isNumber(a), isNumberB = _.isNumber(b);
if (isNumberA || isNumberB) return isNumberA && isNumberB && +a == +b;
// Compare boolean objects by value. The value of `true` is 1; the value of `false` is 0.
var isBooleanA = _.isBoolean(a), isBooleanB = _.isBoolean(b);
if (isBooleanA || isBooleanB) return isBooleanA && isBooleanB && +a == +b;
// Compare dates by their millisecond values.
var isDateA = _.isDate(a), isDateB = _.isDate(b);
if (isDateA || isDateB) return isDateA && isDateB && a.getTime() == b.getTime();
// Compare RegExps by their source patterns and flags.
var isRegExpA = _.isRegExp(a), isRegExpB = _.isRegExp(b);
if (isRegExpA || isRegExpB) {
// Ensure commutative equality for RegExps.
return isRegExpA && isRegExpB &&
a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
// Ensure that both values are objects.
if (typeA != 'object') return false;
// Arrays or Arraylikes with different lengths are not equal.
if (a.length !== b.length) return false;
// Objects with different constructors are not equal.
if (a.constructor !== b.constructor) 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;
// Deep compare objects.
for (var key in a) {
if (hasOwnProperty.call(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = hasOwnProperty.call(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 (hasOwnProperty.call(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 (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (hasOwnProperty.call(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?
if (toString.call(arguments) == '[object Arguments]') {
_.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
} else {
_.isArguments = function(obj) {
return !!(obj && hasOwnProperty.call(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 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;
};
// 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(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
};
// 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
};
// 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(str, data) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.escape, function(match, code) {
return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
})
.replace(c.interpolate, function(match, code) {
return "'," + code.replace(/\\'/g, "'") + ",'";
})
.replace(c.evaluate || null, function(match, code) {
return "');" + code.replace(/\\'/g, "'")
.replace(/[\r\n\t]/g, ' ') + "__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ "');}return __p.join('');";
var func = new Function('obj', tmpl);
return data ? func(data) : func;
};
// 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() {
method.apply(this._wrapped, arguments);
return result(this._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;
};
})();
<!DOCTYPE html>
<html>
<!-- Support Localization -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<head>
<title>Knockback Demo: Todos</title>
<!-- Demo Dependencies -->
<script src="../vendor/json2.js"></script>
<script src="../vendor/jquery-1.7.min.js"></script>
<script src="../vendor/jquery.tmpl.min.js"></script>
<script src="../vendor/globalize/globalize.js"></script>
<script src="../vendor/globalize/globalize.culture.fr-FR.js"></script>
<script src="../vendor/globalize/globalize.culture.it-IT.js"></script>
<script src="../vendor/mColorPicker.min.js"></script>
<script type="text/javascript">
$.fn.mColorPicker.init.showLogo = false;
$.fn.mColorPicker.init.replace = false;
</script>
<!-- Knockback Dependencies -->
<script src="dependencies/underscore.js"></script>
<script src="dependencies/backbone.js"></script>
<script src="dependencies/optional/backbone-modelref.js"></script>
<script src="dependencies/knockout-2.0.0.js"></script>
<script src="dependencies/knockback.js"></script>
<!-- Demo Dependencies -->
<script src="../vendor/backbone-localstorage.js"></script>
<!-- The Demo -->
<link href="../css/todos.css" media="all" rel="stylesheet" type="text/css"/>
<script src="todos_locale_manager.js"></script>
<script src="todos_extended.js"></script>
</head>
<body>
<!-- Todo App Interface -->
<div id="todoapp">
<!-- Header and Settings Section -->
<div id="todo-header">
<div class="title"><h1>Todos</h1></div>
<div id="color-settings" data-bind="template: {name: 'priority-setting-template', foreach: window.settings_view_model.priority_settings}"></div>
</div>
<!-- Create Todo Section -->
<div id="todo-create">
<div class="content">
<div id="create-todo">
<input id="new-todo" type="text" data-bind="value: create.input_text, placeholder: create.input_placeholder_text, event: {keyup: create.addTodo}"/>
<span class="ui-tooltip-top" style="display:none;" data-bind="text: create.input_tooltip_text"></span>
<div data-bind="template: {name: 'priority-swatch-picker-template', data: create}"></div>
</div>
</div>
</div>
<!-- Todos List Section -->
<div id="todo-list">
<div class="todo-list-sorting-wrapper-outer" data-bind="visible: todo_list.sort_visible">
<div class="todo-list-sorting-wrapper-inner">
<div id="todo-list-sorting" class="selection codestyle" data-bind="template: {name: 'option-template', foreach: todo_list.sorting_options, templateOptions: {selected_value: todo_list.selected_value} }"></div>
</div>
</div>
<ul class="todo-list" data-bind="template: {name: 'item-template', foreach: todo_list.todos}"></ul>
</div>
<!-- Stats Section -->
<div id="todo-stats">
<span class="todo-count" data-bind="text: stats.remaining_text"></span>
<span class="todo-clear"> <a href="#" data-bind="text: stats.clear_text, click: stats.onDestroyDone"></a></span>
</div>
<!-- Footer Section -->
<div id="todo-footer">
<ul id="instructions">
<li data-bind="text: footer.instructions_text"></li>
</ul>
<div id="todo-languages" class="selection codestyle" data-bind="template: {name: 'option-template', foreach: footer.language_options, templateOptions: {selected_value: footer.selected_value} }"></div>
</div>
</div>
<div id="credits">
Created by
<br />
<a href="http://jgn.me/">J&eacute;r&ocirc;me Gravel-Niquet</a>
<br />
<br />
Modified to use <a href="https://github.com/kmalakoff/knockback">Knockback.js</a> by
<br />
<a href="https://github.com/kmalakoff">Kevin Malakoff</a>
<br />
and
<br />
<a href="https://github.com/yuchi">Pier Paolo Ramon</a>
</div>
<!-- Templates -->
<script type="text/x-jquery-tmpl" id="item-template">
<li>
<div class="todo" data-bind="css: {done: done, done_enhanced: done, editing: edit_mode}">
<div class="display">
<input class="check" type="checkbox" data-bind="checked: done" />
<div data-bind="template: {name: 'priority-swatch-picker-template', data: $data}"></div>
<div class="todo-text" data-bind="text: text, dblclick: toggleEditMode"></div>
<div class="todo-done-text" data-bind="text: done_text"></div>
<div class="todo-destroy" data-bind="click: destroyTodo"></div>
</div>
<div class="edit">
<input class="todo-input" type="text" data-bind="value: text, event: {keyup: onEnterEndEdit}" />
</div>
</div>
</li>
</script>
<script type="text/x-jquery-tmpl" id="priority-setting-template">
<div class="priority-color-entry">
<div class="priority-text" data-bind="text: priority_text"></div>
<input data-bind="attr: {id: priority}, value: priority_color" class='priority-color-swatch colorpicker' data-text="hidden" data-hex="true"/>
</div>
</script>
<script type="text/x-jquery-tmpl" id="priority-swatch-picker-template">
<div class="priority-color-swatch todo create" data-bind="style: {background: priority_color}, click: onToggleTooltip">
<span class="priority-picker-tooltip ui-tooltip-top" data-bind="visible: tooltip_visible">
<div data-bind="template: {name: 'priority-picker-template', foreach: window.settings_view_model.priority_settings, templateOptions: {onSelectPriority: onSelectPriority} }"></div>
</span>
</div>
</script>
<script type="text/x-jquery-tmpl" id="priority-picker-template">
<div class="priority-color-entry">
<div class="priority-text" data-bind="text: priority_text"></div>
<div class='priority-color-swatch' data-bind="style: {background: priority_color}, click: $item.onSelectPriority"></div>
</div>
</script>
<script type="text/x-jquery-tmpl" id="option-template">
<div class="option"><input type="radio" data-bind="attr: {id: id, name: option_group}, value: id, checked: $item.selected_value"><label data-bind="attr: {for: id}, text: label"></label></div>
</script>
</body>
</html>
###
knockback-todos.js
(c) 2011 Kevin Malakoff.
Knockback-Todos is freely distributable under the MIT license.
See the following for full license details:
https:#github.com/kmalakoff/knockback-todos/blob/master/LICENSE
###
$(document).ready(->
###################################
# Knockback-powered enhancements - BEGIN
###################################
###################################
# Configure
# set the language
kb.locale_manager.setLocale('en')
kb.locale_change_observable = kb.triggeredObservable(kb.locale_manager, 'change') # use to register a localization dependency
# add a doubleclick and placeholder handlers to KO
ko.bindingHandlers.dblclick =
init: (element, value_accessor, all_bindings_accessor, view_model) ->
$(element).dblclick(ko.utils.unwrapObservable(value_accessor()))
ko.bindingHandlers.placeholder =
update: (element, value_accessor, all_bindings_accessor, view_model) ->
$(element).attr('placeholder', ko.utils.unwrapObservable(value_accessor()))
# ko1.2.1 compatibility with 1.3
if _.isUndefined(ko.templateSources)
_ko_native_apply_bindings = ko.applyBindings
ko.applyBindings = (view_model, element) ->
view_model['$data'] = view_model
_ko_native_apply_bindings(view_model, element)
###################################
# Model: http://en.wikipedia.org/wiki/Model_view_controller
# ORM: http://en.wikipedia.org/wiki/Object-relational_mapping
###################################
# Settings
class PrioritySetting extends Backbone.Model
class PrioritiesSettingList extends Backbone.Collection
model: PrioritySetting
localStorage: new Store("kb_priorities") # Save all of the todo items under the `"kb_priorities"` namespace.
priorities = new PrioritiesSettingList()
###################################
# MVVM: http://en.wikipedia.org/wiki/Model_View_ViewModel
###################################
# Localization
LanguageOptionViewModel = (locale) ->
@id = locale
@label = kb.locale_manager.localeToLabel(locale)
@option_group = 'lang'
@
# Settings
PrioritySettingsViewModel = (model) ->
@priority = model.get('id')
@priority_text = kb.observable(kb.locale_manager, {key: @priority})
@priority_color = kb.observable(model, {key: 'color'})
@
SettingsViewModel = (priority_settings) ->
@priority_settings = ko.observableArray(_.map(priority_settings, (model)-> return new PrioritySettingsViewModel(model)))
@getColorByPriority = (priority) ->
@createColorsDependency()
(return view_model.priority_color() if view_model.priority == priority) for view_model in @priority_settings()
return ''
@createColorsDependency = => view_model.priority_color() for view_model in @priority_settings()
@default_priority = ko.observable('medium')
@default_priority_color = ko.dependentObservable(=> return @getColorByPriority(@default_priority()))
@priorityToRank = (priority) ->
switch priority
when 'high' then return 0
when 'medium' then return 1
when 'low' then return 2
@
# Content
SortingOptionViewModel = (string_id) ->
@id = string_id
@label = kb.observable(kb.locale_manager, {key: string_id})
@option_group = 'list_sort'
@
###################################
# Knockback-powered enhancements - END
###################################
###################################
# Model: http://en.wikipedia.org/wiki/Model_view_controller
# ORM: http://en.wikipedia.org/wiki/Object-relational_mapping
###################################
# Todos
class Todo extends Backbone.Model
defaults: -> return {created_at: new Date()}
set: (attrs) ->
# note: this is to convert between Dates as JSON strings and Date objects. To automate this, take a look at Backbone.Articulation: https://github.com/kmalakoff/backbone-articulation
attrs['done_at'] = new Date(attrs['done_at']) if attrs and attrs.hasOwnProperty('done_at') and _.isString(attrs['done_at'])
attrs['created_at'] = new Date(attrs['created_at']) if attrs and attrs.hasOwnProperty('created_at') and _.isString(attrs['created_at'])
super
done: (done) ->
return !!@get('done_at') if arguments.length == 0
@save({done_at: if done then new Date() else null})
class TodoList extends Backbone.Collection
model: Todo
localStorage: new Store("kb_todos") # Save all of the todo items under the `"kb_todos"` namespace.
doneCount: -> @models.reduce(((prev,cur)-> return prev + if !!cur.get('done_at') then 1 else 0), 0)
remainingCount: -> @models.length - @doneCount()
allDone: -> return @filter((todo) -> return !!todo.get('done_at'))
todos = new TodoList()
todos.fetch()
###################################
# MVVM: http://en.wikipedia.org/wiki/Model_View_ViewModel
###################################
CreateTodoViewModel = ->
@input_text = ko.observable('')
@input_placeholder_text = kb.observable(kb.locale_manager, {key: 'placeholder_create'})
@input_tooltip_text = kb.observable(kb.locale_manager, {key: 'tooltip_create'})
@addTodo = (view_model, event) ->
text = @create.input_text()
return true if (!text || event.keyCode != 13)
todos.create({text: text, priority: window.settings_view_model.default_priority()})
@create.input_text('')
@priority_color = ko.dependentObservable(-> return window.settings_view_model.default_priority_color())
@tooltip_visible = ko.observable(false)
tooltip_visible = @tooltip_visible # closured for onSelectPriority
@onSelectPriority = (view_model, event) ->
event.stopPropagation()
tooltip_visible(false)
window.settings_view_model.default_priority(ko.utils.unwrapObservable(@priority))
@onToggleTooltip = => @tooltip_visible(!@tooltip_visible())
@
# Content
TodoViewModel = (model) ->
@text = kb.observable(model, {key: 'text', write: ((text) -> model.save({text: text}))}, this)
@edit_mode = ko.observable(false)
@toggleEditMode = => @edit_mode(!@edit_mode()) if not @done()
@onEnterEndEdit = (view_model, event) => @edit_mode(false) if (event.keyCode == 13)
@created_at = model.get('created_at')
@done = kb.observable(model, {key: 'done_at', read: (-> return model.done()), write: ((done) -> model.done(done)) }, this)
@done_at = kb.observable(model, {key: 'done_at', localizer: LongDateLocalizer})
@done_text = ko.dependentObservable(=>
done_at = @done_at() # ensure there is a dependency
return if !!done_at then return "#{kb.locale_manager.get('label_completed')}: #{done_at}" else ''
)
@priority_color = kb.observable(model, {key: 'priority', read: -> return window.settings_view_model.getColorByPriority(model.get('priority'))})
@tooltip_visible = ko.observable(false)
tooltip_visible = @tooltip_visible # closured for onSelectPriority
@onSelectPriority = (view_model, event) ->
event.stopPropagation()
tooltip_visible(false)
model.save({priority: ko.utils.unwrapObservable(@priority)})
@onToggleTooltip = => @tooltip_visible(!@tooltip_visible())
@destroyTodo = => model.destroy()
@
TodoListViewModel = (todos) ->
@todos = ko.observableArray([])
@sort_mode = ko.observable('label_text') # used to create a dependency
@sorting_options = [new SortingOptionViewModel('label_text'), new SortingOptionViewModel('label_created'), new SortingOptionViewModel('label_priority')]
@selected_value = ko.dependentObservable(
read: => return @sort_mode()
write: (new_mode) =>
@sort_mode(new_mode)
# update the collection observable's sorting function
switch new_mode
when 'label_text' then @collection_observable.sortAttribute('text')
when 'label_created' then @collection_observable.sortedIndex((models, model)-> return _.sortedIndex(models, model, (test) -> test.get('created_at').valueOf()))
when 'label_priority' then @collection_observable.sortedIndex((models, model)-> return _.sortedIndex(models, model, (test) -> window.settings_view_model.priorityToRank(test.get('priority'))))
owner: this
)
@collection_observable = kb.collectionObservable(todos, @todos, {view_model: TodoViewModel, sort_attribute: 'text'})
@sort_visible = ko.dependentObservable(=> @collection_observable().length)
@
FooterViewModel = (locales) ->
@instructions_text = kb.observable(kb.locale_manager, {key: 'instructions'})
@current_language = ko.observable(kb.locale_manager.getLocale()) # used to create a dependency
@language_options = ko.observableArray(_.map(locales, (locale) -> return new LanguageOptionViewModel(locale)))
@selected_value = ko.dependentObservable(
read: => return @current_language()
write: (new_locale) => kb.locale_manager.setLocale(new_locale); @current_language(new_locale)
owner: this
)
@
###################################
# Knockback-powered enhancements - BEGIN
###################################
# Stats Footer
StatsViewModel = (todos) ->
@co = kb.collectionObservable(todos)
@remaining_text_key = ko.dependentObservable(=> return if (@co.collection().remainingCount()==0) then null else (if (todos.remainingCount() == 1) then 'remaining_template_s' else 'remaining_template_pl'))
@remaining_text = kb.observable(kb.locale_manager, {key: @remaining_text_key, args: => @co.collection().remainingCount()})
@clear_text_key = ko.dependentObservable(=> return if (@co.collection().doneCount()==0) then null else (if (todos.doneCount() == 1) then 'remaining_template_s' else 'remaining_template_pl'))
@clear_text = kb.observable(kb.locale_manager, {key: @clear_text_key, args: => @co.collection().doneCount()})
@onDestroyDone = => model.destroy() for model in todos.allDone()
@
# set up and bind the application view model
window.settings_view_model = new SettingsViewModel([
new Backbone.ModelRef(priorities, 'high'),
new Backbone.ModelRef(priorities, 'medium'),
new Backbone.ModelRef(priorities, 'low')
])
app_view_model =
create: new CreateTodoViewModel()
todo_list: new TodoListViewModel(todos)
footer: new FooterViewModel(kb.locale_manager.getLocales())
stats: new StatsViewModel(todos)
ko.applyBindings(app_view_model, $('#todoapp')[0])
# Destroy when finished with the view models
# kb.vmRelease(window.settings_view_model)
# kb.vmRelease(app_view_model)
###################################
# Load the prioties late to show the dynamic nature of Knockback with Backbone.ModelRef
_.delay((->
priorities.fetch(
success: (collection) ->
collection.create({id:'high', color:'#bf30ff'}) if not collection.get('high')
collection.create({id:'medium', color:'#98acff'}) if not collection.get('medium')
collection.create({id:'low', color:'#38ff6a'}) if not collection.get('low')
)
# set up color pickers
$('.colorpicker').mColorPicker({imageFolder: '../css/images/'})
$('.colorpicker').bind('colorpicked', ->
model = priorities.get($(this).attr('id'))
model.save({color: $(this).val()}) if model
)
), 1000)
###################################
# Knockback-powered enhancements - END
###################################
)
\ No newline at end of file
/*
knockback-todos.js
(c) 2011 Kevin Malakoff.
Knockback-Todos is freely distributable under the MIT license.
See the following for full license details:
https:#github.com/kmalakoff/knockback-todos/blob/master/LICENSE
*/
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
}, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
$(document).ready(function() {
var CreateTodoViewModel, FooterViewModel, LanguageOptionViewModel, PrioritiesSettingList, PrioritySetting, PrioritySettingsViewModel, SettingsViewModel, SortingOptionViewModel, StatsViewModel, Todo, TodoList, TodoListViewModel, TodoViewModel, app_view_model, priorities, todos, _ko_native_apply_bindings;
kb.locale_manager.setLocale('en');
kb.locale_change_observable = kb.triggeredObservable(kb.locale_manager, 'change');
ko.bindingHandlers.dblclick = {
init: function(element, value_accessor, all_bindings_accessor, view_model) {
return $(element).dblclick(ko.utils.unwrapObservable(value_accessor()));
}
};
ko.bindingHandlers.placeholder = {
update: function(element, value_accessor, all_bindings_accessor, view_model) {
return $(element).attr('placeholder', ko.utils.unwrapObservable(value_accessor()));
}
};
if (_.isUndefined(ko.templateSources)) {
_ko_native_apply_bindings = ko.applyBindings;
ko.applyBindings = function(view_model, element) {
view_model['$data'] = view_model;
return _ko_native_apply_bindings(view_model, element);
};
}
PrioritySetting = (function() {
__extends(PrioritySetting, Backbone.Model);
function PrioritySetting() {
PrioritySetting.__super__.constructor.apply(this, arguments);
}
return PrioritySetting;
})();
PrioritiesSettingList = (function() {
__extends(PrioritiesSettingList, Backbone.Collection);
function PrioritiesSettingList() {
PrioritiesSettingList.__super__.constructor.apply(this, arguments);
}
PrioritiesSettingList.prototype.model = PrioritySetting;
PrioritiesSettingList.prototype.localStorage = new Store("kb_priorities");
return PrioritiesSettingList;
})();
priorities = new PrioritiesSettingList();
LanguageOptionViewModel = function(locale) {
this.id = locale;
this.label = kb.locale_manager.localeToLabel(locale);
this.option_group = 'lang';
return this;
};
PrioritySettingsViewModel = function(model) {
this.priority = model.get('id');
this.priority_text = kb.observable(kb.locale_manager, {
key: this.priority
});
this.priority_color = kb.observable(model, {
key: 'color'
});
return this;
};
SettingsViewModel = function(priority_settings) {
this.priority_settings = ko.observableArray(_.map(priority_settings, function(model) {
return new PrioritySettingsViewModel(model);
}));
this.getColorByPriority = function(priority) {
var view_model, _i, _len, _ref;
this.createColorsDependency();
_ref = this.priority_settings();
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
view_model = _ref[_i];
if (view_model.priority === priority) {
return view_model.priority_color();
}
}
return '';
};
this.createColorsDependency = __bind(function() {
var view_model, _i, _len, _ref, _results;
_ref = this.priority_settings();
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
view_model = _ref[_i];
_results.push(view_model.priority_color());
}
return _results;
}, this);
this.default_priority = ko.observable('medium');
this.default_priority_color = ko.dependentObservable(__bind(function() {
return this.getColorByPriority(this.default_priority());
}, this));
this.priorityToRank = function(priority) {
switch (priority) {
case 'high':
return 0;
case 'medium':
return 1;
case 'low':
return 2;
}
};
return this;
};
SortingOptionViewModel = function(string_id) {
this.id = string_id;
this.label = kb.observable(kb.locale_manager, {
key: string_id
});
this.option_group = 'list_sort';
return this;
};
Todo = (function() {
__extends(Todo, Backbone.Model);
function Todo() {
Todo.__super__.constructor.apply(this, arguments);
}
Todo.prototype.defaults = function() {
return {
created_at: new Date()
};
};
Todo.prototype.set = function(attrs) {
if (attrs && attrs.hasOwnProperty('done_at') && _.isString(attrs['done_at'])) {
attrs['done_at'] = new Date(attrs['done_at']);
}
if (attrs && attrs.hasOwnProperty('created_at') && _.isString(attrs['created_at'])) {
attrs['created_at'] = new Date(attrs['created_at']);
}
return Todo.__super__.set.apply(this, arguments);
};
Todo.prototype.done = function(done) {
if (arguments.length === 0) {
return !!this.get('done_at');
}
return this.save({
done_at: done ? new Date() : null
});
};
return Todo;
})();
TodoList = (function() {
__extends(TodoList, Backbone.Collection);
function TodoList() {
TodoList.__super__.constructor.apply(this, arguments);
}
TodoList.prototype.model = Todo;
TodoList.prototype.localStorage = new Store("kb_todos");
TodoList.prototype.doneCount = function() {
return this.models.reduce((function(prev, cur) {
return prev + (!!cur.get('done_at') ? 1 : 0);
}), 0);
};
TodoList.prototype.remainingCount = function() {
return this.models.length - this.doneCount();
};
TodoList.prototype.allDone = function() {
return this.filter(function(todo) {
return !!todo.get('done_at');
});
};
return TodoList;
})();
todos = new TodoList();
todos.fetch();
CreateTodoViewModel = function() {
var tooltip_visible;
this.input_text = ko.observable('');
this.input_placeholder_text = kb.observable(kb.locale_manager, {
key: 'placeholder_create'
});
this.input_tooltip_text = kb.observable(kb.locale_manager, {
key: 'tooltip_create'
});
this.addTodo = function(view_model, event) {
var text;
text = this.create.input_text();
if (!text || event.keyCode !== 13) {
return true;
}
todos.create({
text: text,
priority: window.settings_view_model.default_priority()
});
return this.create.input_text('');
};
this.priority_color = ko.dependentObservable(function() {
return window.settings_view_model.default_priority_color();
});
this.tooltip_visible = ko.observable(false);
tooltip_visible = this.tooltip_visible;
this.onSelectPriority = function(view_model, event) {
event.stopPropagation();
tooltip_visible(false);
return window.settings_view_model.default_priority(ko.utils.unwrapObservable(this.priority));
};
this.onToggleTooltip = __bind(function() {
return this.tooltip_visible(!this.tooltip_visible());
}, this);
return this;
};
TodoViewModel = function(model) {
var tooltip_visible;
this.text = kb.observable(model, {
key: 'text',
write: (function(text) {
return model.save({
text: text
});
})
}, this);
this.edit_mode = ko.observable(false);
this.toggleEditMode = __bind(function() {
if (!this.done()) {
return this.edit_mode(!this.edit_mode());
}
}, this);
this.onEnterEndEdit = __bind(function(view_model, event) {
if (event.keyCode === 13) {
return this.edit_mode(false);
}
}, this);
this.created_at = model.get('created_at');
this.done = kb.observable(model, {
key: 'done_at',
read: (function() {
return model.done();
}),
write: (function(done) {
return model.done(done);
})
}, this);
this.done_at = kb.observable(model, {
key: 'done_at',
localizer: LongDateLocalizer
});
this.done_text = ko.dependentObservable(__bind(function() {
var done_at;
done_at = this.done_at();
if (!!done_at) {
return "" + (kb.locale_manager.get('label_completed')) + ": " + done_at;
} else {
return '';
}
}, this));
this.priority_color = kb.observable(model, {
key: 'priority',
read: function() {
return window.settings_view_model.getColorByPriority(model.get('priority'));
}
});
this.tooltip_visible = ko.observable(false);
tooltip_visible = this.tooltip_visible;
this.onSelectPriority = function(view_model, event) {
event.stopPropagation();
tooltip_visible(false);
return model.save({
priority: ko.utils.unwrapObservable(this.priority)
});
};
this.onToggleTooltip = __bind(function() {
return this.tooltip_visible(!this.tooltip_visible());
}, this);
this.destroyTodo = __bind(function() {
return model.destroy();
}, this);
return this;
};
TodoListViewModel = function(todos) {
this.todos = ko.observableArray([]);
this.sort_mode = ko.observable('label_text');
this.sorting_options = [new SortingOptionViewModel('label_text'), new SortingOptionViewModel('label_created'), new SortingOptionViewModel('label_priority')];
this.selected_value = ko.dependentObservable({
read: __bind(function() {
return this.sort_mode();
}, this),
write: __bind(function(new_mode) {
this.sort_mode(new_mode);
switch (new_mode) {
case 'label_text':
return this.collection_observable.sortAttribute('text');
case 'label_created':
return this.collection_observable.sortedIndex(function(models, model) {
return _.sortedIndex(models, model, function(test) {
return test.get('created_at').valueOf();
});
});
case 'label_priority':
return this.collection_observable.sortedIndex(function(models, model) {
return _.sortedIndex(models, model, function(test) {
return window.settings_view_model.priorityToRank(test.get('priority'));
});
});
}
}, this),
owner: this
});
this.collection_observable = kb.collectionObservable(todos, this.todos, {
view_model: TodoViewModel,
sort_attribute: 'text'
});
this.sort_visible = ko.dependentObservable(__bind(function() {
return this.collection_observable().length;
}, this));
return this;
};
FooterViewModel = function(locales) {
this.instructions_text = kb.observable(kb.locale_manager, {
key: 'instructions'
});
this.current_language = ko.observable(kb.locale_manager.getLocale());
this.language_options = ko.observableArray(_.map(locales, function(locale) {
return new LanguageOptionViewModel(locale);
}));
this.selected_value = ko.dependentObservable({
read: __bind(function() {
return this.current_language();
}, this),
write: __bind(function(new_locale) {
kb.locale_manager.setLocale(new_locale);
return this.current_language(new_locale);
}, this),
owner: this
});
return this;
};
StatsViewModel = function(todos) {
this.co = kb.collectionObservable(todos);
this.remaining_text_key = ko.dependentObservable(__bind(function() {
if (this.co.collection().remainingCount() === 0) {
return null;
} else {
if (todos.remainingCount() === 1) {
return 'remaining_template_s';
} else {
return 'remaining_template_pl';
}
}
}, this));
this.remaining_text = kb.observable(kb.locale_manager, {
key: this.remaining_text_key,
args: __bind(function() {
return this.co.collection().remainingCount();
}, this)
});
this.clear_text_key = ko.dependentObservable(__bind(function() {
if (this.co.collection().doneCount() === 0) {
return null;
} else {
if (todos.doneCount() === 1) {
return 'remaining_template_s';
} else {
return 'remaining_template_pl';
}
}
}, this));
this.clear_text = kb.observable(kb.locale_manager, {
key: this.clear_text_key,
args: __bind(function() {
return this.co.collection().doneCount();
}, this)
});
this.onDestroyDone = __bind(function() {
var model, _i, _len, _ref, _results;
_ref = todos.allDone();
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
model = _ref[_i];
_results.push(model.destroy());
}
return _results;
}, this);
return this;
};
window.settings_view_model = new SettingsViewModel([new Backbone.ModelRef(priorities, 'high'), new Backbone.ModelRef(priorities, 'medium'), new Backbone.ModelRef(priorities, 'low')]);
app_view_model = {
create: new CreateTodoViewModel(),
todo_list: new TodoListViewModel(todos),
footer: new FooterViewModel(kb.locale_manager.getLocales()),
stats: new StatsViewModel(todos)
};
ko.applyBindings(app_view_model, $('#todoapp')[0]);
return _.delay((function() {
priorities.fetch({
success: function(collection) {
if (!collection.get('high')) {
collection.create({
id: 'high',
color: '#bf30ff'
});
}
if (!collection.get('medium')) {
collection.create({
id: 'medium',
color: '#98acff'
});
}
if (!collection.get('low')) {
return collection.create({
id: 'low',
color: '#38ff6a'
});
}
}
});
$('.colorpicker').mColorPicker({
imageFolder: '../css/images/'
});
return $('.colorpicker').bind('colorpicked', function() {
var model;
model = priorities.get($(this).attr('id'));
if (model) {
return model.save({
color: $(this).val()
});
}
});
}), 1000);
});
\ No newline at end of file
###
knockback-todos.js
(c) 2011 Kevin Malakoff.
Knockback-Todos is freely distributable under the MIT license.
See the following for full license details:
https:#github.com/kmalakoff/knockback-todos/blob/master/LICENSE
###
# Localization
class LocaleManager
constructor: (locale_identifier, @translations_by_locale) ->
@setLocale(locale_identifier) if locale_identifier
get: (string_id, parameters) ->
return '' if not string_id
culture_map = @translations_by_locale[@locale_identifier] if @locale_identifier
return '' if not culture_map
string = if culture_map.hasOwnProperty(string_id) then culture_map[string_id] else ''
return string if arguments.length == 1
return Knockback.toFormattedString.apply(null, [string].concat(Array.prototype.slice.call(arguments, 1)))
getLocale: -> return @locale_identifier
setLocale: (locale_identifier) ->
@locale_identifier = locale_identifier
Globalize.culture = Globalize.findClosestCulture(locale_identifier)
return if !window.Backbone
@trigger('change', this)
culture_map = @translations_by_locale[@locale_identifier]
return if not culture_map
@trigger("change:#{key}", value) for key, value of culture_map
getLocales: ->
locales = []
locales.push(string_id) for string_id, value of @translations_by_locale
return locales
localeToLabel: (locale) ->
locale_parts = locale.split('-')
return locale_parts[locale_parts.length-1].toUpperCase()
localizeDate: (date) -> Globalize.format(date, Globalize.cultures[@locale_identifier].calendars.standard.patterns.f, @locale_identifier)
#######################################
# Mix in Backbone.Events so callers can subscribe
#######################################
LocaleManager.prototype extends Backbone.Events if !!window.Backbone
#######################################
# Set up strings
#######################################
kb ||(kb={})
kb.locale_manager = new LocaleManager(null, {
'en':
placeholder_create: 'What needs to be done?'
tooltip_create: 'Press Enter to save this task'
label_text: 'Name'
label_created: 'Created'
label_priority: 'Priority'
label_completed: 'Completed'
instructions: 'Double-click to edit a todo.'
high: 'high'
medium: 'medium'
low: 'low'
remaining_template_s: '{0} item remaining'
remaining_template_pl:'{0} items remaining'
clear_template_s: 'Clear {0} completed item'
clear_template_pl: 'Clear {0} completed items'
'fr-FR':
placeholder_create: 'Que faire?'
tooltip_create: 'Appuyez sur Enter pour enregistrer cette tâche'
label_text: 'Nom'
label_created: 'Création'
label_priority: 'Priorité'
label_completed: 'Complété'
instructions: 'Double-cliquez pour modifier un todo.'
high: 'haute'
medium: 'moyen'
low: 'bas'
remaining_template_s: '{0} point restant'
remaining_template_pl:'{0} éléments restants'
clear_template_s: 'Retirer {0} point terminée'
clear_template_pl: 'Retirer les {0} éléments terminés'
'it-IT':
placeholder_create: 'Cosa fare?'
tooltip_create: 'Premere Enter per salvare questo compito'
label_text: 'Nome'
label_created: 'Creato'
label_priority: 'Priorità'
label_completed: 'Completato'
instructions: 'Fare doppio clic per modificare una delle cose da fare.'
high: 'alto'
medium: 'medio'
low: 'basso'
remaining_template_s: '{0} elemento restante'
remaining_template_pl:'{0} elementi rimanenti'
clear_template_s: 'Rimuovere {0} elemento completato'
clear_template_pl: 'Rimuovere {0} elementi completato'
})
#######################################
# Date localizer
#######################################
if !!window.Backbone
class LongDateLocalizer extends kb.LocalizedObservable
constructor: -> return super
read: (value) ->
return Globalize.format(value, Globalize.cultures[kb.locale_manager.getLocale()].calendars.standard.patterns.f, kb.locale_manager.getLocale())
write: (localized_string, value, observable) ->
new_value = Globalize.parseDate(localized_string, Globalize.cultures[kb.locale_manager.getLocale()].calendars.standard.patterns.d, kb.locale_manager.getLocale())
value.setTime(new_value.valueOf())
/*
knockback-todos.js
(c) 2011 Kevin Malakoff.
Knockback-Todos is freely distributable under the MIT license.
See the following for full license details:
https:#github.com/kmalakoff/knockback-todos/blob/master/LICENSE
*/
var LocaleManager, LongDateLocalizer, kb;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
LocaleManager = (function() {
function LocaleManager(locale_identifier, translations_by_locale) {
this.translations_by_locale = translations_by_locale;
if (locale_identifier) {
this.setLocale(locale_identifier);
}
}
LocaleManager.prototype.get = function(string_id, parameters) {
var culture_map, string;
if (!string_id) {
return '';
}
if (this.locale_identifier) {
culture_map = this.translations_by_locale[this.locale_identifier];
}
if (!culture_map) {
return '';
}
string = culture_map.hasOwnProperty(string_id) ? culture_map[string_id] : '';
if (arguments.length === 1) {
return string;
}
return Knockback.toFormattedString.apply(null, [string].concat(Array.prototype.slice.call(arguments, 1)));
};
LocaleManager.prototype.getLocale = function() {
return this.locale_identifier;
};
LocaleManager.prototype.setLocale = function(locale_identifier) {
var culture_map, key, value, _results;
this.locale_identifier = locale_identifier;
Globalize.culture = Globalize.findClosestCulture(locale_identifier);
if (!window.Backbone) {
return;
}
this.trigger('change', this);
culture_map = this.translations_by_locale[this.locale_identifier];
if (!culture_map) {
return;
}
_results = [];
for (key in culture_map) {
value = culture_map[key];
_results.push(this.trigger("change:" + key, value));
}
return _results;
};
LocaleManager.prototype.getLocales = function() {
var locales, string_id, value, _ref;
locales = [];
_ref = this.translations_by_locale;
for (string_id in _ref) {
value = _ref[string_id];
locales.push(string_id);
}
return locales;
};
LocaleManager.prototype.localeToLabel = function(locale) {
var locale_parts;
locale_parts = locale.split('-');
return locale_parts[locale_parts.length - 1].toUpperCase();
};
LocaleManager.prototype.localizeDate = function(date) {
return Globalize.format(date, Globalize.cultures[this.locale_identifier].calendars.standard.patterns.f, this.locale_identifier);
};
return LocaleManager;
})();
if (!!window.Backbone) {
__extends(LocaleManager.prototype, Backbone.Events);
}
kb || (kb = {});
kb.locale_manager = new LocaleManager(null, {
'en': {
placeholder_create: 'What needs to be done?',
tooltip_create: 'Press Enter to save this task',
label_text: 'Name',
label_created: 'Created',
label_priority: 'Priority',
label_completed: 'Completed',
instructions: 'Double-click to edit a todo.',
high: 'high',
medium: 'medium',
low: 'low',
remaining_template_s: '{0} item remaining',
remaining_template_pl: '{0} items remaining',
clear_template_s: 'Clear {0} completed item',
clear_template_pl: 'Clear {0} completed items'
},
'fr-FR': {
placeholder_create: 'Que faire?',
tooltip_create: 'Appuyez sur Enter pour enregistrer cette tâche',
label_text: 'Nom',
label_created: 'Création',
label_priority: 'Priorité',
label_completed: 'Complété',
instructions: 'Double-cliquez pour modifier un todo.',
high: 'haute',
medium: 'moyen',
low: 'bas',
remaining_template_s: '{0} point restant',
remaining_template_pl: '{0} éléments restants',
clear_template_s: 'Retirer {0} point terminée',
clear_template_pl: 'Retirer les {0} éléments terminés'
},
'it-IT': {
placeholder_create: 'Cosa fare?',
tooltip_create: 'Premere Enter per salvare questo compito',
label_text: 'Nome',
label_created: 'Creato',
label_priority: 'Priorità',
label_completed: 'Completato',
instructions: 'Fare doppio clic per modificare una delle cose da fare.',
high: 'alto',
medium: 'medio',
low: 'basso',
remaining_template_s: '{0} elemento restante',
remaining_template_pl: '{0} elementi rimanenti',
clear_template_s: 'Rimuovere {0} elemento completato',
clear_template_pl: 'Rimuovere {0} elementi completato'
}
});
if (!!window.Backbone) {
LongDateLocalizer = (function() {
__extends(LongDateLocalizer, kb.LocalizedObservable);
function LongDateLocalizer() {
return LongDateLocalizer.__super__.constructor.apply(this, arguments);
}
LongDateLocalizer.prototype.read = function(value) {
return Globalize.format(value, Globalize.cultures[kb.locale_manager.getLocale()].calendars.standard.patterns.f, kb.locale_manager.getLocale());
};
LongDateLocalizer.prototype.write = function(localized_string, value, observable) {
var new_value;
new_value = Globalize.parseDate(localized_string, Globalize.cultures[kb.locale_manager.getLocale()].calendars.standard.patterns.d, kb.locale_manager.getLocale());
return value.setTime(new_value.valueOf());
};
return LongDateLocalizer;
})();
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment