backbone_model_binder-rails 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c7ab63a707ada43aaf91e8f026d3c80b8b54f3c
4
+ data.tar.gz: 504606265014a3dffe2fa6af234b31a2030b688c
5
+ SHA512:
6
+ metadata.gz: 15ac126a2bf79baf03a30999b211c7d2a6b0728ed276a304c23b5ed4d33ab1eec965f260049a0a1b91f25c215784344dc09ec4b0ec62178948317fd9c017e7f5
7
+ data.tar.gz: 0d9b96ad97f30085d67e4135e30d315ff3ace1cc2a4d670fd8bf86f606c1a074c2a5b022db43346d5c59085d47548a017d94258de042d4d71ba393152065de7b
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in backbone_model_binder-rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Anton Taraev
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,29 @@
1
+ # BackboneModelBinder::Rails
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'backbone_model_binder-rails'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install backbone_model_binder-rails
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'backbone_model_binder-rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "backbone_model_binder-rails"
8
+ spec.version = BackboneModelBinder::Rails::VERSION
9
+ spec.authors = ["Anton Taraev"]
10
+ spec.email = ["anti191@gmail.com"]
11
+ spec.description = %q{Backbone.ModelBinding for Rails}
12
+ spec.summary = %q{Vendors Backbone.ModelBinding for use with the asset pipeline.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,8 @@
1
+ require "rails"
2
+
3
+ module BackboneModelBinder
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module BackboneModelBinder
2
+ module Rails
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,281 @@
1
+ // Backbone.CollectionBinder v1.0.2
2
+ // (c) 2013 Bart Wood
3
+ // Distributed Under MIT License
4
+
5
+ (function(){
6
+
7
+ if(!Backbone){
8
+ throw 'Please include Backbone.js before Backbone.ModelBinder.js';
9
+ }
10
+
11
+ if(!Backbone.ModelBinder){
12
+ throw 'Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js';
13
+ }
14
+
15
+ Backbone.CollectionBinder = function(elManagerFactory, options){
16
+ _.bindAll.apply(_, [this].concat(_.functions(this)));
17
+ this._elManagers = {};
18
+
19
+ this._elManagerFactory = elManagerFactory;
20
+ if(!this._elManagerFactory) throw 'elManagerFactory must be defined.';
21
+
22
+ // Let the factory just use the trigger function on the view binder
23
+ this._elManagerFactory.trigger = this.trigger;
24
+
25
+ this._options = options || {};
26
+ };
27
+
28
+ Backbone.CollectionBinder.VERSION = '1.0.1';
29
+
30
+ _.extend(Backbone.CollectionBinder.prototype, Backbone.Events, {
31
+ bind: function(collection, parentEl){
32
+ this.unbind();
33
+
34
+ if(!collection) throw 'collection must be defined';
35
+ if(!parentEl) throw 'parentEl must be defined';
36
+
37
+ this._collection = collection;
38
+ this._elManagerFactory.setParentEl(parentEl);
39
+
40
+ this._onCollectionReset();
41
+
42
+ this._collection.on('add', this._onCollectionAdd, this);
43
+ this._collection.on('remove', this._onCollectionRemove, this);
44
+ this._collection.on('reset', this._onCollectionReset, this);
45
+ this._collection.on('sort', this._onCollectionSort, this);
46
+ },
47
+
48
+ unbind: function(){
49
+ if(this._collection !== undefined){
50
+ this._collection.off('add', this._onCollectionAdd);
51
+ this._collection.off('remove', this._onCollectionRemove);
52
+ this._collection.off('reset', this._onCollectionReset);
53
+ this._collection.off('sort', this._onCollectionSort);
54
+ }
55
+
56
+ this._removeAllElManagers();
57
+ },
58
+
59
+ getManagerForEl: function(el){
60
+ var i, elManager, elManagers = _.values(this._elManagers);
61
+
62
+ for(i = 0; i < elManagers.length; i++){
63
+ elManager = elManagers[i];
64
+
65
+ if(elManager.isElContained(el)){
66
+ return elManager;
67
+ }
68
+ }
69
+
70
+ return undefined;
71
+ },
72
+
73
+ getManagerForModel: function(model){
74
+ return this._elManagers[_.isObject(model)? model.cid : model];
75
+ },
76
+
77
+ _onCollectionAdd: function(model){
78
+ this._elManagers[model.cid] = this._elManagerFactory.makeElManager(model);
79
+ this._elManagers[model.cid].createEl();
80
+
81
+ if(this._options['autoSort']){
82
+ this.sortRootEls();
83
+ }
84
+ },
85
+
86
+ _onCollectionRemove: function(model){
87
+ this._removeElManager(model);
88
+ },
89
+
90
+ _onCollectionReset: function(){
91
+ this._removeAllElManagers();
92
+
93
+ this._collection.each(function(model){
94
+ this._onCollectionAdd(model);
95
+ }, this);
96
+
97
+ this.trigger('elsReset', this._collection);
98
+ },
99
+
100
+ _onCollectionSort: function() {
101
+ if(this._options['autoSort']){
102
+ this.sortRootEls();
103
+ }
104
+ },
105
+
106
+ _removeAllElManagers: function(){
107
+ _.each(this._elManagers, function(elManager){
108
+ elManager.removeEl();
109
+ delete this._elManagers[elManager._model.cid];
110
+ }, this);
111
+
112
+ delete this._elManagers;
113
+ this._elManagers = {};
114
+ },
115
+
116
+ _removeElManager: function(model){
117
+ if(this._elManagers[model.cid] !== undefined){
118
+ this._elManagers[model.cid].removeEl();
119
+ delete this._elManagers[model.cid];
120
+ }
121
+ },
122
+
123
+ sortRootEls: function(){
124
+ this._collection.each(function(model, modelIndex){
125
+ var modelElManager = this.getManagerForModel(model);
126
+ if(modelElManager){
127
+ var modelEl = modelElManager.getEl();
128
+ var currentRootEls = $(this._elManagerFactory.getParentEl()).children();
129
+
130
+ if(currentRootEls[modelIndex] !== modelEl[0]){
131
+ modelEl.detach();
132
+ modelEl.insertBefore(currentRootEls[modelIndex]);
133
+ }
134
+ }
135
+ }, this);
136
+ }
137
+ });
138
+
139
+ // The ElManagerFactory is used for els that are just html templates
140
+ // elHtml - how the model's html will be rendered. Must have a single root element (div,span).
141
+ // bindings (optional) - either a string which is the binding attribute (name, id, data-name, etc.) or a normal bindings hash
142
+ Backbone.CollectionBinder.ElManagerFactory = function(elHtml, bindings){
143
+ _.bindAll.apply(_, [this].concat(_.functions(this)));
144
+
145
+ this._elHtml = elHtml;
146
+ this._bindings = bindings;
147
+
148
+ if(! _.isString(this._elHtml)) throw 'elHtml must be a valid html string';
149
+ };
150
+
151
+ _.extend(Backbone.CollectionBinder.ElManagerFactory.prototype, {
152
+ setParentEl: function(parentEl){
153
+ this._parentEl = parentEl;
154
+ },
155
+
156
+ getParentEl: function(){
157
+ return this._parentEl;
158
+ },
159
+
160
+ makeElManager: function(model){
161
+
162
+ var elManager = {
163
+ _model: model,
164
+
165
+ createEl: function(){
166
+
167
+ this._el = $(this._elHtml);
168
+ $(this._parentEl).append(this._el);
169
+
170
+ if(this._bindings){
171
+ if(_.isString(this._bindings)){
172
+ this._modelBinder = new Backbone.ModelBinder();
173
+ this._modelBinder.bind(this._model, this._el, Backbone.ModelBinder.createDefaultBindings(this._el, this._bindings));
174
+ }
175
+ else if(_.isObject(this._bindings)){
176
+ this._modelBinder = new Backbone.ModelBinder();
177
+ this._modelBinder.bind(this._model, this._el, this._bindings);
178
+ }
179
+ else {
180
+ throw 'Unsupported bindings type, please use a boolean or a bindings hash';
181
+ }
182
+ }
183
+
184
+ this.trigger('elCreated', this._model, this._el);
185
+ },
186
+
187
+ removeEl: function(){
188
+ if(this._modelBinder !== undefined){
189
+ this._modelBinder.unbind();
190
+ }
191
+
192
+ this._el.remove();
193
+ this.trigger('elRemoved', this._model, this._el);
194
+ },
195
+
196
+ isElContained: function(findEl){
197
+ return this._el === findEl || $(this._el).has(findEl).length > 0;
198
+ },
199
+
200
+ getModel: function(){
201
+ return this._model;
202
+ },
203
+
204
+ getEl: function(){
205
+ return this._el;
206
+ }
207
+ };
208
+
209
+ _.extend(elManager, this);
210
+ return elManager;
211
+ }
212
+ });
213
+
214
+
215
+ // The ViewManagerFactory is used for els that are created and owned by backbone views.
216
+ // There is no bindings option because the view made by the viewCreator should take care of any binding
217
+ // viewCreator - a callback that will create backbone view instances for a model passed to the callback
218
+ Backbone.CollectionBinder.ViewManagerFactory = function(viewCreator){
219
+ _.bindAll.apply(_, [this].concat(_.functions(this)));
220
+ this._viewCreator = viewCreator;
221
+
222
+ if(!_.isFunction(this._viewCreator)) throw 'viewCreator must be a valid function that accepts a model and returns a backbone view';
223
+ };
224
+
225
+ _.extend(Backbone.CollectionBinder.ViewManagerFactory.prototype, {
226
+ setParentEl: function(parentEl){
227
+ this._parentEl = parentEl;
228
+ },
229
+
230
+ getParentEl: function(){
231
+ return this._parentEl;
232
+ },
233
+
234
+ makeElManager: function(model){
235
+ var elManager = {
236
+
237
+ _model: model,
238
+
239
+ createEl: function(){
240
+ this._view = this._viewCreator(model);
241
+ $(this._parentEl).append(this._view.render(this._model).el);
242
+
243
+ this.trigger('elCreated', this._model, this._view);
244
+ },
245
+
246
+ removeEl: function(){
247
+ if(this._view.close !== undefined){
248
+ this._view.close();
249
+ }
250
+ else {
251
+ this._view.$el.remove();
252
+ console.log('warning, you should implement a close() function for your view, you might end up with zombies');
253
+ }
254
+
255
+ this.trigger('elRemoved', this._model, this._view);
256
+ },
257
+
258
+ isElContained: function(findEl){
259
+ return this._view.el === findEl || this._view.$el.has(findEl).length > 0;
260
+ },
261
+
262
+ getModel: function(){
263
+ return this._model;
264
+ },
265
+
266
+ getView: function(){
267
+ return this._view;
268
+ },
269
+
270
+ getEl: function(){
271
+ return this._view.$el;
272
+ }
273
+ };
274
+
275
+ _.extend(elManager, this);
276
+
277
+ return elManager;
278
+ }
279
+ });
280
+
281
+ }).call(this);
@@ -0,0 +1,5 @@
1
+ // Backbone.CollectionBinder v1.0.2
2
+ // (c) 2013 Bart Wood
3
+ // Distributed Under MIT License
4
+
5
+ (function(){if(!Backbone){throw"Please include Backbone.js before Backbone.ModelBinder.js"}if(!Backbone.ModelBinder){throw"Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js"}Backbone.CollectionBinder=function(e,t){_.bindAll.apply(_,[this].concat(_.functions(this)));this._elManagers={};this._elManagerFactory=e;if(!this._elManagerFactory)throw"elManagerFactory must be defined.";this._elManagerFactory.trigger=this.trigger;this._options=t||{}};Backbone.CollectionBinder.VERSION="1.0.1";_.extend(Backbone.CollectionBinder.prototype,Backbone.Events,{bind:function(e,t){this.unbind();if(!e)throw"collection must be defined";if(!t)throw"parentEl must be defined";this._collection=e;this._elManagerFactory.setParentEl(t);this._onCollectionReset();this._collection.on("add",this._onCollectionAdd,this);this._collection.on("remove",this._onCollectionRemove,this);this._collection.on("reset",this._onCollectionReset,this);this._collection.on("sort",this._onCollectionSort,this)},unbind:function(){if(this._collection!==undefined){this._collection.off("add",this._onCollectionAdd);this._collection.off("remove",this._onCollectionRemove);this._collection.off("reset",this._onCollectionReset);this._collection.off("sort",this._onCollectionSort)}this._removeAllElManagers()},getManagerForEl:function(e){var t,n,r=_.values(this._elManagers);for(t=0;t<r.length;t++){n=r[t];if(n.isElContained(e)){return n}}return undefined},getManagerForModel:function(e){return this._elManagers[_.isObject(e)?e.cid:e]},_onCollectionAdd:function(e){this._elManagers[e.cid]=this._elManagerFactory.makeElManager(e);this._elManagers[e.cid].createEl();if(this._options["autoSort"]){this.sortRootEls()}},_onCollectionRemove:function(e){this._removeElManager(e)},_onCollectionReset:function(){this._removeAllElManagers();this._collection.each(function(e){this._onCollectionAdd(e)},this);this.trigger("elsReset",this._collection)},_onCollectionSort:function(){if(this._options["autoSort"]){this.sortRootEls()}},_removeAllElManagers:function(){_.each(this._elManagers,function(e){e.removeEl();delete this._elManagers[e._model.cid]},this);delete this._elManagers;this._elManagers={}},_removeElManager:function(e){if(this._elManagers[e.cid]!==undefined){this._elManagers[e.cid].removeEl();delete this._elManagers[e.cid]}},sortRootEls:function(){this._collection.each(function(e,t){var n=this.getManagerForModel(e);if(n){var r=n.getEl();var i=$(this._elManagerFactory.getParentEl()).children();if(i[t]!==r[0]){r.detach();r.insertBefore(i[t])}}},this)}});Backbone.CollectionBinder.ElManagerFactory=function(e,t){_.bindAll.apply(_,[this].concat(_.functions(this)));this._elHtml=e;this._bindings=t;if(!_.isString(this._elHtml))throw"elHtml must be a valid html string"};_.extend(Backbone.CollectionBinder.ElManagerFactory.prototype,{setParentEl:function(e){this._parentEl=e},getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._el=$(this._elHtml);$(this._parentEl).append(this._el);if(this._bindings){if(_.isString(this._bindings)){this._modelBinder=new Backbone.ModelBinder;this._modelBinder.bind(this._model,this._el,Backbone.ModelBinder.createDefaultBindings(this._el,this._bindings))}else if(_.isObject(this._bindings)){this._modelBinder=new Backbone.ModelBinder;this._modelBinder.bind(this._model,this._el,this._bindings)}else{throw"Unsupported bindings type, please use a boolean or a bindings hash"}}this.trigger("elCreated",this._model,this._el)},removeEl:function(){if(this._modelBinder!==undefined){this._modelBinder.unbind()}this._el.remove();this.trigger("elRemoved",this._model,this._el)},isElContained:function(e){return this._el===e||$(this._el).has(e).length>0},getModel:function(){return this._model},getEl:function(){return this._el}};_.extend(t,this);return t}});Backbone.CollectionBinder.ViewManagerFactory=function(e){_.bindAll.apply(_,[this].concat(_.functions(this)));this._viewCreator=e;if(!_.isFunction(this._viewCreator))throw"viewCreator must be a valid function that accepts a model and returns a backbone view"};_.extend(Backbone.CollectionBinder.ViewManagerFactory.prototype,{setParentEl:function(e){this._parentEl=e},getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._view=this._viewCreator(e);$(this._parentEl).append(this._view.render(this._model).el);this.trigger("elCreated",this._model,this._view)},removeEl:function(){if(this._view.close!==undefined){this._view.close()}else{this._view.$el.remove();console.log("warning, you should implement a close() function for your view, you might end up with zombies")}this.trigger("elRemoved",this._model,this._view)},isElContained:function(e){return this._view.el===e||this._view.$el.has(e).length>0},getModel:function(){return this._model},getView:function(){return this._view},getEl:function(){return this._view.$el}};_.extend(t,this);return t}})}).call(this)
@@ -0,0 +1,576 @@
1
+ // Backbone.ModelBinder v1.0.2
2
+ // (c) 2013 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(){
20
+ _.bindAll.apply(_, [this].concat(_.functions(this)));
21
+ };
22
+
23
+ // Static setter for class level options
24
+ Backbone.ModelBinder.SetOptions = function(options){
25
+ Backbone.ModelBinder.options = options;
26
+ };
27
+
28
+ // Current version of the library.
29
+ Backbone.ModelBinder.VERSION = '1.0.2';
30
+ Backbone.ModelBinder.Constants = {};
31
+ Backbone.ModelBinder.Constants.ModelToView = 'ModelToView';
32
+ Backbone.ModelBinder.Constants.ViewToModel = 'ViewToModel';
33
+
34
+ _.extend(Backbone.ModelBinder.prototype, {
35
+
36
+ bind:function (model, rootEl, attributeBindings, options) {
37
+ this.unbind();
38
+
39
+ this._model = model;
40
+ this._rootEl = rootEl;
41
+ this._setOptions(options);
42
+
43
+ if (!this._model) this._throwException('model must be specified');
44
+ if (!this._rootEl) this._throwException('rootEl must be specified');
45
+
46
+ if(attributeBindings){
47
+ // Create a deep clone of the attribute bindings
48
+ this._attributeBindings = $.extend(true, {}, attributeBindings);
49
+
50
+ this._initializeAttributeBindings();
51
+ this._initializeElBindings();
52
+ }
53
+ else {
54
+ this._initializeDefaultBindings();
55
+ }
56
+
57
+ this._bindModelToView();
58
+ this._bindViewToModel();
59
+ },
60
+
61
+ bindCustomTriggers: function (model, rootEl, triggers, attributeBindings, modelSetOptions) {
62
+ this._triggers = triggers;
63
+ this.bind(model, rootEl, attributeBindings, modelSetOptions)
64
+ },
65
+
66
+ unbind:function () {
67
+ this._unbindModelToView();
68
+ this._unbindViewToModel();
69
+
70
+ if(this._attributeBindings){
71
+ delete this._attributeBindings;
72
+ this._attributeBindings = undefined;
73
+ }
74
+ },
75
+
76
+ _setOptions: function(options){
77
+ this._options = _.extend({
78
+ boundAttribute: 'name'
79
+ }, Backbone.ModelBinder.options, options);
80
+
81
+ // initialize default options
82
+ if(!this._options['modelSetOptions']){
83
+ this._options['modelSetOptions'] = {};
84
+ }
85
+ this._options['modelSetOptions'].changeSource = 'ModelBinder';
86
+
87
+ if(!this._options['changeTriggers']){
88
+ this._options['changeTriggers'] = {'': 'change', '[contenteditable]': 'blur'};
89
+ }
90
+
91
+ if(!this._options['initialCopyDirection']){
92
+ this._options['initialCopyDirection'] = Backbone.ModelBinder.Constants.ModelToView;
93
+ }
94
+ },
95
+
96
+ // Converts the input bindings, which might just be empty or strings, to binding objects
97
+ _initializeAttributeBindings:function () {
98
+ var attributeBindingKey, inputBinding, attributeBinding, elementBindingCount, elementBinding;
99
+
100
+ for (attributeBindingKey in this._attributeBindings) {
101
+ inputBinding = this._attributeBindings[attributeBindingKey];
102
+
103
+ if (_.isString(inputBinding)) {
104
+ attributeBinding = {elementBindings: [{selector: inputBinding}]};
105
+ }
106
+ else if (_.isArray(inputBinding)) {
107
+ attributeBinding = {elementBindings: inputBinding};
108
+ }
109
+ else if(_.isObject(inputBinding)){
110
+ attributeBinding = {elementBindings: [inputBinding]};
111
+ }
112
+ else {
113
+ this._throwException('Unsupported type passed to Model Binder ' + attributeBinding);
114
+ }
115
+
116
+ // Add a linkage from the element binding back to the attribute binding
117
+ for(elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++){
118
+ elementBinding = attributeBinding.elementBindings[elementBindingCount];
119
+ elementBinding.attributeBinding = attributeBinding;
120
+ }
121
+
122
+ attributeBinding.attributeName = attributeBindingKey;
123
+ this._attributeBindings[attributeBindingKey] = attributeBinding;
124
+ }
125
+ },
126
+
127
+ // If the bindings are not specified, the default binding is performed on the specified attribute, name by default
128
+ _initializeDefaultBindings: function(){
129
+ var elCount, elsWithAttribute, matchedEl, name, attributeBinding;
130
+
131
+ this._attributeBindings = {};
132
+ elsWithAttribute = $('[' + this._options['boundAttribute'] + ']', this._rootEl);
133
+
134
+ for(elCount = 0; elCount < elsWithAttribute.length; elCount++){
135
+ matchedEl = elsWithAttribute[elCount];
136
+ name = $(matchedEl).attr(this._options['boundAttribute']);
137
+
138
+ // For elements like radio buttons we only want a single attribute binding with possibly multiple element bindings
139
+ if(!this._attributeBindings[name]){
140
+ attributeBinding = {attributeName: name};
141
+ attributeBinding.elementBindings = [{attributeBinding: attributeBinding, boundEls: [matchedEl]}];
142
+ this._attributeBindings[name] = attributeBinding;
143
+ }
144
+ else{
145
+ this._attributeBindings[name].elementBindings.push({attributeBinding: this._attributeBindings[name], boundEls: [matchedEl]});
146
+ }
147
+ }
148
+ },
149
+
150
+ _initializeElBindings:function () {
151
+ var bindingKey, attributeBinding, bindingCount, elementBinding, foundEls, elCount, el;
152
+ for (bindingKey in this._attributeBindings) {
153
+ attributeBinding = this._attributeBindings[bindingKey];
154
+
155
+ for (bindingCount = 0; bindingCount < attributeBinding.elementBindings.length; bindingCount++) {
156
+ elementBinding = attributeBinding.elementBindings[bindingCount];
157
+ if (elementBinding.selector === '') {
158
+ foundEls = $(this._rootEl);
159
+ }
160
+ else {
161
+ foundEls = $(elementBinding.selector, this._rootEl);
162
+ }
163
+
164
+ if (foundEls.length === 0) {
165
+ this._throwException('Bad binding found. No elements returned for binding selector ' + elementBinding.selector);
166
+ }
167
+ else {
168
+ elementBinding.boundEls = [];
169
+ for (elCount = 0; elCount < foundEls.length; elCount++) {
170
+ el = foundEls[elCount];
171
+ elementBinding.boundEls.push(el);
172
+ }
173
+ }
174
+ }
175
+ }
176
+ },
177
+
178
+ _bindModelToView: function () {
179
+ this._model.on('change', this._onModelChange, this);
180
+
181
+ if(this._options['initialCopyDirection'] === Backbone.ModelBinder.Constants.ModelToView){
182
+ this.copyModelAttributesToView();
183
+ }
184
+ },
185
+
186
+ // attributesToCopy is an optional parameter - if empty, all attributes
187
+ // that are bound will be copied. Otherwise, only attributeBindings specified
188
+ // in the attributesToCopy are copied.
189
+ copyModelAttributesToView: function(attributesToCopy){
190
+ var attributeName, attributeBinding;
191
+
192
+ for (attributeName in this._attributeBindings) {
193
+ if(attributesToCopy === undefined || _.indexOf(attributesToCopy, attributeName) !== -1){
194
+ attributeBinding = this._attributeBindings[attributeName];
195
+ this._copyModelToView(attributeBinding);
196
+ }
197
+ }
198
+ },
199
+
200
+ copyViewValuesToModel: function(){
201
+ var bindingKey, attributeBinding, bindingCount, elementBinding, elCount, el;
202
+ for (bindingKey in this._attributeBindings) {
203
+ attributeBinding = this._attributeBindings[bindingKey];
204
+
205
+ for (bindingCount = 0; bindingCount < attributeBinding.elementBindings.length; bindingCount++) {
206
+ elementBinding = attributeBinding.elementBindings[bindingCount];
207
+
208
+ if(this._isBindingUserEditable(elementBinding)){
209
+ if(this._isBindingRadioGroup(elementBinding)){
210
+ el = this._getRadioButtonGroupCheckedEl(elementBinding);
211
+ if(el){
212
+ this._copyViewToModel(elementBinding, el);
213
+ }
214
+ }
215
+ else {
216
+ for(elCount = 0; elCount < elementBinding.boundEls.length; elCount++){
217
+ el = $(elementBinding.boundEls[elCount]);
218
+ if(this._isElUserEditable(el)){
219
+ this._copyViewToModel(elementBinding, el);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ },
227
+
228
+ _unbindModelToView: function(){
229
+ if(this._model){
230
+ this._model.off('change', this._onModelChange);
231
+ this._model = undefined;
232
+ }
233
+ },
234
+
235
+ _bindViewToModel: function () {
236
+ _.each(this._options['changeTriggers'], function (event, selector) {
237
+ $(this._rootEl).delegate(selector, event, this._onElChanged);
238
+ }, this);
239
+
240
+ if(this._options['initialCopyDirection'] === Backbone.ModelBinder.Constants.ViewToModel){
241
+ this.copyViewValuesToModel();
242
+ }
243
+ },
244
+
245
+ _unbindViewToModel: function () {
246
+ if(this._options && this._options['changeTriggers']){
247
+ _.each(this._options['changeTriggers'], function (event, selector) {
248
+ $(this._rootEl).undelegate(selector, event, this._onElChanged);
249
+ }, this);
250
+ }
251
+ },
252
+
253
+ _onElChanged:function (event) {
254
+ var el, elBindings, elBindingCount, elBinding;
255
+
256
+ el = $(event.target)[0];
257
+ elBindings = this._getElBindings(el);
258
+
259
+ for(elBindingCount = 0; elBindingCount < elBindings.length; elBindingCount++){
260
+ elBinding = elBindings[elBindingCount];
261
+ if (this._isBindingUserEditable(elBinding)) {
262
+ this._copyViewToModel(elBinding, el);
263
+ }
264
+ }
265
+ },
266
+
267
+ _isBindingUserEditable: function(elBinding){
268
+ return elBinding.elAttribute === undefined ||
269
+ elBinding.elAttribute === 'text' ||
270
+ elBinding.elAttribute === 'html';
271
+ },
272
+
273
+ _isElUserEditable: function(el){
274
+ var isContentEditable = el.attr('contenteditable');
275
+ return isContentEditable || el.is('input') || el.is('select') || el.is('textarea');
276
+ },
277
+
278
+ _isBindingRadioGroup: function(elBinding){
279
+ var elCount, el;
280
+ var isAllRadioButtons = elBinding.boundEls.length > 0;
281
+ for(elCount = 0; elCount < elBinding.boundEls.length; elCount++){
282
+ el = $(elBinding.boundEls[elCount]);
283
+ if(el.attr('type') !== 'radio'){
284
+ isAllRadioButtons = false;
285
+ break;
286
+ }
287
+ }
288
+
289
+ return isAllRadioButtons;
290
+ },
291
+
292
+ _getRadioButtonGroupCheckedEl: function(elBinding){
293
+ var elCount, el;
294
+ for(elCount = 0; elCount < elBinding.boundEls.length; elCount++){
295
+ el = $(elBinding.boundEls[elCount]);
296
+ if(el.attr('type') === 'radio' && el.attr('checked')){
297
+ return el;
298
+ }
299
+ }
300
+
301
+ return undefined;
302
+ },
303
+
304
+ _getElBindings:function (findEl) {
305
+ var attributeName, attributeBinding, elementBindingCount, elementBinding, boundElCount, boundEl;
306
+ var elBindings = [];
307
+
308
+ for (attributeName in this._attributeBindings) {
309
+ attributeBinding = this._attributeBindings[attributeName];
310
+
311
+ for (elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++) {
312
+ elementBinding = attributeBinding.elementBindings[elementBindingCount];
313
+
314
+ for (boundElCount = 0; boundElCount < elementBinding.boundEls.length; boundElCount++) {
315
+ boundEl = elementBinding.boundEls[boundElCount];
316
+
317
+ if (boundEl === findEl) {
318
+ elBindings.push(elementBinding);
319
+ }
320
+ }
321
+ }
322
+ }
323
+
324
+ return elBindings;
325
+ },
326
+
327
+ _onModelChange:function () {
328
+ var changedAttribute, attributeBinding;
329
+
330
+ for (changedAttribute in this._model.changedAttributes()) {
331
+ attributeBinding = this._attributeBindings[changedAttribute];
332
+
333
+ if (attributeBinding) {
334
+ this._copyModelToView(attributeBinding);
335
+ }
336
+ }
337
+ },
338
+
339
+ _copyModelToView:function (attributeBinding) {
340
+ var elementBindingCount, elementBinding, boundElCount, boundEl, value, convertedValue;
341
+
342
+ value = this._model.get(attributeBinding.attributeName);
343
+
344
+ for (elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++) {
345
+ elementBinding = attributeBinding.elementBindings[elementBindingCount];
346
+
347
+ for (boundElCount = 0; boundElCount < elementBinding.boundEls.length; boundElCount++) {
348
+ boundEl = elementBinding.boundEls[boundElCount];
349
+
350
+ if(!boundEl._isSetting){
351
+ convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value);
352
+ this._setEl($(boundEl), elementBinding, convertedValue);
353
+ }
354
+ }
355
+ }
356
+ },
357
+
358
+ _setEl: function (el, elementBinding, convertedValue) {
359
+ if (elementBinding.elAttribute) {
360
+ this._setElAttribute(el, elementBinding, convertedValue);
361
+ }
362
+ else {
363
+ this._setElValue(el, convertedValue);
364
+ }
365
+ },
366
+
367
+ _setElAttribute:function (el, elementBinding, convertedValue) {
368
+ switch (elementBinding.elAttribute) {
369
+ case 'html':
370
+ el.html(convertedValue);
371
+ break;
372
+ case 'text':
373
+ el.text(convertedValue);
374
+ break;
375
+ case 'enabled':
376
+ el.prop('disabled', !convertedValue);
377
+ break;
378
+ case 'displayed':
379
+ el[convertedValue ? 'show' : 'hide']();
380
+ break;
381
+ case 'hidden':
382
+ el[convertedValue ? 'hide' : 'show']();
383
+ break;
384
+ case 'css':
385
+ el.css(elementBinding.cssAttribute, convertedValue);
386
+ break;
387
+ case 'class':
388
+ var previousValue = this._model.previous(elementBinding.attributeBinding.attributeName);
389
+ var currentValue = this._model.get(elementBinding.attributeBinding.attributeName);
390
+ // is current value is now defined then remove the class the may have been set for the undefined value
391
+ if(!_.isUndefined(previousValue) || !_.isUndefined(currentValue)){
392
+ previousValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, previousValue);
393
+ el.removeClass(previousValue);
394
+ }
395
+
396
+ if(convertedValue){
397
+ el.addClass(convertedValue);
398
+ }
399
+ break;
400
+ default:
401
+ el.attr(elementBinding.elAttribute, convertedValue);
402
+ }
403
+ },
404
+
405
+ _setElValue:function (el, convertedValue) {
406
+ if(el.attr('type')){
407
+ switch (el.attr('type')) {
408
+ case 'radio':
409
+ if (el.val() === convertedValue) {
410
+ // must defer the change trigger or the change will actually fire with the old value
411
+ el.prop('checked') || _.defer(function() { el.trigger('change'); });
412
+ el.prop('checked', true);
413
+ }
414
+ else {
415
+ // must defer the change trigger or the change will actually fire with the old value
416
+ el.prop('checked', false);
417
+ }
418
+ break;
419
+ case 'checkbox':
420
+ // must defer the change trigger or the change will actually fire with the old value
421
+ el.prop('checked') === !!convertedValue || _.defer(function() { el.trigger('change') });
422
+ el.prop('checked', !!convertedValue);
423
+ break;
424
+ case 'file':
425
+ break;
426
+ default:
427
+ el.val(convertedValue);
428
+ }
429
+ }
430
+ else if(el.is('input') || el.is('select') || el.is('textarea')){
431
+ el.val(convertedValue || (convertedValue === 0 ? '0' : ''));
432
+ }
433
+ else {
434
+ el.text(convertedValue || (convertedValue === 0 ? '0' : ''));
435
+ }
436
+ },
437
+
438
+ _copyViewToModel: function (elementBinding, el) {
439
+ var result, value, convertedValue;
440
+
441
+ if (!el._isSetting) {
442
+
443
+ el._isSetting = true;
444
+ result = this._setModel(elementBinding, $(el));
445
+ el._isSetting = false;
446
+
447
+ if(result && elementBinding.converter){
448
+ value = this._model.get(elementBinding.attributeBinding.attributeName);
449
+ convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value);
450
+ this._setEl($(el), elementBinding, convertedValue);
451
+ }
452
+ }
453
+ },
454
+
455
+ _getElValue: function(elementBinding, el){
456
+ switch (el.attr('type')) {
457
+ case 'checkbox':
458
+ return el.prop('checked') ? true : false;
459
+ default:
460
+ if(el.attr('contenteditable') !== undefined){
461
+ return el.html();
462
+ }
463
+ else {
464
+ return el.val();
465
+ }
466
+ }
467
+ },
468
+
469
+ _setModel: function (elementBinding, el) {
470
+ var data = {};
471
+ var elVal = this._getElValue(elementBinding, el);
472
+ elVal = this._getConvertedValue(Backbone.ModelBinder.Constants.ViewToModel, elementBinding, elVal);
473
+ data[elementBinding.attributeBinding.attributeName] = elVal;
474
+ return this._model.set(data, this._options['modelSetOptions']);
475
+ },
476
+
477
+ _getConvertedValue: function (direction, elementBinding, value) {
478
+ if (elementBinding.converter) {
479
+ value = elementBinding.converter(direction, value, elementBinding.attributeBinding.attributeName, this._model, elementBinding.boundEls);
480
+ }
481
+
482
+ return value;
483
+ },
484
+
485
+ _throwException: function(message){
486
+ if(this._options.suppressThrows){
487
+ if(console && console.error){
488
+ console.error(message);
489
+ }
490
+ }
491
+ else {
492
+ throw message;
493
+ }
494
+ }
495
+ });
496
+
497
+ Backbone.ModelBinder.CollectionConverter = function(collection){
498
+ this._collection = collection;
499
+
500
+ if(!this._collection){
501
+ throw 'Collection must be defined';
502
+ }
503
+ _.bindAll(this, 'convert');
504
+ };
505
+
506
+ _.extend(Backbone.ModelBinder.CollectionConverter.prototype, {
507
+ convert: function(direction, value){
508
+ if (direction === Backbone.ModelBinder.Constants.ModelToView) {
509
+ return value ? value.id : undefined;
510
+ }
511
+ else {
512
+ return this._collection.get(value);
513
+ }
514
+ }
515
+ });
516
+
517
+ // A static helper function to create a default set of bindings that you can customize before calling the bind() function
518
+ // rootEl - where to find all of the bound elements
519
+ // attributeType - probably 'name' or 'id' in most cases
520
+ // converter(optional) - the default converter you want applied to all your bindings
521
+ // elAttribute(optional) - the default elAttribute you want applied to all your bindings
522
+ Backbone.ModelBinder.createDefaultBindings = function(rootEl, attributeType, converter, elAttribute){
523
+ var foundEls, elCount, foundEl, attributeName;
524
+ var bindings = {};
525
+
526
+ foundEls = $('[' + attributeType + ']', rootEl);
527
+
528
+ for(elCount = 0; elCount < foundEls.length; elCount++){
529
+ foundEl = foundEls[elCount];
530
+ attributeName = $(foundEl).attr(attributeType);
531
+
532
+ if(!bindings[attributeName]){
533
+ var attributeBinding = {selector: '[' + attributeType + '="' + attributeName + '"]'};
534
+ bindings[attributeName] = attributeBinding;
535
+
536
+ if(converter){
537
+ bindings[attributeName].converter = converter;
538
+ }
539
+
540
+ if(elAttribute){
541
+ bindings[attributeName].elAttribute = elAttribute;
542
+ }
543
+ }
544
+ }
545
+
546
+ return bindings;
547
+ };
548
+
549
+ // Helps you to combine 2 sets of bindings
550
+ Backbone.ModelBinder.combineBindings = function(destination, source){
551
+ _.each(source, function(value, key){
552
+ var elementBinding = {selector: value.selector};
553
+
554
+ if(value.converter){
555
+ elementBinding.converter = value.converter;
556
+ }
557
+
558
+ if(value.elAttribute){
559
+ elementBinding.elAttribute = value.elAttribute;
560
+ }
561
+
562
+ if(!destination[key]){
563
+ destination[key] = elementBinding;
564
+ }
565
+ else {
566
+ destination[key] = [destination[key], elementBinding];
567
+ }
568
+ });
569
+
570
+ return destination;
571
+ };
572
+
573
+
574
+ return Backbone.ModelBinder;
575
+
576
+ }));
@@ -0,0 +1,4 @@
1
+ // Backbone.ModelBinder v1.0.2
2
+ // (c) 2013 Bart Wood
3
+ // Distributed Under MIT License
4
+ (function(e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","backbone"],e)}else{e(_,$,Backbone)}})(function(e,t,n){if(!n){throw"Please include Backbone.js before Backbone.ModelBinder.js"}n.ModelBinder=function(){e.bindAll.apply(e,[this].concat(e.functions(this)))};n.ModelBinder.SetOptions=function(e){n.ModelBinder.options=e};n.ModelBinder.VERSION="1.0.2";n.ModelBinder.Constants={};n.ModelBinder.Constants.ModelToView="ModelToView";n.ModelBinder.Constants.ViewToModel="ViewToModel";e.extend(n.ModelBinder.prototype,{bind:function(e,n,r,i){this.unbind();this._model=e;this._rootEl=n;this._setOptions(i);if(!this._model)this._throwException("model must be specified");if(!this._rootEl)this._throwException("rootEl must be specified");if(r){this._attributeBindings=t.extend(true,{},r);this._initializeAttributeBindings();this._initializeElBindings()}else{this._initializeDefaultBindings()}this._bindModelToView();this._bindViewToModel()},bindCustomTriggers:function(e,t,n,r,i){this._triggers=n;this.bind(e,t,r,i)},unbind:function(){this._unbindModelToView();this._unbindViewToModel();if(this._attributeBindings){delete this._attributeBindings;this._attributeBindings=undefined}},_setOptions:function(t){this._options=e.extend({boundAttribute:"name"},n.ModelBinder.options,t);if(!this._options["modelSetOptions"]){this._options["modelSetOptions"]={}}this._options["modelSetOptions"].changeSource="ModelBinder";if(!this._options["changeTriggers"]){this._options["changeTriggers"]={"":"change","[contenteditable]":"blur"}}if(!this._options["initialCopyDirection"]){this._options["initialCopyDirection"]=n.ModelBinder.Constants.ModelToView}},_initializeAttributeBindings:function(){var t,n,r,i,s;for(t in this._attributeBindings){n=this._attributeBindings[t];if(e.isString(n)){r={elementBindings:[{selector:n}]}}else if(e.isArray(n)){r={elementBindings:n}}else if(e.isObject(n)){r={elementBindings:[n]}}else{this._throwException("Unsupported type passed to Model Binder "+r)}for(i=0;i<r.elementBindings.length;i++){s=r.elementBindings[i];s.attributeBinding=r}r.attributeName=t;this._attributeBindings[t]=r}},_initializeDefaultBindings:function(){var e,n,r,i,s;this._attributeBindings={};n=t("["+this._options["boundAttribute"]+"]",this._rootEl);for(e=0;e<n.length;e++){r=n[e];i=t(r).attr(this._options["boundAttribute"]);if(!this._attributeBindings[i]){s={attributeName:i};s.elementBindings=[{attributeBinding:s,boundEls:[r]}];this._attributeBindings[i]=s}else{this._attributeBindings[i].elementBindings.push({attributeBinding:this._attributeBindings[i],boundEls:[r]})}}},_initializeElBindings:function(){var e,n,r,i,s,o,u;for(e in this._attributeBindings){n=this._attributeBindings[e];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];if(i.selector===""){s=t(this._rootEl)}else{s=t(i.selector,this._rootEl)}if(s.length===0){this._throwException("Bad binding found. No elements returned for binding selector "+i.selector)}else{i.boundEls=[];for(o=0;o<s.length;o++){u=s[o];i.boundEls.push(u)}}}}},_bindModelToView:function(){this._model.on("change",this._onModelChange,this);if(this._options["initialCopyDirection"]===n.ModelBinder.Constants.ModelToView){this.copyModelAttributesToView()}},copyModelAttributesToView:function(t){var n,r;for(n in this._attributeBindings){if(t===undefined||e.indexOf(t,n)!==-1){r=this._attributeBindings[n];this._copyModelToView(r)}}},copyViewValuesToModel:function(){var e,n,r,i,s,o;for(e in this._attributeBindings){n=this._attributeBindings[e];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];if(this._isBindingUserEditable(i)){if(this._isBindingRadioGroup(i)){o=this._getRadioButtonGroupCheckedEl(i);if(o){this._copyViewToModel(i,o)}}else{for(s=0;s<i.boundEls.length;s++){o=t(i.boundEls[s]);if(this._isElUserEditable(o)){this._copyViewToModel(i,o)}}}}}}},_unbindModelToView:function(){if(this._model){this._model.off("change",this._onModelChange);this._model=undefined}},_bindViewToModel:function(){e.each(this._options["changeTriggers"],function(e,n){t(this._rootEl).delegate(n,e,this._onElChanged)},this);if(this._options["initialCopyDirection"]===n.ModelBinder.Constants.ViewToModel){this.copyViewValuesToModel()}},_unbindViewToModel:function(){if(this._options&&this._options["changeTriggers"]){e.each(this._options["changeTriggers"],function(e,n){t(this._rootEl).undelegate(n,e,this._onElChanged)},this)}},_onElChanged:function(e){var n,r,i,s;n=t(e.target)[0];r=this._getElBindings(n);for(i=0;i<r.length;i++){s=r[i];if(this._isBindingUserEditable(s)){this._copyViewToModel(s,n)}}},_isBindingUserEditable:function(e){return e.elAttribute===undefined||e.elAttribute==="text"||e.elAttribute==="html"},_isElUserEditable:function(e){var t=e.attr("contenteditable");return t||e.is("input")||e.is("select")||e.is("textarea")},_isBindingRadioGroup:function(e){var n,r;var i=e.boundEls.length>0;for(n=0;n<e.boundEls.length;n++){r=t(e.boundEls[n]);if(r.attr("type")!=="radio"){i=false;break}}return i},_getRadioButtonGroupCheckedEl:function(e){var n,r;for(n=0;n<e.boundEls.length;n++){r=t(e.boundEls[n]);if(r.attr("type")==="radio"&&r.attr("checked")){return r}}return undefined},_getElBindings:function(e){var t,n,r,i,s,o;var u=[];for(t in this._attributeBindings){n=this._attributeBindings[t];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];for(s=0;s<i.boundEls.length;s++){o=i.boundEls[s];if(o===e){u.push(i)}}}}return u},_onModelChange:function(){var e,t;for(e in this._model.changedAttributes()){t=this._attributeBindings[e];if(t){this._copyModelToView(t)}}},_copyModelToView:function(e){var r,i,s,o,u,a;u=this._model.get(e.attributeName);for(r=0;r<e.elementBindings.length;r++){i=e.elementBindings[r];for(s=0;s<i.boundEls.length;s++){o=i.boundEls[s];if(!o._isSetting){a=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,i,u);this._setEl(t(o),i,a)}}}},_setEl:function(e,t,n){if(t.elAttribute){this._setElAttribute(e,t,n)}else{this._setElValue(e,n)}},_setElAttribute:function(t,r,i){switch(r.elAttribute){case"html":t.html(i);break;case"text":t.text(i);break;case"enabled":t.prop("disabled",!i);break;case"displayed":t[i?"show":"hide"]();break;case"hidden":t[i?"hide":"show"]();break;case"css":t.css(r.cssAttribute,i);break;case"class":var s=this._model.previous(r.attributeBinding.attributeName);var o=this._model.get(r.attributeBinding.attributeName);if(!e.isUndefined(s)||!e.isUndefined(o)){s=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,r,s);t.removeClass(s)}if(i){t.addClass(i)}break;default:t.attr(r.elAttribute,i)}},_setElValue:function(t,n){if(t.attr("type")){switch(t.attr("type")){case"radio":if(t.val()===n){t.prop("checked")||e.defer(function(){t.trigger("change")});t.prop("checked",true)}else{t.prop("checked",false)}break;case"checkbox":t.prop("checked")===!!n||e.defer(function(){t.trigger("change")});t.prop("checked",!!n);break;case"file":break;default:t.val(n)}}else if(t.is("input")||t.is("select")||t.is("textarea")){t.val(n||(n===0?"0":""))}else{t.text(n||(n===0?"0":""))}},_copyViewToModel:function(e,r){var i,s,o;if(!r._isSetting){r._isSetting=true;i=this._setModel(e,t(r));r._isSetting=false;if(i&&e.converter){s=this._model.get(e.attributeBinding.attributeName);o=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,e,s);this._setEl(t(r),e,o)}}},_getElValue:function(e,t){switch(t.attr("type")){case"checkbox":return t.prop("checked")?true:false;default:if(t.attr("contenteditable")!==undefined){return t.html()}else{return t.val()}}},_setModel:function(e,t){var r={};var i=this._getElValue(e,t);i=this._getConvertedValue(n.ModelBinder.Constants.ViewToModel,e,i);r[e.attributeBinding.attributeName]=i;return this._model.set(r,this._options["modelSetOptions"])},_getConvertedValue:function(e,t,n){if(t.converter){n=t.converter(e,n,t.attributeBinding.attributeName,this._model,t.boundEls)}return n},_throwException:function(e){if(this._options.suppressThrows){if(console&&console.error){console.error(e)}}else{throw e}}});n.ModelBinder.CollectionConverter=function(t){this._collection=t;if(!this._collection){throw"Collection must be defined"}e.bindAll(this,"convert")};e.extend(n.ModelBinder.CollectionConverter.prototype,{convert:function(e,t){if(e===n.ModelBinder.Constants.ModelToView){return t?t.id:undefined}else{return this._collection.get(t)}}});n.ModelBinder.createDefaultBindings=function(e,n,r,i){var s,o,u,a;var f={};s=t("["+n+"]",e);for(o=0;o<s.length;o++){u=s[o];a=t(u).attr(n);if(!f[a]){var l={selector:"["+n+'="'+a+'"]'};f[a]=l;if(r){f[a].converter=r}if(i){f[a].elAttribute=i}}}return f};n.ModelBinder.combineBindings=function(t,n){e.each(n,function(e,n){var r={selector:e.selector};if(e.converter){r.converter=e.converter}if(e.elAttribute){r.elAttribute=e.elAttribute}if(!t[n]){t[n]=r}else{t[n]=[t[n],r]}});return t};return n.ModelBinder})
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backbone_model_binder-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Anton Taraev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Backbone.ModelBinding for Rails
42
+ email:
43
+ - anti191@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - backbone_model_binder-rails.gemspec
54
+ - lib/backbone_model_binder-rails.rb
55
+ - lib/backbone_model_binder-rails/version.rb
56
+ - vendor/assets/javascripts/Backbone.CollectionBinder.js
57
+ - vendor/assets/javascripts/Backbone.CollectionBinder.min.js
58
+ - vendor/assets/javascripts/Backbone.ModelBinder.js
59
+ - vendor/assets/javascripts/Backbone.ModelBinder.min.js
60
+ homepage: ''
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.0.3
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Vendors Backbone.ModelBinding for use with the asset pipeline.
84
+ test_files: []