From 9d2811a639b3ddf1867e19a8b791a4b518202c11 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 26 Jul 2011 13:31:51 -0400 Subject: [PATCH] Backbone.js 0.5.2 --- backbone.js | 4 +- docs/backbone.html | 90 ++++++++++++++++++++--------------------- docs/todos.html | 13 +++--- examples/todos/todos.js | 13 +++--- index.html | 49 +++++++++++++--------- package.json | 2 +- 6 files changed, 88 insertions(+), 83 deletions(-) diff --git a/backbone.js b/backbone.js index 7850315d4..62da3bdd1 100644 --- a/backbone.js +++ b/backbone.js @@ -1,4 +1,4 @@ -// Backbone.js 0.5.1 +// Backbone.js 0.5.2 // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may be freely distributed under the MIT license. // For all details and documentation: @@ -25,7 +25,7 @@ } // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '0.5.1'; + Backbone.VERSION = '0.5.2'; // Require Underscore, if we're on the server, and it's not already present. var _ = root._; diff --git a/docs/backbone.html b/docs/backbone.html index 3ee0e99a8..103953dcc 100644 --- a/docs/backbone.html +++ b/docs/backbone.html @@ -1,4 +1,4 @@ - backbone.js

backbone.js

Backbone.js 0.5.1
+      backbone.js           

backbone.js

Backbone.js 0.5.2
 (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
 Backbone may be freely distributed under the MIT license.
 For all details and documentation:
@@ -9,7 +9,7 @@
     Backbone = exports;
   } else {
     Backbone = root.Backbone = {};
-  }

Current version of the library. Keep in sync with package.json.

  Backbone.VERSION = '0.5.1';

Require Underscore, if we're on the server, and it's not already present.

  var _ = root._;
+  }

Current version of the library. Keep in sync with package.json.

  Backbone.VERSION = '0.5.2';

Require Underscore, if we're on the server, and it's not already present.

  var _ = root._;
   if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;

For Backbone's purposes, jQuery 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;
@@ -28,10 +28,10 @@
 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) {
+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);
+      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 @@ -46,7 +46,7 @@ var list = calls[ev]; if (!list) return this; for (var i = 0, l = list.length; i < l; i++) { - if (callback === list[i]) { + if (list[i] && callback === list[i][0]) { list[i] = null; break; } @@ -68,7 +68,7 @@ list.splice(i, 1); i--; l--; } else { args = both ? Array.prototype.slice.call(arguments, 1) : arguments; - callback.apply(this, args); + callback[0].apply(callback[1] || this, args); } } } @@ -81,7 +81,7 @@ var defaults; attributes || (attributes = {}); if (defaults = this.defaults) { - if (_.isFunction(defaults)) defaults = defaults(); + if (_.isFunction(defaults)) defaults = defaults.call(this); attributes = _.extend({}, defaults, attributes); } this.attributes = {}; @@ -365,7 +365,7 @@ options || (options = {}); model = this._prepareModel(model, options); if (!model) return false; - var already = this.getByCid(model) || this.get(model); + 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; @@ -507,23 +507,23 @@ 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); + 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);
       }
       return this.loadUrl();
-    },

Add a route to be tested when the fragment changes. Routes added later may + },

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, + },

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 + },

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);
@@ -534,7 +534,7 @@
         }
       });
       return matched;
-    },

Save a fragment into the hash history. You are responsible for properly + },

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, '');
@@ -554,27 +554,27 @@
       if (triggerRoute) this.loadUrl(fragment);
     }
 
-  });

Backbone.View

Creating a Backbone.View creates its initial element outside of the DOM, + });

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. + };

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 + };

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 + },

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 + },

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'));
@@ -583,7 +583,7 @@
       if (attributes) $(el).attr(attributes);
       if (content) $(el).html(content);
       return el;
-    },

Set callbacks, where this.callbacks is a hash of

+ },

Set callbacks, where this.callbacks is a hash of

{"event selector": "callback"}

@@ -613,7 +613,7 @@ $(this.el).delegate(selector, eventName, method); } } - },

Performs the initial configuration of a View with a set of options. + },

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);
@@ -622,7 +622,7 @@
         if (options[attr]) this[attr] = options[attr];
       }
       this.options = options;
-    },

Ensure that the View has a DOM element to render into. + },

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() {
@@ -636,17 +636,17 @@
       }
     }
 
-  });

