chiropractor 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chiropractor.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Andrew Eberbach
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # Chiropractor
2
+
3
+ I miss ActiveSupport. I really do. The javascript standard library is lacking. So this is a collection of some useful Backbone and Underscore components. Saves me from having to keep downloading them into every project. This seems like a reasonable stack that gives you pretty powerful framework for building backbone apps.
4
+
5
+ Included:
6
+
7
+ * Underscore.Inflector: https://github.com/jeremyruppel/underscore.inflection
8
+ * Underscore.String: https://github.com/epeli/underscore.string
9
+ * Backbone.Marionette: https://github.com/marionettejs/backbone.marionette
10
+ * Backbone.Relational: https://github.com/PaulUithol/Backbone-relational
11
+ * Backbone.ModelBinder: https://github.com/theironcook/Backbone.ModelBinder
12
+ * AccountingJS: http://josscrowcroft.github.com/accounting.js/
13
+ * MomentJS: https://github.com/timrwood/moment/
14
+
15
+ Also have a couple extensions:
16
+
17
+ * Backbone.LayoutRegion: Combines Layouts and Regions from Marionette
18
+ * Backbone.JST: Adds JST support to Marionette
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'chiropractor'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install chiropractor
33
+
34
+ ## Usage
35
+
36
+ ```javascript
37
+ //= require chiropractor
38
+ ```
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ scripts = {
4
+ "https://github.com/jeremyruppel/underscore.inflection" => "https://raw.github.com/jeremyruppel/underscore.inflection/master/src/underscore.inflection.js",
5
+ "https://github.com/epeli/underscore.string" => "https://raw.github.com/epeli/underscore.string/master/lib/underscore.string.js",
6
+ "https://github.com/timrwood/moment/" => "https://raw.github.com/timrwood/moment/master/moment.js",
7
+ "http://josscrowcroft.github.com/accounting.js/" => "https://raw.github.com/josscrowcroft/accounting.js/master/accounting.js",
8
+ "https://github.com/PaulUithol/Backbone-relational" => "https://raw.github.com/PaulUithol/Backbone-relational/master/backbone-relational.js",
9
+ "https://github.com/theironcook/Backbone.ModelBinder" => "https://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.ModelBinder.js"
10
+ }
11
+
12
+ require 'open-uri'
13
+ desc "Update scripts"
14
+ task :update_scripts do
15
+ dir = File.expand_path("../vendor/assets/javascripts/chiropractor", __FILE__)
16
+ FileUtils.mkdir_p(dir)
17
+ scripts.each do |repo_url, file_url|
18
+ file = File.basename(URI.parse(file_url).path)
19
+ output = File.join(dir,file)
20
+ File.open(output, "w+"){|f| f.write(open(file_url).read)}
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chiropractor/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "chiropractor"
8
+ gem.version = Chiropractor::VERSION
9
+ gem.authors = ["Andrew Eberbach"]
10
+ gem.email = ["andrew@ebertech.ca"]
11
+ gem.description = %q{A helpful collection of tools to help your Backbone}
12
+ gem.summary = %q{A helpful collection of tools to help your Backbone}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_runtime_dependency "backbone-on-rails"
21
+ gem.add_runtime_dependency "marionette-rails"
22
+ gem.add_runtime_dependency "coffee-rails"
23
+ gem.add_runtime_dependency "jquery-rails"
24
+ end
@@ -0,0 +1,11 @@
1
+ require "backbone-on-rails"
2
+ require "marionette-rails"
3
+ require "coffee-rails"
4
+ require "jquery-rails"
5
+
6
+ module Chiropractor
7
+ autoload :Engine, 'chiropractor/engine'
8
+ autoload :VERSION, 'chiropractor/version'
9
+ end
10
+
11
+ Chiropractor::Engine
@@ -0,0 +1,6 @@
1
+ module Chiropractor
2
+ class Engine < Rails::Engine
3
+
4
+ end
5
+ end
6
+
@@ -0,0 +1,3 @@
1
+ module Chiropractor
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,482 @@
1
+ // Backbone.ModelBinder v0.1.6
2
+ // (c) 2012 Bart Wood
3
+ // Distributed Under MIT License
4
+
5
+ (function (factory) {
6
+ if (typeof define === 'function' && define.amd) {
7
+ // AMD. Register as an anonymous module.
8
+ define(['underscore', 'jquery', 'backbone'], factory);
9
+ } else {
10
+ // Browser globals
11
+ factory(_, $, Backbone);
12
+ }
13
+ }(function(_, $, Backbone){
14
+
15
+ if(!Backbone){
16
+ throw 'Please include Backbone.js before Backbone.ModelBinder.js';
17
+ }
18
+
19
+ Backbone.ModelBinder = function(modelSetOptions){
20
+ _.bindAll(this);
21
+ this._modelSetOptions = modelSetOptions || {};
22
+ };
23
+
24
+ // Current version of the library.
25
+ Backbone.ModelBinder.VERSION = '0.1.6';
26
+ Backbone.ModelBinder.Constants = {};
27
+ Backbone.ModelBinder.Constants.ModelToView = 'ModelToView';
28
+ Backbone.ModelBinder.Constants.ViewToModel = 'ViewToModel';
29
+
30
+ _.extend(Backbone.ModelBinder.prototype, {
31
+
32
+ bind:function (model, rootEl, attributeBindings, modelSetOptions) {
33
+ this.unbind();
34
+
35
+ this._model = model;
36
+ this._rootEl = rootEl;
37
+ this._modelSetOptions = _.extend({}, this._modelSetOptions, modelSetOptions);
38
+
39
+ if (!this._model) throw 'model must be specified';
40
+ if (!this._rootEl) throw 'rootEl must be specified';
41
+
42
+ if(attributeBindings){
43
+ // Create a deep clone of the attribute bindings
44
+ this._attributeBindings = $.extend(true, {}, attributeBindings);
45
+
46
+ this._initializeAttributeBindings();
47
+ this._initializeElBindings();
48
+ }
49
+ else {
50
+ this._initializeDefaultBindings();
51
+ }
52
+
53
+ this._bindModelToView();
54
+ this._bindViewToModel();
55
+ },
56
+
57
+ bindCustomTriggers: function (model, rootEl, triggers, attributeBindings, modelSetOptions) {
58
+ this._triggers = triggers;
59
+ this.bind(model, rootEl, attributeBindings, modelSetOptions)
60
+ },
61
+
62
+ unbind:function () {
63
+ this._unbindModelToView();
64
+ this._unbindViewToModel();
65
+
66
+ if(this._attributeBindings){
67
+ delete this._attributeBindings;
68
+ this._attributeBindings = undefined;
69
+ }
70
+ },
71
+
72
+ // Converts the input bindings, which might just be empty or strings, to binding objects
73
+ _initializeAttributeBindings:function () {
74
+ var attributeBindingKey, inputBinding, attributeBinding, elementBindingCount, elementBinding;
75
+
76
+ for (attributeBindingKey in this._attributeBindings) {
77
+ inputBinding = this._attributeBindings[attributeBindingKey];
78
+
79
+ if (_.isString(inputBinding)) {
80
+ attributeBinding = {elementBindings: [{selector: inputBinding}]};
81
+ }
82
+ else if (_.isArray(inputBinding)) {
83
+ attributeBinding = {elementBindings: inputBinding};
84
+ }
85
+ else if(_.isObject(inputBinding)){
86
+ attributeBinding = {elementBindings: [inputBinding]};
87
+ }
88
+ else {
89
+ throw 'Unsupported type passed to Model Binder ' + attributeBinding;
90
+ }
91
+
92
+ // Add a linkage from the element binding back to the attribute binding
93
+ for(elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++){
94
+ elementBinding = attributeBinding.elementBindings[elementBindingCount];
95
+ elementBinding.attributeBinding = attributeBinding;
96
+ }
97
+
98
+ attributeBinding.attributeName = attributeBindingKey;
99
+ this._attributeBindings[attributeBindingKey] = attributeBinding;
100
+ }
101
+ },
102
+
103
+ // If the bindings are not specified, the default binding is performed on the name attribute
104
+ _initializeDefaultBindings: function(){
105
+ var elCount, namedEls, namedEl, name, attributeBinding;
106
+ this._attributeBindings = {};
107
+ namedEls = $('[name]', this._rootEl);
108
+
109
+ for(elCount = 0; elCount < namedEls.length; elCount++){
110
+ namedEl = namedEls[elCount];
111
+ name = $(namedEl).attr('name');
112
+
113
+ // For elements like radio buttons we only want a single attribute binding with possibly multiple element bindings
114
+ if(!this._attributeBindings[name]){
115
+ attributeBinding = {attributeName: name};
116
+ attributeBinding.elementBindings = [{attributeBinding: attributeBinding, boundEls: [namedEl]}];
117
+ this._attributeBindings[name] = attributeBinding;
118
+ }
119
+ else{
120
+ this._attributeBindings[name].elementBindings.push({attributeBinding: this._attributeBindings[name], boundEls: [namedEl]});
121
+ }
122
+ }
123
+ },
124
+
125
+ _initializeElBindings:function () {
126
+ var bindingKey, attributeBinding, bindingCount, elementBinding, foundEls, elCount, el;
127
+ for (bindingKey in this._attributeBindings) {
128
+ attributeBinding = this._attributeBindings[bindingKey];
129
+
130
+ for (bindingCount = 0; bindingCount < attributeBinding.elementBindings.length; bindingCount++) {
131
+ elementBinding = attributeBinding.elementBindings[bindingCount];
132
+ if (elementBinding.selector === '') {
133
+ foundEls = $(this._rootEl);
134
+ }
135
+ else {
136
+ foundEls = $(elementBinding.selector, this._rootEl);
137
+ }
138
+
139
+ if (foundEls.length === 0) {
140
+ throw 'Bad binding found. No elements returned for binding selector ' + elementBinding.selector;
141
+ }
142
+ else {
143
+ elementBinding.boundEls = [];
144
+ for (elCount = 0; elCount < foundEls.length; elCount++) {
145
+ el = foundEls[elCount];
146
+ elementBinding.boundEls.push(el);
147
+ }
148
+ }
149
+ }
150
+ }
151
+ },
152
+
153
+ _bindModelToView: function () {
154
+ this._model.on('change', this._onModelChange, this);
155
+
156
+ this.copyModelAttributesToView();
157
+ },
158
+
159
+ // attributesToCopy is an optional parameter - if empty, all attributes
160
+ // that are bound will be copied. Otherwise, only attributeBindings specified
161
+ // in the attributesToCopy are copied.
162
+ copyModelAttributesToView: function(attributesToCopy){
163
+ var attributeName, attributeBinding;
164
+
165
+ for (attributeName in this._attributeBindings) {
166
+ if(attributesToCopy === undefined || _.indexOf(attributesToCopy, attributeName) !== -1){
167
+ attributeBinding = this._attributeBindings[attributeName];
168
+ this._copyModelToView(attributeBinding);
169
+ }
170
+ }
171
+ },
172
+
173
+ _unbindModelToView: function(){
174
+ if(this._model){
175
+ this._model.off('change', this._onModelChange);
176
+ this._model = undefined;
177
+ }
178
+ },
179
+
180
+ _bindViewToModel: function () {
181
+ if (this._triggers) {
182
+ _.each(this._triggers, function (event, selector) {
183
+ $(this._rootEl).delegate(selector, event, this._onElChanged);
184
+ }, this);
185
+ }
186
+ else {
187
+ $(this._rootEl).delegate('', 'change', this._onElChanged);
188
+ // The change event doesn't work properly for contenteditable elements - but blur does
189
+ $(this._rootEl).delegate('[contenteditable]', 'blur', this._onElChanged);
190
+ }
191
+ },
192
+
193
+ _unbindViewToModel: function () {
194
+ if (this._rootEl) {
195
+ if (this._triggers) {
196
+ _.each(this._triggers, function (event, selector) {
197
+ $(this._rootEl).undelegate(selector, event, this._onElChanged);
198
+ }, this);
199
+ }
200
+ else {
201
+ $(this._rootEl).undelegate('', 'change', this._onElChanged);
202
+ $(this._rootEl).undelegate('[contenteditable]', 'blur', this._onElChanged);
203
+ }
204
+ }
205
+ },
206
+
207
+ _onElChanged:function (event) {
208
+ var el, elBindings, elBindingCount, elBinding;
209
+
210
+ el = $(event.target)[0];
211
+ elBindings = this._getElBindings(el);
212
+
213
+ for(elBindingCount = 0; elBindingCount < elBindings.length; elBindingCount++){
214
+ elBinding = elBindings[elBindingCount];
215
+ if (this._isBindingUserEditable(elBinding)) {
216
+ this._copyViewToModel(elBinding, el);
217
+ }
218
+ }
219
+ },
220
+
221
+ _isBindingUserEditable: function(elBinding){
222
+ return elBinding.elAttribute === undefined ||
223
+ elBinding.elAttribute === 'text' ||
224
+ elBinding.elAttribute === 'html';
225
+ },
226
+
227
+ _getElBindings:function (findEl) {
228
+ var attributeName, attributeBinding, elementBindingCount, elementBinding, boundElCount, boundEl;
229
+ var elBindings = [];
230
+
231
+ for (attributeName in this._attributeBindings) {
232
+ attributeBinding = this._attributeBindings[attributeName];
233
+
234
+ for (elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++) {
235
+ elementBinding = attributeBinding.elementBindings[elementBindingCount];
236
+
237
+ for (boundElCount = 0; boundElCount < elementBinding.boundEls.length; boundElCount++) {
238
+ boundEl = elementBinding.boundEls[boundElCount];
239
+
240
+ if (boundEl === findEl) {
241
+ elBindings.push(elementBinding);
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ return elBindings;
248
+ },
249
+
250
+ _onModelChange:function () {
251
+ var changedAttribute, attributeBinding;
252
+
253
+ for (changedAttribute in this._model.changedAttributes()) {
254
+ attributeBinding = this._attributeBindings[changedAttribute];
255
+
256
+ if (attributeBinding) {
257
+ this._copyModelToView(attributeBinding);
258
+ }
259
+ }
260
+ },
261
+
262
+ _copyModelToView:function (attributeBinding) {
263
+ var elementBindingCount, elementBinding, boundElCount, boundEl, value, convertedValue;
264
+
265
+ value = this._model.get(attributeBinding.attributeName);
266
+
267
+ for (elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++) {
268
+ elementBinding = attributeBinding.elementBindings[elementBindingCount];
269
+
270
+ for (boundElCount = 0; boundElCount < elementBinding.boundEls.length; boundElCount++) {
271
+ boundEl = elementBinding.boundEls[boundElCount];
272
+
273
+ if(!boundEl._isSetting){
274
+ convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value);
275
+ this._setEl($(boundEl), elementBinding, convertedValue);
276
+ }
277
+ }
278
+ }
279
+ },
280
+
281
+ _setEl: function (el, elementBinding, convertedValue) {
282
+ if (elementBinding.elAttribute) {
283
+ this._setElAttribute(el, elementBinding, convertedValue);
284
+ }
285
+ else {
286
+ this._setElValue(el, convertedValue);
287
+ }
288
+ },
289
+
290
+ _setElAttribute:function (el, elementBinding, convertedValue) {
291
+ switch (elementBinding.elAttribute) {
292
+ case 'html':
293
+ el.html(convertedValue);
294
+ break;
295
+ case 'text':
296
+ el.text(convertedValue);
297
+ break;
298
+ case 'enabled':
299
+ el.attr('disabled', !convertedValue);
300
+ break;
301
+ case 'displayed':
302
+ el[convertedValue ? 'show' : 'hide']();
303
+ break;
304
+ case 'hidden':
305
+ el[convertedValue ? 'hide' : 'show']();
306
+ break;
307
+ case 'css':
308
+ el.css(elementBinding.cssAttribute, convertedValue);
309
+ break;
310
+ case 'class':
311
+ var previousValue = this._model.previous(elementBinding.attributeBinding.attributeName);
312
+ if(!_.isUndefined(previousValue)){
313
+ previousValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, previousValue);
314
+ el.removeClass(previousValue);
315
+ }
316
+
317
+ if(convertedValue){
318
+ el.addClass(convertedValue);
319
+ }
320
+ break;
321
+ default:
322
+ el.attr(elementBinding.elAttribute, convertedValue);
323
+ }
324
+ },
325
+
326
+ _setElValue:function (el, convertedValue) {
327
+ if(el.attr('type')){
328
+ switch (el.attr('type')) {
329
+ case 'radio':
330
+ if (el.val() === convertedValue) {
331
+ el.attr('checked', 'checked');
332
+ }
333
+ break;
334
+ case 'checkbox':
335
+ if (convertedValue) {
336
+ el.attr('checked', 'checked');
337
+ }
338
+ else {
339
+ el.removeAttr('checked');
340
+ }
341
+ break;
342
+ default:
343
+ el.val(convertedValue);
344
+ }
345
+ }
346
+ else if(el.is('input') || el.is('select') || el.is('textarea')){
347
+ el.val(convertedValue);
348
+ }
349
+ else {
350
+ el.text(convertedValue);
351
+ }
352
+ },
353
+
354
+ _copyViewToModel: function (elementBinding, el) {
355
+ var value, convertedValue;
356
+
357
+ if (!el._isSetting) {
358
+
359
+ el._isSetting = true;
360
+ this._setModel(elementBinding, $(el));
361
+ el._isSetting = false;
362
+
363
+ if(elementBinding.converter){
364
+ value = this._model.get(elementBinding.attributeBinding.attributeName);
365
+ convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value);
366
+ this._setEl($(el), elementBinding, convertedValue);
367
+ }
368
+ }
369
+ },
370
+
371
+ _getElValue: function(elementBinding, el){
372
+ switch (el.attr('type')) {
373
+ case 'checkbox':
374
+ return el.prop('checked') ? true : false;
375
+ default:
376
+ if(el.attr('contenteditable') !== undefined){
377
+ return el.html();
378
+ }
379
+ else {
380
+ return el.val();
381
+ }
382
+ }
383
+ },
384
+
385
+ _setModel: function (elementBinding, el) {
386
+ var data = {};
387
+ var elVal = this._getElValue(elementBinding, el);
388
+ elVal = this._getConvertedValue(Backbone.ModelBinder.Constants.ViewToModel, elementBinding, elVal);
389
+ data[elementBinding.attributeBinding.attributeName] = elVal;
390
+ var opts = _.extend({}, this._modelSetOptions, {changeSource: 'ModelBinder'});
391
+ this._model.set(data, opts);
392
+ },
393
+
394
+ _getConvertedValue: function (direction, elementBinding, value) {
395
+ if (elementBinding.converter) {
396
+ value = elementBinding.converter(direction, value, elementBinding.attributeBinding.attributeName, this._model);
397
+ }
398
+
399
+ return value;
400
+ }
401
+ });
402
+
403
+ Backbone.ModelBinder.CollectionConverter = function(collection){
404
+ this._collection = collection;
405
+
406
+ if(!this._collection){
407
+ throw 'Collection must be defined';
408
+ }
409
+ _.bindAll(this, 'convert');
410
+ };
411
+
412
+ _.extend(Backbone.ModelBinder.CollectionConverter.prototype, {
413
+ convert: function(direction, value){
414
+ if (direction === Backbone.ModelBinder.Constants.ModelToView) {
415
+ return value ? value.id : undefined;
416
+ }
417
+ else {
418
+ return this._collection.get(value);
419
+ }
420
+ }
421
+ });
422
+
423
+ // A static helper function to create a default set of bindings that you can customize before calling the bind() function
424
+ // rootEl - where to find all of the bound elements
425
+ // attributeType - probably 'name' or 'id' in most cases
426
+ // converter(optional) - the default converter you want applied to all your bindings
427
+ // elAttribute(optional) - the default elAttribute you want applied to all your bindings
428
+ Backbone.ModelBinder.createDefaultBindings = function(rootEl, attributeType, converter, elAttribute){
429
+ var foundEls, elCount, foundEl, attributeName;
430
+ var bindings = {};
431
+
432
+ foundEls = $('[' + attributeType + ']', rootEl);
433
+
434
+ for(elCount = 0; elCount < foundEls.length; elCount++){
435
+ foundEl = foundEls[elCount];
436
+ attributeName = $(foundEl).attr(attributeType);
437
+
438
+ if(!bindings[attributeName]){
439
+ var attributeBinding = {selector: '[' + attributeType + '="' + attributeName + '"]'};
440
+ bindings[attributeName] = attributeBinding;
441
+
442
+ if(converter){
443
+ bindings[attributeName].converter = converter;
444
+ }
445
+
446
+ if(elAttribute){
447
+ bindings[attributeName].elAttribute = elAttribute;
448
+ }
449
+ }
450
+ }
451
+
452
+ return bindings;
453
+ };
454
+
455
+ // Helps you to combine 2 sets of bindings
456
+ Backbone.ModelBinder.combineBindings = function(destination, source){
457
+ _.each(source, function(value, key){
458
+ var elementBinding = {selector: value.selector};
459
+
460
+ if(value.converter){
461
+ elementBinding.converter = value.converter;
462
+ }
463
+
464
+ if(value.elAttribute){
465
+ elementBinding.elAttribute = value.elAttribute;
466
+ }
467
+
468
+ if(!destination[key]){
469
+ destination[key] = elementBinding;
470
+ }
471
+ else {
472
+ destination[key] = [destination[key], elementBinding];
473
+ }
474
+ });
475
+
476
+ return destination;
477
+ };
478
+
479
+
480
+ return Backbone.ModelBinder;
481
+
482
+ }));