[Pkg-javascript-commits] [backbone] 02/101: Initial semi-functional version of backbone.
Jonas Smedegaard
js at moszumanska.debian.org
Sat May 3 16:58:23 UTC 2014
This is an automated email from the git hooks/post-receive script.
js pushed a commit to tag 0.1.0
in repository backbone.
commit ef49944afa0e8d88a4ec62c1b916085798693600
Author: Jeremy Ashkenas <jashkenas at gmail.com>
Date: Fri Oct 1 17:52:46 2010 -0400
Initial semi-functional version of backbone.
---
Rakefile | 10 ++
backbone.js | 527 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 537 insertions(+)
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..4564bc5
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,10 @@
+require 'rubygems'
+require 'closure-compiler'
+
+desc "rebuild the backbone-min.js files for distribution"
+task :build do
+ files = Dir['lib/*.js']
+ source = files.map {|f| File.read f }.join "\n\n"
+ File.open('backbone.js', 'w+') {|f| f.write source }
+ File.open('backbone-min.js', 'w+') {|f| f.write Closure::Compiler.new.compress(source) }
+end
\ No newline at end of file
diff --git a/backbone.js b/backbone.js
new file mode 100644
index 0000000..74fcf27
--- /dev/null
+++ b/backbone.js
@@ -0,0 +1,527 @@
+// Backbone.js
+// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
+// Backbone may be freely distributed under the terms of the MIT license.
+// For all details and documentation:
+// http://documentcloud.github.com/backbone
+
+(function(){
+
+ // ------------------------- Initial Setup ----------------------------------
+
+ // The top-level namespace.
+ var Backbone = {};
+
+ // Keep the version in sync with `package.json`.
+ Backbone.VERSION = '0.1.0';
+
+ // Export for both CommonJS and the Browser.
+ (typeof exports !== 'undefined' ? exports : this).Backbone = Backbone;
+
+ // Helper function to correctly set up the prototype chain, for subclasses.
+ var inherits = function(parent, protoProps, classProps) {
+ if (protoProps.hasOwnProperty('constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
+ var ctor = function(){};
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+ _.extend(child.prototype, protoProps);
+ if (classProps) _.extend(child, classProps);
+ child.prototype.constructor = child;
+ return child;
+ };
+
+ // ------------------------ Backbone.Bindable -------------------------------
+
+ // A module that can be mixed in to any object in order to provide it with
+ // custom events.
+ Backbone.Bindable = {
+
+ // 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) {
+ var calls = this._callbacks || (this._callbacks = {});
+ var list = this._callbacks[ev] || (this._callbacks[ev] = []);
+ list.push(callback);
+ 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];
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (callback === list[i]) {
+ list.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ // Trigger an event, firing all bound callbacks
+ trigger : function(ev) {
+ var calls = this._callbacks;
+ for (var i = 0; i < 2; i++) {
+ var list = calls && calls[i ? 'all' : ev];
+ if (!list) continue;
+ for (var j = 0, l = list.length; j < l; j++) {
+ list[j].apply(this, arguments);
+ }
+ }
+ return this;
+ }
+
+ };
+
+ // ------------------------- Backbone.Model ---------------------------------
+
+ // Create a new model, with defined attributes.
+ // If you do not specify the id, a negative id will be assigned for you.
+ Backbone.Model = function(attributes) {
+ this._attributes = {};
+ attributes = attributes || {};
+ attributes.id = attributes.id || -_.uniqueId();
+ this.set(attributes, true);
+ this.cid = _.uniqueId('c');
+ this._formerAttributes = this.attributes();
+ };
+
+ // Create a model on the server and add it to the set.
+ // When the server returns a JSON representation of the model, we update it
+ // on the client.
+ Backbone.Model.create = function(attributes, options) {
+ options || (options = {});
+ var model = new this(attributes);
+ $.ajax({
+ url : model.set.resource,
+ type : 'POST',
+ data : {model : JSON.stringify(model.attributes())},
+ dataType : 'json',
+ success : function(resp) {
+ model.set(resp.model);
+ if (options.success) return options.success(model, resp);
+ },
+ error : function(resp) {
+ if (options.error) options.error(model, resp);
+ }
+ });
+ return model;
+ };
+
+ // Attach all inheritable methods to the Model prototype.
+ _.extend(Backbone.Model.prototype, Backbone.Bindable, {
+
+ // A snapshot of the model's previous attributes, taken immediately
+ // after the last `changed` event was fired.
+ _formerAttributes : null,
+
+ // Has the item been changed since the last `changed` event?
+ _changed : false,
+
+ // Create a new model with identical attributes to this one.
+ clone : function() {
+ return new (this.constructor)(this.attributes());
+ },
+
+ // Are this model's attributes identical to another model?
+ isEqual : function(other) {
+ return other && _.isEqual(this._attributes, other._attributes);
+ },
+
+ // A model is new if it has never been saved to the server, and has a negative
+ // ID.
+ isNew : function() {
+ return this.id < 0;
+ },
+
+ // Call this method to fire manually fire a `changed` event for this model.
+ // Calling this will cause all objects observing the model to update.
+ changed : function() {
+ this.trigger('change', this);
+ this._formerAttributes = this.attributes();
+ this._changed = false;
+ },
+
+ // Determine if the model has changed since the last `changed` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged : function(attr) {
+ if (attr) return this._formerAttributes[attr] != this._attributes[attr];
+ return this._changed;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `changed` event was fired.
+ formerValue : function(attr) {
+ if (!attr || !this._formerAttributes) return null;
+ return this._formerAttributes[attr];
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `changed` event.
+ formerAttributes : function() {
+ return this._formerAttributes;
+ },
+
+ // 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) {
+ var old = this.formerAttributes(), now = now || this.attributes(), changed = false;
+ for (var attr in now) {
+ if (!_.isEqual(old[attr], now[attr])) {
+ changed = changed || {};
+ changed[attr] = now[attr];
+ }
+ }
+ return changed;
+ },
+
+ // Set a hash of model attributes on the object, firing `changed` unless you
+ // choose to silence it.
+ set : function(attrs, options) {
+ options || (options = {});
+ if (!attrs) return this;
+ attrs = attrs._attributes || attrs;
+ var now = this._attributes;
+ if (attrs.collection) {
+ this.collection = attrs.collection;
+ delete attrs.collection;
+ this.resource = this.collection.resource + '/' + this.id;
+ }
+ if (attrs.id) {
+ this.id = attrs.id;
+ if (this.collection) this.resource = this.collection.resource + '/' + this.id;
+ }
+ for (var attr in attrs) {
+ var val = attrs[attr];
+ if (val === '') val = null;
+ if (!_.isEqual(now[attr], val)) {
+ if (!options.silent) this._changed = true;
+ now[attr] = val;
+ }
+ }
+ if (!options.silent && this._changed) this.changed();
+ return this;
+ },
+
+ // Get the value of an attribute.
+ get : function(attr) {
+ return this._attributes[attr];
+ },
+
+ // Remove an attribute from the model, firing `changed` unless you choose to
+ // silence it.
+ unset : function(attr, options) {
+ options || (options = {});
+ var value = this._attributes[attr];
+ delete this._attributes[attr];
+ if (!options.silent) this.changed();
+ return value;
+ },
+
+ // Set a hash of model attributes, and sync the model to the server.
+ save : function(attrs, options) {
+ if (!this.resource) throw new Error(this.toString() + " cannot be saved without a resource.");
+ options || (options = {});
+ this.set(attrs, options);
+ var model = this;
+ $.ajax({
+ url : this.resource,
+ type : 'PUT',
+ data : {model : JSON.stringify(this.attributes())},
+ dataType : 'json',
+ success : function(resp) {
+ model.set(resp.model);
+ if (options.success) options.success(model, resp);
+ },
+ error : function(resp) { if (options.error) options.error(model, resp); }
+ });
+ },
+
+ // Return a copy of the model's attributes.
+ attributes : function() {
+ return _.clone(this._attributes);
+ },
+
+ // Bind all methods in the list to the model.
+ bindAll : function() {
+ _.bindAll.apply(_, [this].concat(arguments));
+ },
+
+ toString : function() {
+ return 'Model ' + this.id;
+ },
+
+ // Destroy this model on the server.
+ destroy : function(options) {
+ if (this.collection) this.collection.remove(this);
+ $.ajax({
+ url : this.resource,
+ type : 'DELETE',
+ data : {},
+ dataType : 'json',
+ success : function(resp) { if (options.success) options.success(model, resp); },
+ error : function(resp) { if (options.error) options.error(model, resp); }
+ });
+ }
+
+ });
+
+ // ----------------------- 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(options) {
+ this._boundOnModelEvent = _.bind(this._onModelEvent, this);
+ this._initialize();
+ };
+
+ // Define the Collection's inheritable methods.
+ _.extend(Backbone.Collection.prototype, Backbone.Bindable, {
+
+ // Initialize or re-initialize all internal state. Called when the
+ // collection is refreshed.
+ _initialize : function() {
+ this.length = 0;
+ this.models = [];
+ this._byId = {};
+ this._byCid = {};
+ },
+
+ // Get a model from the set by id.
+ get : function(id) {
+ return id && this._byId[id.id || id];
+ },
+
+ // Get a model from the set by client id.
+ getByCid : function(cid) {
+ return cid && this._byCid[cid.cid || cid];
+ },
+
+ // What are the ids for every model in the set?
+ getIds : function() {
+ return _.keys(this._byId);
+ },
+
+ // What are the client ids for every model in the set?
+ getCids : function() {
+ return _.keys(this._byCid);
+ },
+
+ // Get the model at the given index.
+ at: function(index) {
+ return this.models[index];
+ },
+
+ // 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, silent) {
+ if (!_.isArray(models)) return this._add(models, silent);
+ for (var i=0; i<models.length; i++) this._add(models[i], silent);
+ return models;
+ },
+
+ // Internal implementation of adding a single model to the set.
+ _add : function(model, silent) {
+ var already = this.get(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 = this.comparator ? this.sortedIndex(model, this.comparator) : this.length - 1;
+ this.models.splice(index, 0, model);
+ model.bind('all', this._boundOnModelEvent);
+ this.length++;
+ if (!silent) this.trigger('add', model);
+ return model;
+ },
+
+ // 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, silent) {
+ if (!_.isArray(models)) return this._remove(models, silent);
+ for (var i=0; i<models.length; i++) this._remove(models[i], silent);
+ return models;
+ },
+
+ // Internal implementation of removing a single model from the set.
+ _remove : function(model, silent) {
+ 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);
+ model.unbind('all', this._boundOnModelEvent);
+ this.length--;
+ if (!silent) this.trigger('remove', model);
+ return model;
+ },
+
+ // When you have more items than you want to add or remove individually,
+ // you can refresh the entire set with a new list of models, without firing
+ // any `added` or `removed` events. Fires `refreshed` when finished.
+ refresh : function(models, silent) {
+ models = models || [];
+ if (models[0] && !(models[0] instanceof Backbone.Model)) {
+ for (var i = 0, l = models.length; i < l; i++) {
+ models[i].collection = this;
+ models[i] = new this.model(models[i]);
+ }
+ }
+ this._initialize();
+ this.add(models, true);
+ if (!silent) this.trigger('refresh');
+ },
+
+ // Force the set 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(silent) {
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+ this.models = this.sortBy(this.comparator);
+ if (!silent) this.trigger('refresh');
+ },
+
+ // Internal method called every time a model in the set fires an event.
+ // Sets need to update their indexes when models change ids.
+ _onModelEvent : function(ev, model) {
+ if (ev == 'change') {
+ if (model.hasChanged('id')) {
+ delete this._byId[model.formerValue('id')];
+ this._byId[model.id] = model;
+ }
+ this.trigger('change', model);
+ }
+ },
+
+ // Inspect.
+ toString : function() {
+ return 'Set (' + this.length + " models)";
+ }
+
+ });
+
+ // Underscore methods that we want to implement on the Collection.
+ var methods = ['each', 'map', 'reduce', 'reduceRight', 'detect', 'select',
+ 'reject', 'all', 'any', 'include', 'invoke', 'pluck', 'max', 'min', 'sortBy',
+ 'sortedIndex', 'toArray', 'size', 'first', 'rest', 'last', 'without',
+ 'indexOf', 'lastIndexOf', 'isEmpty'];
+
+ // 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 View ---------------------------------
+
+ Backbone.View = function(options) {
+ this.modes = {};
+ this.configure(options || {});
+ if (this.options.el) {
+ this.el = this.options.el;
+ } else {
+ var attrs = {};
+ if (this.id) attrs.id = this.id;
+ if (this.className) attrs['class'] = this.className;
+ this.el = this.make(this.tagName, attrs);
+ }
+ return this;
+ };
+
+ // Set up all interitable view properties and methods.
+ _.extend(Backbone.View.prototype, {
+
+ el : null,
+ model : null,
+ modes : null,
+ id : null,
+ className : null,
+ callbacks : null,
+ options : null,
+ tagName : 'div',
+
+ configure : function(options) {
+ if (this.options) options = _.extend({}, this.options, options);
+ if (options.model) this.model = options.model;
+ if (options.collection) this.collection = options.collection;
+ if (options.id) this.id = options.id;
+ if (options.className) this.className = options.className;
+ this.options = options;
+ },
+
+ render : function() {
+ return this;
+ },
+
+ // jQuery lookup, scoped to the current view.
+ $ : function(selector) {
+ return $(selector, this.el);
+ },
+
+ // Quick-create a dom element with attributes.
+ make : function(tagName, attributes, content) {
+ var el = document.createElement(tagName);
+ if (attributes) $(el).attr(attributes);
+ if (content) $(el).html(content);
+ return el;
+ },
+
+ // Makes the view enter a mode. Modes have both a 'mode' and a 'group',
+ // and are mutually exclusive with any other modes in the same group.
+ // Setting will update the view's modes hash, as well as set an HTML className
+ // of [mode]_[group] on the view's element. Convenient way to swap styles
+ // and behavior.
+ setMode : function(mode, group) {
+ if (this.modes[group] == mode) return;
+ $(this.el).setMode(mode, group);
+ this.modes[group] = mode;
+ },
+
+ // Set callbacks, where this.callbacks is a hash of
+ // {selector.event_name, callback_name}
+ // pairs. Callbacks will be bound to the view, with 'this' set properly.
+ // Passing a selector of 'el' binds to the view's root element.
+ // Change events are not delegated through the view because IE does not bubble
+ // change events at all.
+ setCallbacks : function(callbacks) {
+ $(this.el).unbind();
+ if (!(callbacks || (callbacks = this.callbacks))) return this;
+ for (key in callbacks) {
+ var methodName = callbacks[key];
+ key = key.split(/\.(?!.*\.)/);
+ var selector = key[0], eventName = key[1];
+ var method = _.bind(this[methodName], this);
+ if (selector === '' || eventName == 'change') {
+ $(this.el).bind(eventName, method);
+ } else {
+ $(this.el).delegate(selector, eventName, method);
+ }
+ }
+ return this;
+ }
+
+ });
+
+ // Set up inheritance for the model, collection, and view.
+ var extend = Backbone.Model.extend = Backbone.Collection.extend = Backbone.View.extend = function (protoProps, classProps) {
+ var child = inherits(this, protoProps, classProps);
+ child.extend = extend;
+ return child;
+ };
+
+})();
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/backbone.git
More information about the Pkg-javascript-commits
mailing list