The self-propagating extend function that Backbone classes use.

  var extend = function (protoProps, classProps) {
+  });

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 = {
+  };

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 + };

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:

@@ -663,20 +663,18 @@ 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({
+    var type = methodMap[method];

Default JSON-request options.

    var params = _.extend({
       type:         type,
-      dataType:     'json',
-      processData:  false
-    }, options);

Ensure that we have a URL.

    if (!params.url) {
+      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')) {
+    }

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) {
+    }

For older servers, emulate JSON by encoding the request into an HTML-form.

    if (Backbone.emulateJSON) {
       params.contentType = 'application/x-www-form-urlencoded';
-      params.processData = true;
       params.data        = params.data ? {model : params.data} : {};
-    }

For older servers, emulate HTTP by mimicking the HTTP method with _method + }

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;
@@ -685,29 +683,31 @@
           xhr.setRequestHeader('X-HTTP-Method-Override', type);
         };
       }
-    }

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. + }

Don't process data on a non-GET request.

    if (params.type !== 'GET') {
+      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 + 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 + }

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;
+    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 + };

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 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) {
+  };

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);
@@ -715,8 +715,8 @@
         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;');
+  };

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);
diff --git a/docs/todos.html b/docs/todos.html
index b36fda72d..647a63b8f 100644
--- a/docs/todos.html
+++ b/docs/todos.html
@@ -36,8 +36,7 @@
     },

The TodoView listens for changes to its model, re-rendering. Since there's a one-to-one correspondence between a Todo and a TodoView in this app, we set a direct reference on the model for convenience.

    initialize: function() {
-      _.bindAll(this, 'render', 'close');
-      this.model.bind('change', this.render);
+      this.model.bind('change', this.render, this);
       this.model.view = this;
     },

Re-render the contents of the todo item.

    render: function() {
       $(this.el).html(this.template(this.model.toJSON()));
@@ -48,7 +47,7 @@
       var content = this.model.get('content');
       this.$('.todo-content').text(content);
       this.input = this.$('.todo-input');
-      this.input.bind('blur', this.close);
+      this.input.bind('blur', _.bind(this.close, this));
       this.input.val(content);
     },

Toggle the "done" state of the model.

    toggleDone: function() {
       this.model.toggle();
@@ -74,13 +73,11 @@
     },

At initialization we bind to the relevant events on the Todos collection, when items are added or changed. Kick things off by loading any preexisting todos that might be saved in localStorage.

    initialize: function() {
-      _.bindAll(this, 'addOne', 'addAll', 'render');
-
       this.input    = this.$("#new-todo");
 
-      Todos.bind('add',     this.addOne);
-      Todos.bind('reset',   this.addAll);
-      Todos.bind('all',     this.render);
+      Todos.bind('add',   this.addOne, this);
+      Todos.bind('reset', this.addAll, this);
+      Todos.bind('all',   this.render, this);
 
       Todos.fetch();
     },

Re-rendering the App just means refreshing the statistics -- the rest diff --git a/examples/todos/todos.js b/examples/todos/todos.js index 47c7e37f3..d7935a584 100644 --- a/examples/todos/todos.js +++ b/examples/todos/todos.js @@ -102,8 +102,7 @@ $(function(){ // a one-to-one correspondence between a **Todo** and a **TodoView** in this // app, we set a direct reference on the model for convenience. initialize: function() { - _.bindAll(this, 'render', 'close'); - this.model.bind('change', this.render); + this.model.bind('change', this.render, this); this.model.view = this; }, @@ -120,7 +119,7 @@ $(function(){ var content = this.model.get('content'); this.$('.todo-content').text(content); this.input = this.$('.todo-input'); - this.input.bind('blur', this.close); + this.input.bind('blur', _.bind(this.close, this)); this.input.val(content); }, @@ -182,13 +181,11 @@ $(function(){ // collection, when items are added or changed. Kick things off by // loading any preexisting todos that might be saved in *localStorage*. initialize: function() { - _.bindAll(this, 'addOne', 'addAll', 'render'); - this.input = this.$("#new-todo"); - Todos.bind('add', this.addOne); - Todos.bind('reset', this.addAll); - Todos.bind('all', this.render); + Todos.bind('add', this.addOne, this); + Todos.bind('reset', this.addAll, this); + Todos.bind('all', this.render, this); Todos.fetch(); }, diff --git a/index.html b/index.html index 90612bbdb..5598b518b 100644 --- a/index.html +++ b/index.html @@ -149,7 +149,7 @@