laces 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ };