laces 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CONTRIBUTING.md +38 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +122 -0
  4. data/LICENSE +21 -0
  5. data/README.md +5 -0
  6. data/Rakefile +8 -0
  7. data/bin/laces +16 -0
  8. data/features/creating_a_heroku_app.feature +9 -0
  9. data/features/rake_clean.feature +21 -0
  10. data/features/skipping_clearance.feature +13 -0
  11. data/features/step_definitions/gem_steps.rb +5 -0
  12. data/features/step_definitions/heroku_steps.rb +3 -0
  13. data/features/step_definitions/shell_steps.rb +55 -0
  14. data/features/support/bin/heroku +5 -0
  15. data/features/support/env.rb +15 -0
  16. data/features/support/fake_heroku.rb +21 -0
  17. data/laces-0.0.1.gem +0 -0
  18. data/laces.gemspec +35 -0
  19. data/lib/laces/actions.rb +35 -0
  20. data/lib/laces/app_builder.rb +237 -0
  21. data/lib/laces/generators/app_generator.rb +111 -0
  22. data/lib/laces/version.rb +3 -0
  23. data/templates/.DS_Store +0 -0
  24. data/templates/Gemfile_template +76 -0
  25. data/templates/HEROKU_README.md +66 -0
  26. data/templates/Procfile +1 -0
  27. data/templates/README.md +81 -0
  28. data/templates/app/assets/imgs/glyphicons-halflings-white.png +0 -0
  29. data/templates/app/assets/imgs/glyphicons-halflings.png +0 -0
  30. data/templates/app/assets/javascripts/admin.coffee +20 -0
  31. data/templates/app/assets/javascripts/application.coffee +21 -0
  32. data/templates/app/assets/javascripts/lib/actinology.coffee +47 -0
  33. data/templates/app/assets/javascripts/lib/analytics.js +11 -0
  34. data/templates/app/assets/javascripts/lib/auth_token_sync.js +17 -0
  35. data/templates/app/assets/javascripts/lib/backbone-ui.js +2455 -0
  36. data/templates/app/assets/javascripts/lib/backbone.coffee +27 -0
  37. data/templates/app/assets/javascripts/lib/backbone/collection.js +249 -0
  38. data/templates/app/assets/javascripts/lib/backbone/events.js +64 -0
  39. data/templates/app/assets/javascripts/lib/backbone/helpers.js +68 -0
  40. data/templates/app/assets/javascripts/lib/backbone/history.js +144 -0
  41. data/templates/app/assets/javascripts/lib/backbone/model.js +291 -0
  42. data/templates/app/assets/javascripts/lib/backbone/router.coffee +45 -0
  43. data/templates/app/assets/javascripts/lib/backbone/sync.coffee +38 -0
  44. data/templates/app/assets/javascripts/lib/backbone/view.js +150 -0
  45. data/templates/app/assets/javascripts/lib/backbone_extended.coffee +276 -0
  46. data/templates/app/assets/javascripts/lib/bootstrap.js +1836 -0
  47. data/templates/app/assets/javascripts/lib/inflection.js +658 -0
  48. data/templates/app/assets/javascripts/lib/jquery-ui.js +343 -0
  49. data/templates/app/assets/javascripts/lib/jquery.hotkeys.js +102 -0
  50. data/templates/app/assets/javascripts/lib/jquery.js +4 -0
  51. data/templates/app/assets/javascripts/lib/milk.js.coffee +265 -0
  52. data/templates/app/assets/javascripts/lib/raw.js +143 -0
  53. data/templates/app/assets/javascripts/lib/strftime.js +732 -0
  54. data/templates/app/assets/javascripts/lib/throttle-debounce.js +251 -0
  55. data/templates/app/assets/javascripts/lib/timeago.js +148 -0
  56. data/templates/app/assets/javascripts/lib/underscore.js +28 -0
  57. data/templates/app/assets/styles/application.sass +21 -0
  58. data/templates/app/assets/styles/layouts/default.sass +15 -0
  59. data/templates/app/assets/styles/layouts/footer.sass +0 -0
  60. data/templates/app/assets/styles/layouts/forms.sass +34 -0
  61. data/templates/app/assets/styles/layouts/header.sass +0 -0
  62. data/templates/app/assets/styles/layouts/navigation.sass +0 -0
  63. data/templates/app/assets/styles/lib/backbone-ui.css +580 -0
  64. data/templates/app/assets/styles/lib/bootstrap.sass +4248 -0
  65. data/templates/app/assets/styles/pages/home.sass +0 -0
  66. data/templates/app/assets/styles/sessions/new.sass +0 -0
  67. data/templates/app/assets/styles/users/activate.sass +0 -0
  68. data/templates/app/assets/styles/users/new.sass +0 -0
  69. data/templates/app/assets/styles/users/suspended.sass +0 -0
  70. data/templates/app/controllers/app_controller.rb +14 -0
  71. data/templates/app/controllers/pages_controller.rb +2 -0
  72. data/templates/app/controllers/sessions_controller.rb +2 -0
  73. data/templates/app/controllers/templating_controller.rb +31 -0
  74. data/templates/app/controllers/users_controller.rb +2 -0
  75. data/templates/app/helpers/app_helper.rb +94 -0
  76. data/templates/app/helpers/users_helper.rb +53 -0
  77. data/templates/app/models/user.rb +9 -0
  78. data/templates/app/views/devise/confirmations/new.haml +9 -0
  79. data/templates/app/views/devise/passwords/edit.haml +11 -0
  80. data/templates/app/views/devise/passwords/new.haml +9 -0
  81. data/templates/app/views/devise/registrations/edit.haml +13 -0
  82. data/templates/app/views/devise/registrations/new.haml +8 -0
  83. data/templates/app/views/devise/sessions/_form.haml +7 -0
  84. data/templates/app/views/devise/sessions/new.haml +5 -0
  85. data/templates/app/views/devise/unlocks/new.haml +7 -0
  86. data/templates/app/views/layouts/_ascii.haml +0 -0
  87. data/templates/app/views/layouts/_column.haml +0 -0
  88. data/templates/app/views/layouts/_content.haml +1 -0
  89. data/templates/app/views/layouts/_extra.haml +0 -0
  90. data/templates/app/views/layouts/_footer.haml +9 -0
  91. data/templates/app/views/layouts/_head.haml +13 -0
  92. data/templates/app/views/layouts/_header.haml +6 -0
  93. data/templates/app/views/layouts/application.html.haml +16 -0
  94. data/templates/app/views/pages/home.haml +1 -0
  95. data/templates/config/app.yml +29 -0
  96. data/templates/config/application.erb +28 -0
  97. data/templates/config/database.yml +32 -0
  98. data/templates/config/initializers/devise.rb +232 -0
  99. data/templates/config/initializers/rabl_init.rb +4 -0
  100. data/templates/config/initializers/setup_mail.rb +13 -0
  101. data/templates/config/initializers/wrap_parameters.rb +12 -0
  102. data/templates/db/migrate/user_migration.rb +36 -0
  103. data/templates/laces_gitignore +9 -0
  104. data/templates/lib/development_mail_interceptor.rb +7 -0
  105. data/templates/lib/templating.rb +40 -0
  106. metadata +225 -0
