chiropractor 1.0.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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +46 -0
- data/Rakefile +22 -0
- data/chiropractor.gemspec +24 -0
- data/lib/chiropractor.rb +11 -0
- data/lib/chiropractor/engine.rb +6 -0
- data/lib/chiropractor/version.rb +3 -0
- data/vendor/assets/javascripts/chiropractor/Backbone.ModelBinder.js +482 -0
- data/vendor/assets/javascripts/chiropractor/accounting.js +412 -0
- data/vendor/assets/javascripts/chiropractor/backbone-relational.js +1687 -0
- data/vendor/assets/javascripts/chiropractor/backbone.jst.js +7 -0
- data/vendor/assets/javascripts/chiropractor/backbone.layoutRegion.coffee +75 -0
- data/vendor/assets/javascripts/chiropractor/index.js +13 -0
- data/vendor/assets/javascripts/chiropractor/moment.js +1213 -0
- data/vendor/assets/javascripts/chiropractor/underscore.inflection.js +176 -0
- data/vendor/assets/javascripts/chiropractor/underscore.string.js +613 -0
- metadata +133 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/lib/chiropractor.rb
ADDED
@@ -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
|
+
}));
|