backbone-rails 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -46,3 +46,4 @@ The gem will follow backbone versioning.
46
46
  ## Contributors
47
47
 
48
48
  * [John Bintz](https://github.com/johnbintz) (Support for Rails 3.0 via generators)
49
+ * [Wes Gibbs](https://github.com/wgibbs) and [Les Hill](https://github.com/leshill) (Include all dependencies as a group)
@@ -1,4 +1,4 @@
1
- // Backbone.js 0.5.2
1
+ // Backbone.js 0.5.3
2
2
  // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
3
3
  // Backbone may be freely distributed under the MIT license.
4
4
  // For all details and documentation:
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  // Current version of the library. Keep in sync with `package.json`.
28
- Backbone.VERSION = '0.5.2';
28
+ Backbone.VERSION = '0.5.3';
29
29
 
30
30
  // Require Underscore, if we're on the server, and it's not already present.
31
31
  var _ = root._;
@@ -41,7 +41,7 @@
41
41
  return this;
42
42
  };
43
43
 
44
- // Turn on `emulateHTTP` to use support legacy HTTP servers. Setting this option will
44
+ // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option will
45
45
  // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
46
46
  // `X-Http-Method-Override` header.
47
47
  Backbone.emulateHTTP = false;
@@ -641,7 +641,7 @@
641
641
  var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
642
642
  'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
643
643
  'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
644
- 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty'];
644
+ 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'groupBy'];
645
645
 
646
646
  // Mix in each Underscore method as a proxy to `Collection#models`.
647
647
  _.each(methods, function(method) {
@@ -766,7 +766,7 @@
766
766
  fragment = window.location.hash;
767
767
  }
768
768
  }
769
- return fragment.replace(hashStrip, '');
769
+ return decodeURIComponent(fragment.replace(hashStrip, ''));
770
770
  },
771
771
 
772
772
  // Start the hash change handling, returning `true` if the current URL matches
@@ -812,7 +812,10 @@
812
812
  this.fragment = loc.hash.replace(hashStrip, '');
813
813
  window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
814
814
  }
815
- return this.loadUrl();
815
+
816
+ if (!this.options.silent) {
817
+ return this.loadUrl();
818
+ }
816
819
  },
817
820
 
818
821
  // Add a route to be tested when the fragment changes. Routes added later may
@@ -949,6 +952,7 @@
949
952
  // not `change`, `submit`, and `reset` in Internet Explorer.
950
953
  delegateEvents : function(events) {
951
954
  if (!(events || (events = this.events))) return;
955
+ if (_.isFunction(events)) events = events.call(this);
952
956
  $(this.el).unbind('.delegateEvents' + this.cid);
953
957
  for (var key in events) {
954
958
  var method = this[events[key]];
@@ -1070,7 +1074,7 @@
1070
1074
  }
1071
1075
 
1072
1076
  // Don't process data on a non-GET request.
1073
- if (params.type !== 'GET') {
1077
+ if (params.type !== 'GET' && !Backbone.emulateJSON) {
1074
1078
  params.processData = false;
1075
1079
  }
1076
1080
 
@@ -1151,4 +1155,4 @@
1151
1155
  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;');
1152
1156
  };
1153
1157
 
1154
- }).call(this);
1158
+ }).call(this);
metadata CHANGED
@@ -1,75 +1,67 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: backbone-rails
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.3
4
5
  prerelease:
5
- version: 0.5.2
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Alexander Flatter
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-08-07 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2011-11-02 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: rails
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2153430460 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
23
21
  version: 3.0.0
24
22
  type: :runtime
25
- version_requirements: *id001
26
- description: Ships backbone and underscore to your Rails 3.1 application through the new asset pipeline. Rails 3.0 is supported via generators.
27
- email:
23
+ prerelease: false
24
+ version_requirements: *2153430460
25
+ description: Ships backbone and underscore to your Rails 3.1 application through the
26
+ new asset pipeline. Rails 3.0 is supported via generators.
27
+ email:
28
28
  - aflatter@farbenmeer.net
29
29
  executables: []
30
-
31
30
  extensions: []
32
-
33
31
  extra_rdoc_files: []
34
-
35
- files:
36
- - lib/backbone-rails.rb
37
- - lib/backbone-rails/generators.rb
32
+ files:
38
33
  - lib/backbone-rails/engine.rb
34
+ - lib/backbone-rails/generators.rb
39
35
  - lib/backbone-rails/railtie.rb
40
- - vendor/assets/javascripts/underscore.js
41
- - vendor/assets/javascripts/backbone.js
36
+ - lib/backbone-rails.rb
42
37
  - vendor/assets/javascripts/backbone-rails.js
38
+ - vendor/assets/javascripts/backbone.js
43
39
  - vendor/assets/javascripts/json2.js
44
- - vendor/assets/javascripts/backbone.js.old
40
+ - vendor/assets/javascripts/underscore.js
45
41
  - MIT-LICENSE
46
42
  - README.md
47
43
  homepage: https://github.com/aflatter/backbone-rails
48
44
  licenses: []
49
-
50
45
  post_install_message:
51
46
  rdoc_options: []
52
-
53
- require_paths:
47
+ require_paths:
54
48
  - lib
55
- required_ruby_version: !ruby/object:Gem::Requirement
49
+ required_ruby_version: !ruby/object:Gem::Requirement
56
50
  none: false
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: "0"
61
- required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
56
  none: false
63
- requirements:
64
- - - ">="
65
- - !ruby/object:Gem::Version
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
66
60
  version: 1.3.6
67
61
  requirements: []
68
-
69
62
  rubyforge_project:
70
- rubygems_version: 1.8.4
63
+ rubygems_version: 1.8.10
71
64
  signing_key:
72
65
  specification_version: 3
73
66
  summary: backbone and underscore for Rails
74
67
  test_files: []
75
-
@@ -1,1011 +0,0 @@
1
- // Backbone.js 0.3.3
2
- // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
3
- // Backbone may be freely distributed under the MIT license.
4
- // For all details and documentation:
5
- // http://documentcloud.github.com/backbone
6
-
7
- (function(){
8
-
9
- // Initial Setup
10
- // -------------
11
-
12
- // The top-level namespace. All public Backbone classes and modules will
13
- // be attached to this. Exported for both CommonJS and the browser.
14
- var Backbone;
15
- if (typeof exports !== 'undefined') {
16
- Backbone = exports;
17
- } else {
18
- Backbone = this.Backbone = {};
19
- }
20
-
21
- // Current version of the library. Keep in sync with `package.json`.
22
- Backbone.VERSION = '0.3.3';
23
-
24
- // Require Underscore, if we're on the server, and it's not already present.
25
- var _ = this._;
26
- if (!_ && (typeof require !== 'undefined')) _ = require("underscore")._;
27
-
28
- // For Backbone's purposes, either jQuery or Zepto owns the `$` variable.
29
- var $ = this.jQuery || this.Zepto;
30
-
31
- // Turn on `emulateHTTP` to use support legacy HTTP servers. Setting this option will
32
- // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
33
- // `X-Http-Method-Override` header.
34
- Backbone.emulateHTTP = false;
35
-
36
- // Turn on `emulateJSON` to support legacy servers that can't deal with direct
37
- // `application/json` requests ... will encode the body as
38
- // `application/x-www-form-urlencoded` instead and will send the model in a
39
- // form param named `model`.
40
- Backbone.emulateJSON = false;
41
-
42
- // Backbone.Events
43
- // -----------------
44
-
45
- // A module that can be mixed in to *any object* in order to provide it with
46
- // custom events. You may `bind` or `unbind` a callback function to an event;
47
- // `trigger`-ing an event fires all callbacks in succession.
48
- //
49
- // var object = {};
50
- // _.extend(object, Backbone.Events);
51
- // object.bind('expand', function(){ alert('expanded'); });
52
- // object.trigger('expand');
53
- //
54
- Backbone.Events = {
55
-
56
- // Bind an event, specified by a string name, `ev`, to a `callback` function.
57
- // Passing `"all"` will bind the callback to all events fired.
58
- bind : function(ev, callback) {
59
- var calls = this._callbacks || (this._callbacks = {});
60
- var list = this._callbacks[ev] || (this._callbacks[ev] = []);
61
- list.push(callback);
62
- return this;
63
- },
64
-
65
- // Remove one or many callbacks. If `callback` is null, removes all
66
- // callbacks for the event. If `ev` is null, removes all bound callbacks
67
- // for all events.
68
- unbind : function(ev, callback) {
69
- var calls;
70
- if (!ev) {
71
- this._callbacks = {};
72
- } else if (calls = this._callbacks) {
73
- if (!callback) {
74
- calls[ev] = [];
75
- } else {
76
- var list = calls[ev];
77
- if (!list) return this;
78
- for (var i = 0, l = list.length; i < l; i++) {
79
- if (callback === list[i]) {
80
- list.splice(i, 1);
81
- break;
82
- }
83
- }
84
- }
85
- }
86
- return this;
87
- },
88
-
89
- // Trigger an event, firing all bound callbacks. Callbacks are passed the
90
- // same arguments as `trigger` is, apart from the event name.
91
- // Listening for `"all"` passes the true event name as the first argument.
92
- trigger : function(ev) {
93
- var list, calls, i, l;
94
- if (!(calls = this._callbacks)) return this;
95
- if (list = calls[ev]) {
96
- for (i = 0, l = list.length; i < l; i++) {
97
- list[i].apply(this, Array.prototype.slice.call(arguments, 1));
98
- }
99
- }
100
- if (list = calls['all']) {
101
- for (i = 0, l = list.length; i < l; i++) {
102
- list[i].apply(this, arguments);
103
- }
104
- }
105
- return this;
106
- }
107
-
108
- };
109
-
110
- // Backbone.Model
111
- // --------------
112
-
113
- // Create a new model, with defined attributes. A client id (`cid`)
114
- // is automatically generated and assigned for you.
115
- Backbone.Model = function(attributes, options) {
116
- attributes || (attributes = {});
117
- if (this.defaults) attributes = _.extend({}, this.defaults, attributes);
118
- this.attributes = {};
119
- this._escapedAttributes = {};
120
- this.cid = _.uniqueId('c');
121
- this.set(attributes, {silent : true});
122
- this._previousAttributes = _.clone(this.attributes);
123
- if (options && options.collection) this.collection = options.collection;
124
- this.initialize(attributes, options);
125
- };
126
-
127
- // Attach all inheritable methods to the Model prototype.
128
- _.extend(Backbone.Model.prototype, Backbone.Events, {
129
-
130
- // A snapshot of the model's previous attributes, taken immediately
131
- // after the last `"change"` event was fired.
132
- _previousAttributes : null,
133
-
134
- // Has the item been changed since the last `"change"` event?
135
- _changed : false,
136
-
137
- // Initialize is an empty function by default. Override it with your own
138
- // initialization logic.
139
- initialize : function(){},
140
-
141
- // Return a copy of the model's `attributes` object.
142
- toJSON : function() {
143
- return _.clone(this.attributes);
144
- },
145
-
146
- // Get the value of an attribute.
147
- get : function(attr) {
148
- return this.attributes[attr];
149
- },
150
-
151
- // Get the HTML-escaped value of an attribute.
152
- escape : function(attr) {
153
- var html;
154
- if (html = this._escapedAttributes[attr]) return html;
155
- var val = this.attributes[attr];
156
- return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : val);
157
- },
158
-
159
- // Set a hash of model attributes on the object, firing `"change"` unless you
160
- // choose to silence it.
161
- set : function(attrs, options) {
162
-
163
- // Extract attributes and options.
164
- options || (options = {});
165
- if (!attrs) return this;
166
- if (attrs.attributes) attrs = attrs.attributes;
167
- var now = this.attributes, escaped = this._escapedAttributes;
168
-
169
- // Run validation.
170
- if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
171
-
172
- // Check for changes of `id`.
173
- if ('id' in attrs) this.id = attrs.id;
174
-
175
- // Update attributes.
176
- for (var attr in attrs) {
177
- var val = attrs[attr];
178
- if (!_.isEqual(now[attr], val)) {
179
- now[attr] = val;
180
- delete escaped[attr];
181
- if (!options.silent) {
182
- this._changed = true;
183
- this.trigger('change:' + attr, this, val, options);
184
- }
185
- }
186
- }
187
-
188
- // Fire the `"change"` event, if the model has been changed.
189
- if (!options.silent && this._changed) this.change(options);
190
- return this;
191
- },
192
-
193
- // Remove an attribute from the model, firing `"change"` unless you choose
194
- // to silence it.
195
- unset : function(attr, options) {
196
- options || (options = {});
197
- var value = this.attributes[attr];
198
-
199
- // Run validation.
200
- var validObj = {};
201
- validObj[attr] = void 0;
202
- if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
203
-
204
- // Remove the attribute.
205
- delete this.attributes[attr];
206
- delete this._escapedAttributes[attr];
207
- if (!options.silent) {
208
- this._changed = true;
209
- this.trigger('change:' + attr, this, void 0, options);
210
- this.change(options);
211
- }
212
- return this;
213
- },
214
-
215
- // Clear all attributes on the model, firing `"change"` unless you choose
216
- // to silence it.
217
- clear : function(options) {
218
- options || (options = {});
219
- var old = this.attributes;
220
-
221
- // Run validation.
222
- var validObj = {};
223
- for (attr in old) validObj[attr] = void 0;
224
- if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
225
-
226
- this.attributes = {};
227
- this._escapedAttributes = {};
228
- if (!options.silent) {
229
- this._changed = true;
230
- for (attr in old) {
231
- this.trigger('change:' + attr, this, void 0, options);
232
- }
233
- this.change(options);
234
- }
235
- return this;
236
- },
237
-
238
- // Fetch the model from the server. If the server's representation of the
239
- // model differs from its current attributes, they will be overriden,
240
- // triggering a `"change"` event.
241
- fetch : function(options) {
242
- options || (options = {});
243
- var model = this;
244
- var success = function(resp) {
245
- if (!model.set(model.parse(resp), options)) return false;
246
- if (options.success) options.success(model, resp);
247
- };
248
- var error = wrapError(options.error, model, options);
249
- (this.sync || Backbone.sync)('read', this, success, error);
250
- return this;
251
- },
252
-
253
- // Set a hash of model attributes, and sync the model to the server.
254
- // If the server returns an attributes hash that differs, the model's
255
- // state will be `set` again.
256
- save : function(attrs, options) {
257
- options || (options = {});
258
- if (attrs && !this.set(attrs, options)) return false;
259
- var model = this;
260
- var success = function(resp) {
261
- if (!model.set(model.parse(resp), options)) return false;
262
- if (options.success) options.success(model, resp);
263
- };
264
- var error = wrapError(options.error, model, options);
265
- var method = this.isNew() ? 'create' : 'update';
266
- (this.sync || Backbone.sync)(method, this, success, error);
267
- return this;
268
- },
269
-
270
- // Destroy this model on the server. Upon success, the model is removed
271
- // from its collection, if it has one.
272
- destroy : function(options) {
273
- options || (options = {});
274
- var model = this;
275
- var success = function(resp) {
276
- if (model.collection) model.collection.remove(model);
277
- if (options.success) options.success(model, resp);
278
- };
279
- var error = wrapError(options.error, model, options);
280
- (this.sync || Backbone.sync)('delete', this, success, error);
281
- return this;
282
- },
283
-
284
- // Default URL for the model's representation on the server -- if you're
285
- // using Backbone's restful methods, override this to change the endpoint
286
- // that will be called.
287
- url : function() {
288
- var base = getUrl(this.collection);
289
- if (this.isNew()) return base;
290
- return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
291
- },
292
-
293
- // **parse** converts a response into the hash of attributes to be `set` on
294
- // the model. The default implementation is just to pass the response along.
295
- parse : function(resp) {
296
- return resp;
297
- },
298
-
299
- // Create a new model with identical attributes to this one.
300
- clone : function() {
301
- return new this.constructor(this);
302
- },
303
-
304
- // A model is new if it has never been saved to the server, and has a negative
305
- // ID.
306
- isNew : function() {
307
- return !this.id;
308
- },
309
-
310
- // Call this method to manually fire a `change` event for this model.
311
- // Calling this will cause all objects observing the model to update.
312
- change : function(options) {
313
- this.trigger('change', this, options);
314
- this._previousAttributes = _.clone(this.attributes);
315
- this._changed = false;
316
- },
317
-
318
- // Determine if the model has changed since the last `"change"` event.
319
- // If you specify an attribute name, determine if that attribute has changed.
320
- hasChanged : function(attr) {
321
- if (attr) return this._previousAttributes[attr] != this.attributes[attr];
322
- return this._changed;
323
- },
324
-
325
- // Return an object containing all the attributes that have changed, or false
326
- // if there are no changed attributes. Useful for determining what parts of a
327
- // view need to be updated and/or what attributes need to be persisted to
328
- // the server.
329
- changedAttributes : function(now) {
330
- now || (now = this.attributes);
331
- var old = this._previousAttributes;
332
- var changed = false;
333
- for (var attr in now) {
334
- if (!_.isEqual(old[attr], now[attr])) {
335
- changed = changed || {};
336
- changed[attr] = now[attr];
337
- }
338
- }
339
- return changed;
340
- },
341
-
342
- // Get the previous value of an attribute, recorded at the time the last
343
- // `"change"` event was fired.
344
- previous : function(attr) {
345
- if (!attr || !this._previousAttributes) return null;
346
- return this._previousAttributes[attr];
347
- },
348
-
349
- // Get all of the attributes of the model at the time of the previous
350
- // `"change"` event.
351
- previousAttributes : function() {
352
- return _.clone(this._previousAttributes);
353
- },
354
-
355
- // Run validation against a set of incoming attributes, returning `true`
356
- // if all is well. If a specific `error` callback has been passed,
357
- // call that instead of firing the general `"error"` event.
358
- _performValidation : function(attrs, options) {
359
- var error = this.validate(attrs);
360
- if (error) {
361
- if (options.error) {
362
- options.error(this, error);
363
- } else {
364
- this.trigger('error', this, error, options);
365
- }
366
- return false;
367
- }
368
- return true;
369
- }
370
-
371
- });
372
-
373
- // Backbone.Collection
374
- // -------------------
375
-
376
- // Provides a standard collection class for our sets of models, ordered
377
- // or unordered. If a `comparator` is specified, the Collection will maintain
378
- // its models in sort order, as they're added and removed.
379
- Backbone.Collection = function(models, options) {
380
- options || (options = {});
381
- if (options.comparator) {
382
- this.comparator = options.comparator;
383
- delete options.comparator;
384
- }
385
- this._boundOnModelEvent = _.bind(this._onModelEvent, this);
386
- this._reset();
387
- if (models) this.refresh(models, {silent: true});
388
- this.initialize(models, options);
389
- };
390
-
391
- // Define the Collection's inheritable methods.
392
- _.extend(Backbone.Collection.prototype, Backbone.Events, {
393
-
394
- // The default model for a collection is just a **Backbone.Model**.
395
- // This should be overridden in most cases.
396
- model : Backbone.Model,
397
-
398
- // Initialize is an empty function by default. Override it with your own
399
- // initialization logic.
400
- initialize : function(){},
401
-
402
- // The JSON representation of a Collection is an array of the
403
- // models' attributes.
404
- toJSON : function() {
405
- return this.map(function(model){ return model.toJSON(); });
406
- },
407
-
408
- // Add a model, or list of models to the set. Pass **silent** to avoid
409
- // firing the `added` event for every new model.
410
- add : function(models, options) {
411
- if (_.isArray(models)) {
412
- for (var i = 0, l = models.length; i < l; i++) {
413
- this._add(models[i], options);
414
- }
415
- } else {
416
- this._add(models, options);
417
- }
418
- return this;
419
- },
420
-
421
- // Remove a model, or a list of models from the set. Pass silent to avoid
422
- // firing the `removed` event for every model removed.
423
- remove : function(models, options) {
424
- if (_.isArray(models)) {
425
- for (var i = 0, l = models.length; i < l; i++) {
426
- this._remove(models[i], options);
427
- }
428
- } else {
429
- this._remove(models, options);
430
- }
431
- return this;
432
- },
433
-
434
- // Get a model from the set by id.
435
- get : function(id) {
436
- if (id == null) return null;
437
- return this._byId[id.id != null ? id.id : id];
438
- },
439
-
440
- // Get a model from the set by client id.
441
- getByCid : function(cid) {
442
- return cid && this._byCid[cid.cid || cid];
443
- },
444
-
445
- // Get the model at the given index.
446
- at: function(index) {
447
- return this.models[index];
448
- },
449
-
450
- // Force the collection to re-sort itself. You don't need to call this under normal
451
- // circumstances, as the set will maintain sort order as each item is added.
452
- sort : function(options) {
453
- options || (options = {});
454
- if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
455
- this.models = this.sortBy(this.comparator);
456
- if (!options.silent) this.trigger('refresh', this, options);
457
- return this;
458
- },
459
-
460
- // Pluck an attribute from each model in the collection.
461
- pluck : function(attr) {
462
- return _.map(this.models, function(model){ return model.get(attr); });
463
- },
464
-
465
- // When you have more items than you want to add or remove individually,
466
- // you can refresh the entire set with a new list of models, without firing
467
- // any `added` or `removed` events. Fires `refresh` when finished.
468
- refresh : function(models, options) {
469
- models || (models = []);
470
- options || (options = {});
471
- this._reset();
472
- this.add(models, {silent: true});
473
- if (!options.silent) this.trigger('refresh', this, options);
474
- return this;
475
- },
476
-
477
- // Fetch the default set of models for this collection, refreshing the
478
- // collection when they arrive.
479
- fetch : function(options) {
480
- options || (options = {});
481
- var collection = this;
482
- var success = function(resp) {
483
- collection.refresh(collection.parse(resp));
484
- if (options.success) options.success(collection, resp);
485
- };
486
- var error = wrapError(options.error, collection, options);
487
- (this.sync || Backbone.sync)('read', this, success, error);
488
- return this;
489
- },
490
-
491
- // Create a new instance of a model in this collection. After the model
492
- // has been created on the server, it will be added to the collection.
493
- create : function(model, options) {
494
- var coll = this;
495
- options || (options = {});
496
- if (!(model instanceof Backbone.Model)) {
497
- model = new this.model(model, {collection: coll});
498
- } else {
499
- model.collection = coll;
500
- }
501
- var success = function(nextModel, resp) {
502
- coll.add(nextModel);
503
- if (options.success) options.success(nextModel, resp);
504
- };
505
- return model.save(null, {success : success, error : options.error});
506
- },
507
-
508
- // **parse** converts a response into a list of models to be added to the
509
- // collection. The default implementation is just to pass it through.
510
- parse : function(resp) {
511
- return resp;
512
- },
513
-
514
- // Proxy to _'s chain. Can't be proxied the same way the rest of the
515
- // underscore methods are proxied because it relies on the underscore
516
- // constructor.
517
- chain: function () {
518
- return _(this.models).chain();
519
- },
520
-
521
- // Reset all internal state. Called when the collection is refreshed.
522
- _reset : function(options) {
523
- this.length = 0;
524
- this.models = [];
525
- this._byId = {};
526
- this._byCid = {};
527
- },
528
-
529
- // Internal implementation of adding a single model to the set, updating
530
- // hash indexes for `id` and `cid` lookups.
531
- _add : function(model, options) {
532
- options || (options = {});
533
- if (!(model instanceof Backbone.Model)) {
534
- model = new this.model(model, {collection: this});
535
- }
536
- var already = this.getByCid(model);
537
- if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
538
- this._byId[model.id] = model;
539
- this._byCid[model.cid] = model;
540
- model.collection = this;
541
- var index = this.comparator ? this.sortedIndex(model, this.comparator) : this.length;
542
- this.models.splice(index, 0, model);
543
- model.bind('all', this._boundOnModelEvent);
544
- this.length++;
545
- if (!options.silent) model.trigger('add', model, this, options);
546
- return model;
547
- },
548
-
549
- // Internal implementation of removing a single model from the set, updating
550
- // hash indexes for `id` and `cid` lookups.
551
- _remove : function(model, options) {
552
- options || (options = {});
553
- model = this.getByCid(model) || this.get(model);
554
- if (!model) return null;
555
- delete this._byId[model.id];
556
- delete this._byCid[model.cid];
557
- delete model.collection;
558
- this.models.splice(this.indexOf(model), 1);
559
- this.length--;
560
- if (!options.silent) model.trigger('remove', model, this, options);
561
- model.unbind('all', this._boundOnModelEvent);
562
- return model;
563
- },
564
-
565
- // Internal method called every time a model in the set fires an event.
566
- // Sets need to update their indexes when models change ids. All other
567
- // events simply proxy through.
568
- _onModelEvent : function(ev, model) {
569
- if (ev === 'change:id') {
570
- delete this._byId[model.previous('id')];
571
- this._byId[model.id] = model;
572
- }
573
- this.trigger.apply(this, arguments);
574
- }
575
-
576
- });
577
-
578
- // Underscore methods that we want to implement on the Collection.
579
- var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
580
- 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
581
- 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
582
- 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty'];
583
-
584
- // Mix in each Underscore method as a proxy to `Collection#models`.
585
- _.each(methods, function(method) {
586
- Backbone.Collection.prototype[method] = function() {
587
- return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
588
- };
589
- });
590
-
591
- // Backbone.Controller
592
- // -------------------
593
-
594
- // Controllers map faux-URLs to actions, and fire events when routes are
595
- // matched. Creating a new one sets its `routes` hash, if not set statically.
596
- Backbone.Controller = function(options) {
597
- options || (options = {});
598
- if (options.routes) this.routes = options.routes;
599
- this._bindRoutes();
600
- this.initialize(options);
601
- };
602
-
603
- // Cached regular expressions for matching named param parts and splatted
604
- // parts of route strings.
605
- var namedParam = /:([\w\d]+)/g;
606
- var splatParam = /\*([\w\d]+)/g;
607
-
608
- // Set up all inheritable **Backbone.Controller** properties and methods.
609
- _.extend(Backbone.Controller.prototype, Backbone.Events, {
610
-
611
- // Initialize is an empty function by default. Override it with your own
612
- // initialization logic.
613
- initialize : function(){},
614
-
615
- // Manually bind a single named route to a callback. For example:
616
- //
617
- // this.route('search/:query/p:num', 'search', function(query, num) {
618
- // ...
619
- // });
620
- //
621
- route : function(route, name, callback) {
622
- Backbone.history || (Backbone.history = new Backbone.History);
623
- if (!_.isRegExp(route)) route = this._routeToRegExp(route);
624
- Backbone.history.route(route, _.bind(function(fragment) {
625
- var args = this._extractParameters(route, fragment);
626
- callback.apply(this, args);
627
- this.trigger.apply(this, ['route:' + name].concat(args));
628
- }, this));
629
- },
630
-
631
- // Simple proxy to `Backbone.history` to save a fragment into the history,
632
- // without triggering routes.
633
- saveLocation : function(fragment) {
634
- Backbone.history.saveLocation(fragment);
635
- },
636
-
637
- // Bind all defined routes to `Backbone.history`.
638
- _bindRoutes : function() {
639
- if (!this.routes) return;
640
- for (var route in this.routes) {
641
- var name = this.routes[route];
642
- this.route(route, name, this[name]);
643
- }
644
- },
645
-
646
- // Convert a route string into a regular expression, suitable for matching
647
- // against the current location fragment.
648
- _routeToRegExp : function(route) {
649
- route = route.replace(namedParam, "([^\/]*)").replace(splatParam, "(.*?)");
650
- return new RegExp('^' + route + '$');
651
- },
652
-
653
- // Given a route, and a URL fragment that it matches, return the array of
654
- // extracted parameters.
655
- _extractParameters : function(route, fragment) {
656
- return route.exec(fragment).slice(1);
657
- }
658
-
659
- });
660
-
661
- // Backbone.History
662
- // ----------------
663
-
664
- // Handles cross-browser history management, based on URL hashes. If the
665
- // browser does not support `onhashchange`, falls back to polling.
666
- Backbone.History = function() {
667
- this.handlers = [];
668
- this.fragment = this.getFragment();
669
- _.bindAll(this, 'checkUrl');
670
- };
671
-
672
- // Cached regex for cleaning hashes.
673
- var hashStrip = /^#*/;
674
-
675
- // Set up all inheritable **Backbone.History** properties and methods.
676
- _.extend(Backbone.History.prototype, {
677
-
678
- // The default interval to poll for hash changes, if necessary, is
679
- // twenty times a second.
680
- interval: 50,
681
-
682
- // Get the cross-browser normalized URL fragment.
683
- getFragment : function(loc) {
684
- return (loc || window.location).hash.replace(hashStrip, '');
685
- },
686
-
687
- // Start the hash change handling, returning `true` if the current URL matches
688
- // an existing route, and `false` otherwise.
689
- start : function() {
690
- var docMode = document.documentMode;
691
- var oldIE = ($.browser.msie && (!docMode || docMode <= 7));
692
- if (oldIE) {
693
- this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
694
- }
695
- if ('onhashchange' in window && !oldIE) {
696
- $(window).bind('hashchange', this.checkUrl);
697
- } else {
698
- setInterval(this.checkUrl, this.interval);
699
- }
700
- return this.loadUrl();
701
- },
702
-
703
- // Add a route to be tested when the hash changes. Routes are matched in the
704
- // order they are added.
705
- route : function(route, callback) {
706
- this.handlers.push({route : route, callback : callback});
707
- },
708
-
709
- // Checks the current URL to see if it has changed, and if it has,
710
- // calls `loadUrl`, normalizing across the hidden iframe.
711
- checkUrl : function() {
712
- var current = this.getFragment();
713
- if (current == this.fragment && this.iframe) {
714
- current = this.getFragment(this.iframe.location);
715
- }
716
- if (current == this.fragment ||
717
- current == decodeURIComponent(this.fragment)) return false;
718
- if (this.iframe) {
719
- window.location.hash = this.iframe.location.hash = current;
720
- }
721
- this.loadUrl();
722
- },
723
-
724
- // Attempt to load the current URL fragment. If a route succeeds with a
725
- // match, returns `true`. If no defined routes matches the fragment,
726
- // returns `false`.
727
- loadUrl : function() {
728
- var fragment = this.fragment = this.getFragment();
729
- var matched = _.any(this.handlers, function(handler) {
730
- if (handler.route.test(fragment)) {
731
- handler.callback(fragment);
732
- return true;
733
- }
734
- });
735
- return matched;
736
- },
737
-
738
- // Save a fragment into the hash history. You are responsible for properly
739
- // URL-encoding the fragment in advance. This does not trigger
740
- // a `hashchange` event.
741
- saveLocation : function(fragment) {
742
- fragment = (fragment || '').replace(hashStrip, '');
743
- if (this.fragment == fragment) return;
744
- window.location.hash = this.fragment = fragment;
745
- if (this.iframe && (fragment != this.getFragment(this.iframe.location))) {
746
- this.iframe.document.open().close();
747
- this.iframe.location.hash = fragment;
748
- }
749
- }
750
-
751
- });
752
-
753
- // Backbone.View
754
- // -------------
755
-
756
- // Creating a Backbone.View creates its initial element outside of the DOM,
757
- // if an existing element is not provided...
758
- Backbone.View = function(options) {
759
- this._configure(options || {});
760
- this._ensureElement();
761
- this.delegateEvents();
762
- this.initialize(options);
763
- };
764
-
765
- // Element lookup, scoped to DOM elements within the current view.
766
- // This should be prefered to global lookups, if you're dealing with
767
- // a specific view.
768
- var selectorDelegate = function(selector) {
769
- return $(selector, this.el);
770
- };
771
-
772
- // Cached regex to split keys for `delegate`.
773
- var eventSplitter = /^(\w+)\s*(.*)$/;
774
-
775
- // Set up all inheritable **Backbone.View** properties and methods.
776
- _.extend(Backbone.View.prototype, Backbone.Events, {
777
-
778
- // The default `tagName` of a View's element is `"div"`.
779
- tagName : 'div',
780
-
781
- // Attach the `selectorDelegate` function as the `$` property.
782
- $ : selectorDelegate,
783
-
784
- // Initialize is an empty function by default. Override it with your own
785
- // initialization logic.
786
- initialize : function(){},
787
-
788
- // **render** is the core function that your view should override, in order
789
- // to populate its element (`this.el`), with the appropriate HTML. The
790
- // convention is for **render** to always return `this`.
791
- render : function() {
792
- return this;
793
- },
794
-
795
- // Remove this view from the DOM. Note that the view isn't present in the
796
- // DOM by default, so calling this method may be a no-op.
797
- remove : function() {
798
- $(this.el).remove();
799
- return this;
800
- },
801
-
802
- // For small amounts of DOM Elements, where a full-blown template isn't
803
- // needed, use **make** to manufacture elements, one at a time.
804
- //
805
- // var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
806
- //
807
- make : function(tagName, attributes, content) {
808
- var el = document.createElement(tagName);
809
- if (attributes) $(el).attr(attributes);
810
- if (content) $(el).html(content);
811
- return el;
812
- },
813
-
814
- // Set callbacks, where `this.callbacks` is a hash of
815
- //
816
- // *{"event selector": "callback"}*
817
- //
818
- // {
819
- // 'mousedown .title': 'edit',
820
- // 'click .button': 'save'
821
- // }
822
- //
823
- // pairs. Callbacks will be bound to the view, with `this` set properly.
824
- // Uses event delegation for efficiency.
825
- // Omitting the selector binds the event to `this.el`.
826
- // This only works for delegate-able events: not `focus`, `blur`, and
827
- // not `change`, `submit`, and `reset` in Internet Explorer.
828
- delegateEvents : function(events) {
829
- if (!(events || (events = this.events))) return;
830
- $(this.el).unbind();
831
- for (var key in events) {
832
- var methodName = events[key];
833
- var match = key.match(eventSplitter);
834
- var eventName = match[1], selector = match[2];
835
- var method = _.bind(this[methodName], this);
836
- if (selector === '') {
837
- $(this.el).bind(eventName, method);
838
- } else {
839
- $(this.el).delegate(selector, eventName, method);
840
- }
841
- }
842
- },
843
-
844
- // Performs the initial configuration of a View with a set of options.
845
- // Keys with special meaning *(model, collection, id, className)*, are
846
- // attached directly to the view.
847
- _configure : function(options) {
848
- if (this.options) options = _.extend({}, this.options, options);
849
- if (options.model) this.model = options.model;
850
- if (options.collection) this.collection = options.collection;
851
- if (options.el) this.el = options.el;
852
- if (options.id) this.id = options.id;
853
- if (options.className) this.className = options.className;
854
- if (options.tagName) this.tagName = options.tagName;
855
- this.options = options;
856
- },
857
-
858
- // Ensure that the View has a DOM element to render into.
859
- _ensureElement : function() {
860
- if (this.el) return;
861
- var attrs = {};
862
- if (this.id) attrs.id = this.id;
863
- if (this.className) attrs["class"] = this.className;
864
- this.el = this.make(this.tagName, attrs);
865
- }
866
-
867
- });
868
-
869
- // The self-propagating extend function that Backbone classes use.
870
- var extend = function (protoProps, classProps) {
871
- var child = inherits(this, protoProps, classProps);
872
- child.extend = extend;
873
- return child;
874
- };
875
-
876
- // Set up inheritance for the model, collection, and view.
877
- Backbone.Model.extend = Backbone.Collection.extend =
878
- Backbone.Controller.extend = Backbone.View.extend = extend;
879
-
880
- // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
881
- var methodMap = {
882
- 'create': 'POST',
883
- 'update': 'PUT',
884
- 'delete': 'DELETE',
885
- 'read' : 'GET'
886
- };
887
-
888
- // Backbone.sync
889
- // -------------
890
-
891
- // Override this function to change the manner in which Backbone persists
892
- // models to the server. You will be passed the type of request, and the
893
- // model in question. By default, uses makes a RESTful Ajax request
894
- // to the model's `url()`. Some possible customizations could be:
895
- //
896
- // * Use `setTimeout` to batch rapid-fire updates into a single request.
897
- // * Send up the models as XML instead of JSON.
898
- // * Persist models via WebSockets instead of Ajax.
899
- //
900
- // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
901
- // as `POST`, with a `_method` parameter containing the true HTTP method,
902
- // as well as all requests with the body as `application/x-www-form-urlencoded` instead of
903
- // `application/json` with the model in a param named `model`.
904
- // Useful when interfacing with server-side languages like **PHP** that make
905
- // it difficult to read the body of `PUT` requests.
906
- Backbone.sync = function(method, model, success, error) {
907
- var type = methodMap[method];
908
- var modelJSON = (method === 'create' || method === 'update') ?
909
- JSON.stringify(model.toJSON()) : null;
910
-
911
- // Default JSON-request options.
912
- var params = {
913
- url: getUrl(model),
914
- type: type,
915
- contentType: 'application/json',
916
- data: modelJSON,
917
- dataType: 'json',
918
- processData: false,
919
- success: success,
920
- error: error
921
- };
922
-
923
- // For older servers, emulate JSON by encoding the request into an HTML-form.
924
- if (Backbone.emulateJSON) {
925
- params.contentType = 'application/x-www-form-urlencoded';
926
- params.processData = true;
927
- params.data = modelJSON ? {model : modelJSON} : {};
928
- }
929
-
930
- // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
931
- // And an `X-HTTP-Method-Override` header.
932
- if (Backbone.emulateHTTP) {
933
- if (type === 'PUT' || type === 'DELETE') {
934
- if (Backbone.emulateJSON) params.data._method = type;
935
- params.type = 'POST';
936
- params.beforeSend = function(xhr) {
937
- xhr.setRequestHeader("X-HTTP-Method-Override", type);
938
- };
939
- }
940
- }
941
-
942
- // Make the request.
943
- $.ajax(params);
944
- };
945
-
946
- // Helpers
947
- // -------
948
-
949
- // Shared empty constructor function to aid in prototype-chain creation.
950
- var ctor = function(){};
951
-
952
- // Helper function to correctly set up the prototype chain, for subclasses.
953
- // Similar to `goog.inherits`, but uses a hash of prototype properties and
954
- // class properties to be extended.
955
- var inherits = function(parent, protoProps, staticProps) {
956
- var child;
957
-
958
- // The constructor function for the new subclass is either defined by you
959
- // (the "constructor" property in your `extend` definition), or defaulted
960
- // by us to simply call `super()`.
961
- if (protoProps && protoProps.hasOwnProperty('constructor')) {
962
- child = protoProps.constructor;
963
- } else {
964
- child = function(){ return parent.apply(this, arguments); };
965
- }
966
-
967
- // Set the prototype chain to inherit from `parent`, without calling
968
- // `parent`'s constructor function.
969
- ctor.prototype = parent.prototype;
970
- child.prototype = new ctor();
971
-
972
- // Add prototype properties (instance properties) to the subclass,
973
- // if supplied.
974
- if (protoProps) _.extend(child.prototype, protoProps);
975
-
976
- // Add static properties to the constructor function, if supplied.
977
- if (staticProps) _.extend(child, staticProps);
978
-
979
- // Correctly set child's `prototype.constructor`, for `instanceof`.
980
- child.prototype.constructor = child;
981
-
982
- // Set a convenience property in case the parent's prototype is needed later.
983
- child.__super__ = parent.prototype;
984
-
985
- return child;
986
- };
987
-
988
- // Helper function to get a URL from a Model or Collection as a property
989
- // or as a function.
990
- var getUrl = function(object) {
991
- if (!(object && object.url)) throw new Error("A 'url' property or function must be specified");
992
- return _.isFunction(object.url) ? object.url() : object.url;
993
- };
994
-
995
- // Wrap an optional error callback with a fallback error event.
996
- var wrapError = function(onError, model, options) {
997
- return function(resp) {
998
- if (onError) {
999
- onError(model, resp);
1000
- } else {
1001
- model.trigger('error', model, resp, options);
1002
- }
1003
- };
1004
- };
1005
-
1006
- // Helper function to escape a string for HTML rendering.
1007
- var escapeHTML = function(string) {
1008
- return string.replace(/&(?!\w+;)/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
1009
- };
1010
-
1011
- })();