@@ -0,0 +1,291 @@
1
+ // Backbone.Model
2
+ // --------------
3
+
4
+ // Create a new model, with defined attributes. A client id (`cid`)
5
+ // is automatically generated and assigned for you.
6
+ Backbone.Model = function(attributes, options) {
7
+ var defaults;
8
+ attributes || (attributes = {});
9
+ if (defaults = this.defaults) {
10
+ if (_.isFunction(defaults)) defaults = defaults.call(this);
11
+ attributes = _.extend({}, defaults, attributes);
12
+ }
13
+ this.attributes = {};
14
+ this._escapedAttributes = {};
15
+ this.cid = _.uniqueId('c');
16
+ this.set(attributes, {silent : true});
17
+ this._changed = false;
18
+ this._previousAttributes = _.clone(this.attributes);
19
+ if (options && options.collection) this.collection = options.collection;
20
+ this.initialize(attributes, options);
21
+ };
22
+
23
+ // Attach all inheritable methods to the Model prototype.
24
+ _.extend(Backbone.Model.prototype, Backbone.Events, {
25
+
26
+ // Has the item been changed since the last `"change"` event?
27
+ _changed : false,
28
+
29
+ // The default name for the JSON `id` attribute is `"id"`. MongoDB and
30
+ // CouchDB users may want to set this to `"_id"`.
31
+ idAttribute : 'id',
32
+
33
+ // Initialize is an empty function by default. Override it with your own
34
+ // initialization logic.
35
+ initialize : function(){},
36
+
37
+ // Return a copy of the model's `attributes` object.
38
+ toJSON : function() {
39
+ return _.clone(this.attributes);
40
+ },
41
+
42
+ // Get the value of an attribute.
43
+ get : function(attr) {
44
+ return this.attributes[attr];
45
+ },
46
+
47
+ // Get the HTML-escaped value of an attribute.
48
+ escape : function(attr) {
49
+ var html;
50
+ if (html = this._escapedAttributes[attr]) return html;
51
+ var val = this.attributes[attr];
52
+ return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
53
+ },
54
+
55
+ // Returns `true` if the attribute contains a value that is not null
56
+ // or undefined.
57
+ has : function(attr) {
58
+ return this.attributes[attr] != null;
59
+ },
60
+
61
+ // Set a hash of model attributes on the object, firing `"change"` unless you
62
+ // choose to silence it.
63
+ set : function(attrs, options) {
64
+
65
+ // Extract attributes and options.
66
+ options || (options = {});
67
+ if (!attrs) return this;
68
+ if (attrs.attributes) attrs = attrs.attributes;
69
+ var now = this.attributes, escaped = this._escapedAttributes;
70
+
71
+ // Run validation.
72
+ if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
73
+
74
+ // Check for changes of `id`.
75
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
76
+
77
+ // We're about to start triggering change events.
78
+ var alreadyChanging = this._changing;
79
+ this._changing = true;
80
+
81
+ // Update attributes.
82
+ for (var attr in attrs) {
83
+ var val = attrs[attr];
84
+ if (!_.isEqual(now[attr], val)) {
85
+ now[attr] = val;
86
+ delete escaped[attr];
87
+ this._changed = true;
88
+ if (!options.silent) this.trigger('change:' + attr, this, val, options);
89
+ }
90
+ }
91
+
92
+ // Fire the `"change"` event, if the model has been changed.
93
+ if (!alreadyChanging && !options.silent && this._changed) this.change(options);
94
+ this._changing = false;
95
+ return this;
96
+ },
97
+
98
+ // Remove an attribute from the model, firing `"change"` unless you choose
99
+ // to silence it. `unset` is a noop if the attribute doesn't exist.
100
+ unset : function(attr, options) {
101
+ if (!(attr in this.attributes)) return this;
102
+ options || (options = {});
103
+ var value = this.attributes[attr];
104
+
105
+ // Run validation.
106
+ var validObj = {};
107
+ validObj[attr] = void 0;
108
+ if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
109
+
110
+ // changedAttributes needs to know if an attribute has been unset.
111
+ (this._unsetAttributes || (this._unsetAttributes = [])).push(attr);
112
+
113
+ // Remove the attribute.
114
+ delete this.attributes[attr];
115
+ delete this._escapedAttributes[attr];
116
+ if (attr == this.idAttribute) delete this.id;
117
+ this._changed = true;
118
+ if (!options.silent) {
119
+ this.trigger('change:' + attr, this, void 0, options);
120
+ this.change(options);
121
+ }
122
+ return this;
123
+ },
124
+
125
+ // Clear all attributes on the model, firing `"change"` unless you choose
126
+ // to silence it.
127
+ clear : function(options) {
128
+ options || (options = {});
129
+ var attr;
130
+ var old = this.attributes;
131
+
132
+ // Run validation.
133
+ var validObj = {};
134
+ for (attr in old) validObj[attr] = void 0;
135
+ if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
136
+
137
+ this.attributes = {};
138
+ this._escapedAttributes = {};
139
+ this._changed = true;
140
+ if (!options.silent) {
141
+ for (attr in old) {
142
+ this.trigger('change:' + attr, this, void 0, options);
143
+ }
144
+ this.change(options);
145
+ }
146
+ return this;
147
+ },
148
+
149
+ // Fetch the model from the server. If the server's representation of the
150
+ // model differs from its current attributes, they will be overriden,
151
+ // triggering a `"change"` event.
152
+ fetch : function(options) {
153
+ options || (options = {});
154
+ var model = this;
155
+ var success = options.success;
156
+ options.success = function(resp, status, xhr) {
157
+ if (!model.set(model.parse(resp, xhr), options)) return false;
158
+ if (success) success(model, resp);
159
+ };
160
+ options.error = wrapError(options.error, model, options);
161
+ return (this.sync || Backbone.sync).call(this, 'read', this, options);
162
+ },
163
+
164
+ // Set a hash of model attributes, and sync the model to the server.
165
+ // If the server returns an attributes hash that differs, the model's
166
+ // state will be `set` again.
167
+ save : function(attrs, options) {
168
+ options || (options = {});
169
+ if (attrs && !this.set(attrs, options)) return false;
170
+ var model = this;
171
+ var success = options.success;
172
+ options.success = function(resp, status, xhr) {
173
+ if (!model.set(model.parse(resp, xhr), options)) return false;
174
+ if (success) success(model, resp, xhr);
175
+ };
176
+ options.error = wrapError(options.error, model, options);
177
+ var method = this.isNew() ? 'create' : 'update';
178
+ return (this.sync || Backbone.sync).call(this, method, this, options);
179
+ },
180
+
181
+ // Destroy this model on the server if it was already persisted. Upon success, the model is removed
182
+ // from its collection, if it has one.
183
+ destroy : function(options) {
184
+ options || (options = {});
185
+ if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
186
+ var model = this;
187
+ var success = options.success;
188
+ options.success = function(resp) {
189
+ model.trigger('destroy', model, model.collection, options);
190
+ if (success) success(model, resp);
191
+ };
192
+ options.error = wrapError(options.error, model, options);
193
+ return (this.sync || Backbone.sync).call(this, 'delete', this, options);
194
+ },
195
+
196
+ // Default URL for the model's representation on the server -- if you're
197
+ // using Backbone's restful methods, override this to change the endpoint
198
+ // that will be called.
199
+ url : function() {
200
+ var base = getUrl(this.collection) || this.urlRoot || urlError();
201
+ if (this.isNew()) return base;
202
+ return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
203
+ },
204
+
205
+ // **parse** converts a response into the hash of attributes to be `set` on
206
+ // the model. The default implementation is just to pass the response along.
207
+ parse : function(resp, xhr) {
208
+ return resp;
209
+ },
210
+
211
+ // Create a new model with identical attributes to this one.
212
+ clone : function() {
213
+ return new this.constructor(this);
214
+ },
215
+
216
+ // A model is new if it has never been saved to the server, and lacks an id.
217
+ isNew : function() {
218
+ return this.id == null;
219
+ },
220
+
221
+ // Call this method to manually fire a `change` event for this model.
222
+ // Calling this will cause all objects observing the model to update.
223
+ change : function(options) {
224
+ this.trigger('change', this, options);
225
+ this._previousAttributes = _.clone(this.attributes);
226
+ this._unsetAttributes = null;
227
+ this._changed = false;
228
+ },
229
+
230
+ // Determine if the model has changed since the last `"change"` event.
231
+ // If you specify an attribute name, determine if that attribute has changed.
232
+ hasChanged : function(attr) {
233
+ if (attr) return this._previousAttributes[attr] != this.attributes[attr];
234
+ return this._changed;
235
+ },
236
+
237
+ // Return an object containing all the attributes that have changed, or false
238
+ // if there are no changed attributes. Useful for determining what parts of a
239
+ // view need to be updated and/or what attributes need to be persisted to
240
+ // the server. Unset attributes will be set to undefined.
241
+ changedAttributes : function(now) {
242
+ now || (now = this.attributes);
243
+ var old = this._previousAttributes, unset = this._unsetAttributes;
244
+
245
+ var changed = false;
246
+ for (var attr in now) {
247
+ if (!_.isEqual(old[attr], now[attr])) {
248
+ changed || (changed = {});
249
+ changed[attr] = now[attr];
250
+ }
251
+ }
252
+
253
+ if (unset) {
254
+ changed || (changed = {});
255
+ var len = unset.length;
256
+ while (len--) changed[unset[len]] = void 0;
257
+ }
258
+
259
+ return changed;
260
+ },
261
+
262
+ // Get the previous value of an attribute, recorded at the time the last
263
+ // `"change"` event was fired.
264
+ previous : function(attr) {
265
+ if (!attr || !this._previousAttributes) return null;
266
+ return this._previousAttributes[attr];
267
+ },
268
+
269
+ // Get all of the attributes of the model at the time of the previous
270
+ // `"change"` event.
271
+ previousAttributes : function() {
272
+ return _.clone(this._previousAttributes);
273
+ },
274
+
275
+ // Run validation against a set of incoming attributes, returning `true`
276
+ // if all is well. If a specific `error` callback has been passed,
277
+ // call that instead of firing the general `"error"` event.
278
+ _performValidation : function(attrs, options) {
279
+ var error = this.validate(attrs);
280
+ if (error) {
281
+ if (options.error) {
282
+ options.error(this, error, options);
283
+ } else {
284
+ this.trigger('error', this, error, options);
285
+ }
286
+ return false;
287
+ }
288
+ return true;
289
+ }
290
+
291
+ });
@@ -0,0 +1,45 @@
1
+ Backbone.Router = (options) ->
2
+ options or (options = {})
3
+ @routes = options.routes if options.routes
4
+ @_bindRoutes()
5
+ @initialize.apply this, arguments
6
+
7
+ namedParam = /:([\w\d]+)/g
8
+ splatParam = /\*([\w\d]+)/g
9
+ escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
10
+ _.extend Backbone.Router::, Backbone.Events,
11
+ initialize: ->
12
+ route: (route, name, callback) ->
13
+ Backbone.history or (Backbone.history = new Backbone.History)
14
+ route = @_routeToRegExp route unless _.isRegExp(route)
15
+ Backbone.history.route route, _.bind((fragment) ->
16
+ args = @_extractParameters(route, fragment)
17
+ callback and callback.apply(this, args)
18
+ @trigger.apply this, [ "route:" + name ].concat(args)
19
+ , this)
20
+
21
+ navigate: (fragment, triggerRoute) ->
22
+ Backbone.history.navigate fragment, triggerRoute
23
+
24
+ _bindRoutes: ->
25
+ return unless @routes
26
+ routes = []
27
+ for route of @routes
28
+ routes.unshift [ route, @routes[route] ]
29
+ i = 0
30
+ l = routes.length
31
+
32
+ while i < l
33
+ @route routes[i][0],
34
+ routes[i][1],
35
+ this[routes[i][1]]
36
+ i++
37
+
38
+ _routeToRegExp: (route) ->
39
+ route = route.replace escapeRegExp, "\\$&"
40
+ route = route.replace namedParam , "([^/]*)"
41
+ route = route.replace splatParam , "(.*?)"
42
+ new RegExp("^" + route + "$")
43
+
44
+ _extractParameters: (route, fragment) ->
45
+ route.exec(fragment).slice 1
@@ -0,0 +1,38 @@
1
+ Backbone.sync = (method, model, options) ->
2
+ type = methodMap[method]
3
+ params =
4
+ type: type
5
+ dataType: "json"
6
+
7
+ params.url = getUrl(model) or urlError() unless options.url
8
+
9
+ if not options.data and model and (method is 'create' or method is 'update')
10
+ extra = options.extra
11
+ data = model.toJSON()
12
+ data = _.extend data, extra unless extra is undefined
13
+ params.contentType = "application/json"
14
+ params.data = JSON.stringify data
15
+
16
+ if method is 'delete'
17
+ param = $("meta[name= 'csrf-param']").attr 'content'
18
+ token = $("meta[name= 'csrf-token']").attr 'content'
19
+ data = authenticity_token: token
20
+ params.contentType = 'application/json'
21
+ params.data = JSON.stringify data
22
+
23
+ emulate_json = Backbone.emulateJSON
24
+ if emulate_json
25
+ params.contentType = 'application/x-www-form-urlencoded'
26
+ params.data = if params.data then model: params.data else {}
27
+
28
+ if Backbone.emulateHTTP
29
+ if type is 'PUT' or type is 'DELETE'
30
+ params.data._method = type if emulate_json
31
+ params.type = 'POST'
32
+ params.beforeSend = (xhr) -> xhr.setRequestHeader "X-HTTP-Method-Override", type
33
+
34
+ if params.type isnt 'GET' and not emulate_json
35
+ params.processData = false
36
+
37
+ params = _.extend params, options
38
+ $.ajax params
@@ -0,0 +1,150 @@
1
+ // Backbone.View
2
+ // -------------
3
+
4
+ // Creating a Backbone.View creates its initial element outside of the DOM,
5
+ // if an existing element is not provided...
6
+ Backbone.View = function(options) {
7
+ this.cid = _.uniqueId('view');
8
+ this._configure(options || {});
9
+ this._ensureElement();
10
+ this.delegateEvents();
11
+ this.initialize.apply(this, arguments);
12
+ };
13
+
14
+ // Element lookup, scoped to DOM elements within the current view.
15
+ // This should be prefered to global lookups, if you're dealing with
16
+ // a specific view.
17
+ var selectorDelegate = function(selector) {
18
+ return $(selector, this.el);
19
+ };
20
+
21
+ // Cached regex to split keys for `delegate`.
22
+ var eventSplitter = /^(\S+)\s*(.*)$/;
23
+
24
+ // List of view options to be merged as properties.
25
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
26
+
27
+ // Set up all inheritable **Backbone.View** properties and methods.
28
+ _.extend(Backbone.View.prototype, Backbone.Events, {
29
+
30
+ // The default `tagName` of a View's element is `"div"`.
31
+ tagName : 'div',
32
+
33
+ // Attach the `selectorDelegate` function as the `$` property.
34
+ $ : selectorDelegate,
35
+
36
+ // Initialize is an empty function by default. Override it with your own
37
+ // initialization logic.
38
+ initialize : function(){},
39
+
40
+ // **render** is the core function that your view should override, in order
41
+ // to populate its element (`this.el`), with the appropriate HTML. The
42
+ // convention is for **render** to always return `this`.
43
+ render : function() {
44
+ return this;
45
+ },
46
+
47
+ // Remove this view from the DOM. Note that the view isn't present in the
48
+ // DOM by default, so calling this method may be a no-op.
49
+ remove : function() {
50
+ $(this.el).remove();
51
+ return this;
52
+ },
53
+
54
+ // For small amounts of DOM Elements, where a full-blown template isn't
55
+ // needed, use **make** to manufacture elements, one at a time.
56
+ //
57
+ // var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
58
+ //
59
+ make : function(tagName, attributes, content) {
60
+ var el = document.createElement(tagName);
61
+ if (attributes) $(el).attr(attributes);
62
+ if (content) $(el).html(content);
63
+ return el;
64
+ },
65
+
66
+ // Set callbacks, where `this.events` is a hash of
67
+ //
68
+ // *{"event selector": "callback"}*
69
+ //
70
+ // {
71
+ // 'mousedown .title': 'edit',
72
+ // 'click .button': 'save'
73
+ // }
74
+ //
75
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
76
+ // Uses event delegation for efficiency.
77
+ // Omitting the selector binds the event to `this.el`.
78
+ // This only works for delegate-able events: not `focus`, `blur`, and
79
+ // not `change`, `submit`, and `reset` in Internet Explorer.
80
+ delegateEvents : function(events) {
81
+ if (!(events || (events = this.events))) return;
82
+ if (_.isFunction(events)) events = events.call(this);
83
+ this.undelegateEvents();
84
+ for (var key in events) {
85
+ var method = this[events[key]];
86
+ if (!method) throw new Error('Event "' + events[key] + '" does not exist');
87
+ var match = key.match(eventSplitter);
88
+ var eventName = match[1], selector = match[2];
89
+ method = _.bind(method, this);
90
+ eventName += '.delegateEvents' + this.cid;
91
+ if (selector === '') {
92
+ $(this.el).bind(eventName, method);
93
+ } else {
94
+ $(this.el).delegate(selector, eventName, method);
95
+ }
96
+ }
97
+ },
98
+
99
+ // Clears all callbacks previously bound to the view with `delegateEvents`.
100
+ undelegateEvents: function() {
101
+ $(this.el).unbind('.delegateEvents' + this.cid);
102
+ },
103
+
104
+ // Performs the initial configuration of a View with a set of options.
105
+ // Keys with special meaning *(model, collection, id, className)*, are
106
+ // attached directly to the view.
107
+ _configure : function(options) {
108
+ if (this.options) options = _.extend({}, this.options, options);
109
+ for (var i = 0, l = viewOptions.length; i < l; i++) {
110
+ var attr = viewOptions[i];
111
+ if (options[attr]) this[attr] = options[attr];
112
+ }
113
+ this.options = options;
114
+ },
115
+
116
+ // Ensure that the View has a DOM element to render into.
117
+ // If `this.el` is a string, pass it through `$()`, take the first
118
+ // matching element, and re-assign it to `el`. Otherwise, create
119
+ // an element from the `id`, `className` and `tagName` properties.
120
+ _ensureElement : function() {
121
+ if (!this.el) {
122
+ var attrs = this.attributes || {};
123
+ if (this.id) attrs.id = this.id;
124
+ if (this.className) attrs['class'] = this.className;
125
+ this.el = this.make(this.tagName, attrs);
126
+ } else if (_.isString(this.el)) {
127
+ this.el = $(this.el).get(0);
128
+ }
129
+ }
130
+
131
+ });
132
+
133
+ // The self-propagating extend function that Backbone classes use.
134
+ var extend = function (protoProps, classProps) {
135
+ var child = inherits(this, protoProps, classProps);
136
+ child.extend = this.extend;
137
+ return child;
138
+ };
139
+
140
+ // Set up inheritance for the model, collection, and view.
141
+ Backbone.Model.extend = Backbone.Collection.extend =
142
+ Backbone.Router.extend = Backbone.View.extend = extend;
143
+
144
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
145
+ var methodMap = {
146
+ 'create': 'POST',
147
+ 'update': 'PUT',
148
+ 'delete': 'DELETE',
149
+ 'read' : 'GET'
150
+ };