laces 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +122 -0
- data/LICENSE +21 -0
- data/README.md +5 -0
- data/Rakefile +8 -0
- data/bin/laces +16 -0
- data/features/creating_a_heroku_app.feature +9 -0
- data/features/rake_clean.feature +21 -0
- data/features/skipping_clearance.feature +13 -0
- data/features/step_definitions/gem_steps.rb +5 -0
- data/features/step_definitions/heroku_steps.rb +3 -0
- data/features/step_definitions/shell_steps.rb +55 -0
- data/features/support/bin/heroku +5 -0
- data/features/support/env.rb +15 -0
- data/features/support/fake_heroku.rb +21 -0
- data/laces-0.0.1.gem +0 -0
- data/laces.gemspec +35 -0
- data/lib/laces/actions.rb +35 -0
- data/lib/laces/app_builder.rb +237 -0
- data/lib/laces/generators/app_generator.rb +111 -0
- data/lib/laces/version.rb +3 -0
- data/templates/.DS_Store +0 -0
- data/templates/Gemfile_template +76 -0
- data/templates/HEROKU_README.md +66 -0
- data/templates/Procfile +1 -0
- data/templates/README.md +81 -0
- data/templates/app/assets/imgs/glyphicons-halflings-white.png +0 -0
- data/templates/app/assets/imgs/glyphicons-halflings.png +0 -0
- data/templates/app/assets/javascripts/admin.coffee +20 -0
- data/templates/app/assets/javascripts/application.coffee +21 -0
- data/templates/app/assets/javascripts/lib/actinology.coffee +47 -0
- data/templates/app/assets/javascripts/lib/analytics.js +11 -0
- data/templates/app/assets/javascripts/lib/auth_token_sync.js +17 -0
- data/templates/app/assets/javascripts/lib/backbone-ui.js +2455 -0
- data/templates/app/assets/javascripts/lib/backbone.coffee +27 -0
- data/templates/app/assets/javascripts/lib/backbone/collection.js +249 -0
- data/templates/app/assets/javascripts/lib/backbone/events.js +64 -0
- data/templates/app/assets/javascripts/lib/backbone/helpers.js +68 -0
- data/templates/app/assets/javascripts/lib/backbone/history.js +144 -0
- data/templates/app/assets/javascripts/lib/backbone/model.js +291 -0
- data/templates/app/assets/javascripts/lib/backbone/router.coffee +45 -0
- data/templates/app/assets/javascripts/lib/backbone/sync.coffee +38 -0
- data/templates/app/assets/javascripts/lib/backbone/view.js +150 -0
- data/templates/app/assets/javascripts/lib/backbone_extended.coffee +276 -0
- data/templates/app/assets/javascripts/lib/bootstrap.js +1836 -0
- data/templates/app/assets/javascripts/lib/inflection.js +658 -0
- data/templates/app/assets/javascripts/lib/jquery-ui.js +343 -0
- data/templates/app/assets/javascripts/lib/jquery.hotkeys.js +102 -0
- data/templates/app/assets/javascripts/lib/jquery.js +4 -0
- data/templates/app/assets/javascripts/lib/milk.js.coffee +265 -0
- data/templates/app/assets/javascripts/lib/raw.js +143 -0
- data/templates/app/assets/javascripts/lib/strftime.js +732 -0
- data/templates/app/assets/javascripts/lib/throttle-debounce.js +251 -0
- data/templates/app/assets/javascripts/lib/timeago.js +148 -0
- data/templates/app/assets/javascripts/lib/underscore.js +28 -0
- data/templates/app/assets/styles/application.sass +21 -0
- data/templates/app/assets/styles/layouts/default.sass +15 -0
- data/templates/app/assets/styles/layouts/footer.sass +0 -0
- data/templates/app/assets/styles/layouts/forms.sass +34 -0
- data/templates/app/assets/styles/layouts/header.sass +0 -0
- data/templates/app/assets/styles/layouts/navigation.sass +0 -0
- data/templates/app/assets/styles/lib/backbone-ui.css +580 -0
- data/templates/app/assets/styles/lib/bootstrap.sass +4248 -0
- data/templates/app/assets/styles/pages/home.sass +0 -0
- data/templates/app/assets/styles/sessions/new.sass +0 -0
- data/templates/app/assets/styles/users/activate.sass +0 -0
- data/templates/app/assets/styles/users/new.sass +0 -0
- data/templates/app/assets/styles/users/suspended.sass +0 -0
- data/templates/app/controllers/app_controller.rb +14 -0
- data/templates/app/controllers/pages_controller.rb +2 -0
- data/templates/app/controllers/sessions_controller.rb +2 -0
- data/templates/app/controllers/templating_controller.rb +31 -0
- data/templates/app/controllers/users_controller.rb +2 -0
- data/templates/app/helpers/app_helper.rb +94 -0
- data/templates/app/helpers/users_helper.rb +53 -0
- data/templates/app/models/user.rb +9 -0
- data/templates/app/views/devise/confirmations/new.haml +9 -0
- data/templates/app/views/devise/passwords/edit.haml +11 -0
- data/templates/app/views/devise/passwords/new.haml +9 -0
- data/templates/app/views/devise/registrations/edit.haml +13 -0
- data/templates/app/views/devise/registrations/new.haml +8 -0
- data/templates/app/views/devise/sessions/_form.haml +7 -0
- data/templates/app/views/devise/sessions/new.haml +5 -0
- data/templates/app/views/devise/unlocks/new.haml +7 -0
- data/templates/app/views/layouts/_ascii.haml +0 -0
- data/templates/app/views/layouts/_column.haml +0 -0
- data/templates/app/views/layouts/_content.haml +1 -0
- data/templates/app/views/layouts/_extra.haml +0 -0
- data/templates/app/views/layouts/_footer.haml +9 -0
- data/templates/app/views/layouts/_head.haml +13 -0
- data/templates/app/views/layouts/_header.haml +6 -0
- data/templates/app/views/layouts/application.html.haml +16 -0
- data/templates/app/views/pages/home.haml +1 -0
- data/templates/config/app.yml +29 -0
- data/templates/config/application.erb +28 -0
- data/templates/config/database.yml +32 -0
- data/templates/config/initializers/devise.rb +232 -0
- data/templates/config/initializers/rabl_init.rb +4 -0
- data/templates/config/initializers/setup_mail.rb +13 -0
- data/templates/config/initializers/wrap_parameters.rb +12 -0
- data/templates/db/migrate/user_migration.rb +36 -0
- data/templates/laces_gitignore +9 -0
- data/templates/lib/development_mail_interceptor.rb +7 -0
- data/templates/lib/templating.rb +40 -0
- 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
|
+
};
|