Revision 6dcc461e

b/snf-cyclades-app/synnefo/ui/static/snf/js/lib/backbone-filtered-collection.js
1
/*
2
The MIT License (MIT)
3

  
4
Copyright (c) 2013 Dmitriy Likhten
5

  
6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12

  
13
The above copyright notice and this permission notice shall be included in
14
all copies or substantial portions of the Software.
15

  
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
THE SOFTWARE.
23
*/
24
/* version 1.1.0 */
25
(function(_, Backbone) {
26
  var defaultFilter = function() {return true;};
27
  /**
28
   * This represents a filtered collection. You can either pass a filter or
29
   * invoke setFilter(filter) to give a filter. The filter is identical to
30
   * that which is used for _.select(array, filter)
31
   *
32
   * false filter indicates no filtering.
33
   *
34
   * do not modify this collection directly via #add/#remove, modify the
35
   * underlying origModel.
36
   *
37
   * Events:
38
   *   add - something was added (via filter or trickling from underlying collection)
39
   *   remove - something was removed (via filter or trickling from underlying collection)
40
   *   reset - the whole thing was reset
41
   *   sort - same as reset, but via sort
42
   *   filter-complete - filtering is complete -- very useful if you want to trigger a re-draw
43
   *                                              of the whole collection
44
   */
45
  Backbone.FilteredCollection = Backbone.Collection.extend({
46
    collectionFilter: null
47
    ,defaultFilter: defaultFilter
48

  
49
    ,initialize: function(models, data) {
50
      if (models) throw "models cannot be set directly, unfortunately first argument is the models.";
51
      this.collection = data.collection;
52
      this.setFilter(data.collectionFilter);
53

  
54
      this.collection.bind("add",             this.addModel, this);
55
      this.collection.bind("remove",          this.removeModel, this);
56
      this.collection.bind("reset",           this.resetCollection, this);
57
      this.collection.bind("sort",            this.resortCollection, this);
58
      this.collection.bind("change",          this._modelChanged, this);
59
      this.collection.bind("filter-complete", this._filterComplete, this);
60
    }
61

  
62
    ,_reset: function(options) {
63
      Backbone.Collection.prototype._reset.call(this, options);
64
      this._mapping = [];
65
    }
66

  
67
    ,add: function() {
68
      throw "Do not invoke directly";
69
    }
70

  
71
    ,remove: function() {
72
      throw "Do not invoke directly";
73
    }
74

  
75
    ,reset: function() {
76
      throw "Do not invoke directly";
77
    }
78

  
79
    ,_modelChanged: function(model, collection, options){
80
      options || (options = {});
81

  
82
      var ownIndexOfModel = this.indexOf(model);
83
      if (this.collectionFilter(model)){
84
        // Model passed filter
85
        if (ownIndexOfModel < 0){
86
          // Model not found, add it
87
          var index = this.collection.indexOf(model);
88
          this._forceAddModel(model, {index: index});
89
        }
90
        // the model passes the filter and is already in the collection
91
        // therefore we want to indicate that the model has changed
92
        else {
93
          this.trigger("change", model, this);
94
        }
95
      } else {
96
        // Model did not pass filter
97
        if (ownIndexOfModel > -1){
98
          this._forceRemoveModel(model, {index: ownIndexOfModel});
99
        }
100
      }
101
      if (! options.silent) {
102
        this._filterComplete();
103
      }
104
    }
105

  
106
    ,resortCollection: function() {
107
      // note: we don't need to do any filter work since sort
108
      // implies nothing changed, only order
109
      var newModels = [];
110
      var newMapping = [];
111
      var models = this.models;
112
      _.each(this.collection.models, function(model, index) {
113
        if (models.indexOf(model) >= 0) {
114
          newModels.push(model);
115
          newMapping.push(index);
116
        }
117
      });
118
      this.models = newModels;
119
      this._mapping = newMapping;
120
      this.trigger("sort", this);
121
    }
122

  
123
    ,resetCollection: function() {
124
      this._mapping = [];
125
      this._reset();
126
      this.setFilter(undefined, {silent: true});
127
      this.trigger("reset", this);
128
    }
129

  
130
    // this is to synchronize where the element exists in the original model
131
    // to our _mappings array
132
    ,renumberMappings: function() {
133
      this._mapping = []
134
      var collection = this.collection;
135
      var mapping = this._mapping;
136

  
137
      _(this.models).each(function(model) {
138
        mapping.push(collection.indexOf(model));
139
      });
140
    }
141

  
142
    ,removeModel: function(model, colleciton, options) {
143
      var at = this._mapping.indexOf(options.index);
144
      console.log("requested remove", "options.index", options.index, 
145
                  "at", at, "model.id", model.id);
146
      if (at > -1) {
147
        this._forceRemoveModel(model, _.extend({index: at}, options));
148
      }
149
      this.renumberMappings();
150
    }
151

  
152
    // the options.index here is the index of the current model which we are removing
153
    ,_forceRemoveModel: function(model, options) {
154
      this._mapping.splice(options.index, 1);
155
      Backbone.Collection.prototype.remove.call(this, model, {silent: options.silent});
156
      if (! options.silent) {
157
        this.trigger("remove", model, this, {index: options.index})
158
      }
159
    }
160

  
161
    ,addModel: function(model, collection, options) {
162
      if (this.collectionFilter(model)) {
163
        this._forceAddModel(model, _.extend(options || {}, {index: (options && options.at) || collection.indexOf(model)}));
164
      }
165
      this.renumberMappings();
166
    }
167

  
168
    // the options.index here is the index of the original model which we are inserting
169
    ,_forceAddModel: function(model, options) {
170
      var desiredIndex = options.index;
171
      // determine where to add, look at mapping and find first object with the index
172
      // great than the one that we are given
173
      var addToIndex = _.sortedIndex(this._mapping, desiredIndex, function(origIndex) { return origIndex; });
174

  
175
      // add it there
176
      if (this.get(model.id)) { return }
177
      Backbone.Collection.prototype.add.call(this, model, {at: addToIndex, silent: options.silent});
178
      this._mapping.splice(addToIndex, 0, desiredIndex);
179
      if (! options.silent) {
180
        this.trigger("add", model, this, {index: addToIndex})
181
      }
182
    }
183

  
184
    ,setFilter: function(newFilter, options) {
185
      options || (options = {});
186
      if (newFilter === false) { newFilter = this.defaultFilter } // false = clear out filter
187
      this.collectionFilter = newFilter || this.collectionFilter || this.defaultFilter;
188

  
189
      // this assumes that the original collection was unmodified
190
      // without the use of add/remove/reset events. If it was, a
191
      // reset event must be thrown, or this object's .resetCollection
192
      // method must be invoked, or this will most likely fall out-of-sync
193

  
194
      // why HashMap lookup when you can get it off the stack
195
      var filter = this.collectionFilter;
196
      var mapping = this._mapping;
197

  
198
      // this is the option object to pass, it will be mutated on each
199
      // iteration
200
      var passthroughOption = _.extend({}, options);
201
      this.collection.each(function(model, index) {
202
        var foundIndex = mapping.indexOf(index);
203

  
204
        if (filter(model, index)) {
205
          // if already added, no touchy
206
          if (foundIndex == -1) {
207
            passthroughOption.index = index
208
            this._forceAddModel(model, passthroughOption);
209
          }
210
        }
211
        else {
212
          if (foundIndex > -1) {
213
            passthroughOption.index = foundIndex == -1 ? this.length : foundIndex;
214
            this._forceRemoveModel(model, passthroughOption);
215
          }
216
        }
217
      }, this);
218
      if (! options.silent) {
219
        this._filterComplete();
220
      }
221
    }
222

  
223
    ,_onModelEvent: function(event, model, collection, options) {
224
      // noop, this collection has no business dealing with events of the original model
225
      // they will be handled by the original normal collection and bubble up to here
226
    }
227

  
228
    ,_filterComplete: function() {
229
      this.trigger("filter-complete", this);
230
    }
231
  });
232
})(_, Backbone);
b/snf-cyclades-app/synnefo/ui/static/snf/js/lib/rivets.js
1
// Rivets.js
2
// version: 0.5.10
3
// author: Michael Richards
4
// license: MIT
5
(function() {
6
  var Rivets,
7
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
8
    __slice = [].slice,
9
    __hasProp = {}.hasOwnProperty,
10
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
11
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
12

  
13
  Rivets = {};
14

  
15
  if (!String.prototype.trim) {
16
    String.prototype.trim = function() {
17
      return this.replace(/^\s+|\s+$/g, '');
18
    };
19
  }
20

  
21
  Rivets.Binding = (function() {
22
    function Binding(view, el, type, key, keypath, options) {
23
      var identifier, regexp, value, _ref;
24

  
25
      this.view = view;
26
      this.el = el;
27
      this.type = type;
28
      this.key = key;
29
      this.keypath = keypath;
30
      this.options = options != null ? options : {};
31
      this.update = __bind(this.update, this);
32
      this.unbind = __bind(this.unbind, this);
33
      this.bind = __bind(this.bind, this);
34
      this.publish = __bind(this.publish, this);
35
      this.sync = __bind(this.sync, this);
36
      this.set = __bind(this.set, this);
37
      this.eventHandler = __bind(this.eventHandler, this);
38
      this.formattedValue = __bind(this.formattedValue, this);
39
      if (!(this.binder = Rivets.internalBinders[this.type] || this.view.binders[type])) {
40
        _ref = this.view.binders;
41
        for (identifier in _ref) {
42
          value = _ref[identifier];
43
          if (identifier !== '*' && identifier.indexOf('*') !== -1) {
44
            regexp = new RegExp("^" + (identifier.replace('*', '.+')) + "$");
45
            if (regexp.test(type)) {
46
              this.binder = value;
47
              this.args = new RegExp("^" + (identifier.replace('*', '(.+)')) + "$").exec(type);
48
              this.args.shift();
49
            }
50
          }
51
        }
52
      }
53
      this.binder || (this.binder = this.view.binders['*']);
54
      if (this.binder instanceof Function) {
55
        this.binder = {
56
          routine: this.binder
57
        };
58
      }
59
      this.formatters = this.options.formatters || [];
60
      this.model = this.key ? this.view.models[this.key] : this.view.models;
61
    }
62

  
63
    Binding.prototype.formattedValue = function(value) {
64
      var args, formatter, id, _i, _len, _ref;
65

  
66
      _ref = this.formatters;
67
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
68
        formatter = _ref[_i];
69
        args = formatter.split(/\s+/);
70
        id = args.shift();
71

  
72
        // lookup formatters on all view models
73
        var modelFormatter = this.model[id];
74
        if (!(modelFormatter instanceof Function)) {
75
          _.each(this.view.models, function(m) {
76
            if (m[id] && m[id] instanceof Function) {
77
              modelFormatter = m[id];
78
            }
79
          });
80
        }
81

  
82
        formatter = modelFormatter instanceof Function ? modelFormatter : this.view.formatters[id];
83
        if ((formatter != null ? formatter.read : void 0) instanceof Function) {
84
          value = formatter.read.apply(formatter, [value].concat(__slice.call(args)));
85
        } else if (formatter instanceof Function) {
86
          value = formatter.apply(null, [value].concat(__slice.call(args)));
87
        }
88
      }
89
      return value;
90
    };
91

  
92
    Binding.prototype.eventHandler = function(fn) {
93
      var binding, handler;
94

  
95
      handler = (binding = this).view.config.handler;
96
      return function(ev) {
97
        return handler.call(fn, this, ev, binding);
98
      };
99
    };
100

  
101
    Binding.prototype.set = function(value) {
102
      var _ref;
103
      value = value instanceof Function && !this.binder["function"] ? this.formattedValue(value.call(this.model)) : this.formattedValue(value);
104
      if (this.keypath.indexOf('nics') >= 0) {
105
      }
106
      return (_ref = this.binder.routine) != null ? _ref.call(this, this.el, value) : void 0;
107
    };
108

  
109
    Binding.prototype.sync = function() {
110
      return this.set(this.options.bypass ? this.model[this.keypath] : this.view.config.adapter.read(this.model, this.keypath));
111
    };
112

  
113
    Binding.prototype.publish = function() {
114
      var args, formatter, id, value, _i, _len, _ref, _ref1, _ref2;
115

  
116
      value = Rivets.Util.getInputValue(this.el);
117
      _ref = this.formatters.slice(0).reverse();
118
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
119
        formatter = _ref[_i];
120
        args = formatter.split(/\s+/);
121
        id = args.shift();
122
        if ((_ref1 = this.view.formatters[id]) != null ? _ref1.publish : void 0) {
123
          value = (_ref2 = this.view.formatters[id]).publish.apply(_ref2, [value].concat(__slice.call(args)));
124
        }
125
      }
126
      return this.view.config.adapter.publish(this.model, this.keypath, value);
127
    };
128

  
129
    Binding.prototype.bind = function() {
130
      var dependency, keypath, model, _i, _len, _ref, _ref1, _ref2, _results;
131

  
132
      if ((_ref = this.binder.bind) != null) {
133
        _ref.call(this, this.el);
134
      }
135
      if (this.options.bypass) {
136
        this.sync();
137
      } else {
138
        this.view.config.adapter.subscribe(this.model, this.keypath, this.sync);
139
        if (this.view.config.preloadData) {
140
          this.sync();
141
        }
142
      }
143
      if ((_ref1 = this.options.dependencies) != null ? _ref1.length : void 0) {
144
        _ref2 = this.options.dependencies;
145
        _results = [];
146
        for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
147
          dependency = _ref2[_i];
148
          if (/^\./.test(dependency)) {
149
            model = this.model;
150
            keypath = dependency.substr(1);
151
          } else {
152
            dependency = dependency.split('.');
153
            model = this.view.models[dependency.shift()];
154
            keypath = dependency.join('.');
155
          }
156
          _results.push(this.view.config.adapter.subscribe(model, keypath, this.sync));
157
        }
158
        return _results;
159
      }
160
    };
161

  
162
    Binding.prototype.unbind = function() {
163
      var dependency, keypath, model, _i, _len, _ref, _ref1, _ref2, _results;
164

  
165
      if ((_ref = this.binder.unbind) != null) {
166
        _ref.call(this, this.el);
167
      }
168
      if (!this.options.bypass) {
169
        this.view.config.adapter.unsubscribe(this.model, this.keypath, this.sync);
170
      }
171
      if ((_ref1 = this.options.dependencies) != null ? _ref1.length : void 0) {
172
        _ref2 = this.options.dependencies;
173
        _results = [];
174
        for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
175
          dependency = _ref2[_i];
176
          if (/^\./.test(dependency)) {
177
            model = this.model;
178
            keypath = dependency.substr(1);
179
          } else {
180
            dependency = dependency.split('.');
181
            model = this.view.models[dependency.shift()];
182
            keypath = dependency.join('.');
183
          }
184
          _results.push(this.view.config.adapter.unsubscribe(model, keypath, this.sync));
185
        }
186
        return _results;
187
      }
188
    };
189

  
190
    Binding.prototype.update = function(models) {
191
      var _ref;
192

  
193
      if (models == null) {
194
        models = {};
195
      }
196
      if (this.key) {
197
        if (models[this.key]) {
198
          if (!this.options.bypass) {
199
            this.view.config.adapter.unsubscribe(this.model, this.keypath, this.sync);
200
          }
201
          this.model = models[this.key];
202
          if (this.options.bypass) {
203
            this.sync();
204
          } else {
205
            this.view.config.adapter.subscribe(this.model, this.keypath, this.sync);
206
            if (this.view.config.preloadData) {
207
              this.sync();
208
            }
209
          }
210
        }
211
      } else {
212
        this.sync();
213
      }
214
      return (_ref = this.binder.update) != null ? _ref.call(this, models) : void 0;
215
    };
216

  
217
    return Binding;
218

  
219
  })();
220

  
221
  Rivets.ComponentBinding = (function(_super) {
222
    __extends(ComponentBinding, _super);
223

  
224
    function ComponentBinding(view, el, type) {
225
      var attribute, _i, _len, _ref, _ref1;
226

  
227
      this.view = view;
228
      this.el = el;
229
      this.type = type;
230
      this.unbind = __bind(this.unbind, this);
231
      this.bind = __bind(this.bind, this);
232
      this.update = __bind(this.update, this);
233
      this.locals = __bind(this.locals, this);
234
      this.component = Rivets.components[this.type];
235
      this.attributes = {};
236
      this.inflections = {};
237
      _ref = this.el.attributes || [];
238
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
239
        attribute = _ref[_i];
240
        if (_ref1 = attribute.name, __indexOf.call(this.component.attributes, _ref1) >= 0) {
241
          this.attributes[attribute.name] = attribute.value;
242
        } else {
243
          this.inflections[attribute.name] = attribute.value;
244
        }
245
      }
246
    }
247

  
248
    ComponentBinding.prototype.sync = function() {};
249

  
250
    ComponentBinding.prototype.locals = function(models) {
251
      var inverse, key, model, result, _ref, _ref1;
252

  
253
      if (models == null) {
254
        models = this.view.models;
255
      }
256
      result = {};
257
      _ref = this.inflections;
258
      for (key in _ref) {
259
        inverse = _ref[key];
260
        result[key] = models[inverse];
261
      }
262
      for (key in models) {
263
        model = models[key];
264
        if ((_ref1 = result[key]) == null) {
265
          result[key] = model;
266
        }
267
      }
268
      return result;
269
    };
270

  
271
    ComponentBinding.prototype.update = function(models) {
272
      var _ref;
273

  
274
      return (_ref = this.componentView) != null ? _ref.update(this.locals(models)) : void 0;
275
    };
276

  
277
    ComponentBinding.prototype.bind = function() {
278
      var el, _ref;
279

  
280
      if (this.componentView != null) {
281
        return (_ref = this.componentView) != null ? _ref.bind() : void 0;
282
      } else {
283
        el = this.component.build.call(this.attributes);
284
        (this.componentView = new Rivets.View(el, this.locals(), this.view.options)).bind();
285
        return this.el.parentNode.replaceChild(el, this.el);
286
      }
287
    };
288

  
289
    ComponentBinding.prototype.unbind = function() {
290
      var _ref;
291

  
292
      return (_ref = this.componentView) != null ? _ref.unbind() : void 0;
293
    };
294

  
295
    return ComponentBinding;
296

  
297
  })(Rivets.Binding);
298

  
299
  Rivets.View = (function() {
300
    function View(els, models, options) {
301
      var k, option, v, _base, _i, _len, _ref, _ref1, _ref2, _ref3;
302

  
303
      this.els = els;
304
      this.models = models;
305
      this.options = options != null ? options : {};
306
      this.update = __bind(this.update, this);
307
      this.publish = __bind(this.publish, this);
308
      this.sync = __bind(this.sync, this);
309
      this.unbind = __bind(this.unbind, this);
310
      this.bind = __bind(this.bind, this);
311
      this.select = __bind(this.select, this);
312
      this.build = __bind(this.build, this);
313
      this.componentRegExp = __bind(this.componentRegExp, this);
314
      this.bindingRegExp = __bind(this.bindingRegExp, this);
315
      if (!(this.els.jquery || this.els instanceof Array)) {
316
        this.els = [this.els];
317
      }
318
      _ref = ['config', 'binders', 'formatters'];
319
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
320
        option = _ref[_i];
321
        this[option] = {};
322
        if (this.options[option]) {
323
          _ref1 = this.options[option];
324
          for (k in _ref1) {
325
            v = _ref1[k];
326
            this[option][k] = v;
327
          }
328
        }
329
        _ref2 = Rivets[option];
330
        for (k in _ref2) {
331
          v = _ref2[k];
332
          if ((_ref3 = (_base = this[option])[k]) == null) {
333
            _base[k] = v;
334
          }
335
        }
336
      }
337
      this.build();
338
    }
339

  
340
    View.prototype.bindingRegExp = function() {
341
      var prefix;
342

  
343
      prefix = this.config.prefix;
344
      if (prefix) {
345
        return new RegExp("^data-" + prefix + "-");
346
      } else {
347
        return /^data-/;
348
      }
349
    };
350

  
351
    View.prototype.componentRegExp = function() {
352
      var _ref, _ref1;
353

  
354
      return new RegExp("^" + ((_ref = (_ref1 = this.config.prefix) != null ? _ref1.toUpperCase() : void 0) != null ? _ref : 'RV') + "-");
355
    };
356

  
357
    View.prototype.build = function() {
358
      var bindingRegExp, buildBinding, componentRegExp, el, parse, skipNodes, _i, _len, _ref,
359
        _this = this;
360

  
361
      this.bindings = [];
362
      skipNodes = [];
363
      bindingRegExp = this.bindingRegExp();
364
      componentRegExp = this.componentRegExp();
365
      buildBinding = function(node, type, declaration) {
366
        var context, ctx, dependencies, key, keypath, options, path, pipe, pipes, splitPath;
367

  
368
        options = {};
369
        pipes = (function() {
370
          var _i, _len, _ref, _results;
371

  
372
          _ref = declaration.split('|');
373
          _results = [];
374
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
375
            pipe = _ref[_i];
376
            _results.push(pipe.trim());
377
          }
378
          return _results;
379
        })();
380
        context = (function() {
381
          var _i, _len, _ref, _results;
382

  
383
          _ref = pipes.shift().split('<');
384
          _results = [];
385
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
386
            ctx = _ref[_i];
387
            _results.push(ctx.trim());
388
          }
389
          return _results;
390
        })();
391
        path = context.shift();
392
        splitPath = path.split(/\.|:/);
393
        options.formatters = pipes;
394
        options.bypass = path.indexOf(':') !== -1;
395
        if (splitPath[0]) {
396
          key = splitPath.shift();
397
        } else {
398
          key = null;
399
          splitPath.shift();
400
        }
401
        keypath = splitPath.join('.');
402
        if (dependencies = context.shift()) {
403
          options.dependencies = dependencies.split(/\s+/);
404
        }
405
        return _this.bindings.push(new Rivets.Binding(_this, node, type, key, keypath, options));
406
      };
407
      parse = function(node) {
408
        var attribute, attributes, binder, childNode, delimiters, identifier, n, parser, regexp, restTokens, startToken, text, token, tokens, type, value, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _results;
409

  
410
        if (__indexOf.call(skipNodes, node) < 0) {
411
          if (node.nodeType === Node.TEXT_NODE) {
412
            parser = Rivets.TextTemplateParser;
413
            if (delimiters = _this.config.templateDelimiters) {
414
              if ((tokens = parser.parse(node.data, delimiters)).length) {
415
                if (!(tokens.length === 1 && tokens[0].type === parser.types.text)) {
416
                  startToken = tokens[0], restTokens = 2 <= tokens.length ? __slice.call(tokens, 1) : [];
417
                  node.data = startToken.value;
418
                  switch (startToken.type) {
419
                    case 0:
420
                      node.data = startToken.value;
421
                      break;
422
                    case 1:
423
                      buildBinding(node, 'textNode', startToken.value);
424
                  }
425
                  for (_i = 0, _len = restTokens.length; _i < _len; _i++) {
426
                    token = restTokens[_i];
427
                    node.parentNode.appendChild((text = document.createTextNode(token.value)));
428
                    if (token.type === 1) {
429
                      buildBinding(text, 'textNode', token.value);
430
                    }
431
                  }
432
                }
433
              }
434
            }
435
          } else if (componentRegExp.test(node.tagName)) {
436
            type = node.tagName.replace(componentRegExp, '').toLowerCase();
437
            _this.bindings.push(new Rivets.ComponentBinding(_this, node, type));
438
          } else if (node.attributes != null) {
439
            _ref = node.attributes;
440
            for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
441
              attribute = _ref[_j];
442
              if (bindingRegExp.test(attribute.name)) {
443
                type = attribute.name.replace(bindingRegExp, '');
444
                if (!(binder = _this.binders[type])) {
445
                  _ref1 = _this.binders;
446
                  for (identifier in _ref1) {
447
                    value = _ref1[identifier];
448
                    if (identifier !== '*' && identifier.indexOf('*') !== -1) {
449
                      regexp = new RegExp("^" + (identifier.replace('*', '.+')) + "$");
450
                      if (regexp.test(type)) {
451
                        binder = value;
452
                      }
453
                    }
454
                  }
455
                }
456
                binder || (binder = _this.binders['*']);
457
                if (binder.block) {
458
                  _ref2 = node.childNodes;
459
                  for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
460
                    n = _ref2[_k];
461
                    skipNodes.push(n);
462
                  }
463
                  attributes = [attribute];
464
                }
465
              }
466
            }
467
            _ref3 = attributes || node.attributes;
468
            for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
469
              attribute = _ref3[_l];
470
              if (bindingRegExp.test(attribute.name)) {
471
                type = attribute.name.replace(bindingRegExp, '');
472
                buildBinding(node, type, attribute.value);
473
              }
474
            }
475
          }
476
          _ref4 = node.childNodes;
477
          _results = [];
478
          for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
479
            childNode = _ref4[_m];
480
            _results.push(parse(childNode));
481
          }
482
          return _results;
483
        }
484
      };
485
      _ref = this.els;
486
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
487
        el = _ref[_i];
488
        parse(el);
489
      }
490
    };
491

  
492
    View.prototype.select = function(fn) {
493
      var binding, _i, _len, _ref, _results;
494

  
495
      _ref = this.bindings;
496
      _results = [];
497
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
498
        binding = _ref[_i];
499
        if (fn(binding)) {
500
          _results.push(binding);
501
        }
502
      }
503
      return _results;
504
    };
505

  
506
    View.prototype.bind = function() {
507
      var binding, _i, _len, _ref, _results;
508

  
509
      _ref = this.bindings;
510
      _results = [];
511
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
512
        binding = _ref[_i];
513
        _results.push(binding.bind());
514
      }
515
      return _results;
516
    };
517

  
518
    View.prototype.unbind = function() {
519
      var binding, _i, _len, _ref, _results;
520

  
521
      _ref = this.bindings;
522
      _results = [];
523
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
524
        binding = _ref[_i];
525
        _results.push(binding.unbind());
526
      }
527
      return _results;
528
    };
529

  
530
    View.prototype.sync = function() {
531
      var binding, _i, _len, _ref, _results;
532

  
533
      _ref = this.bindings;
534
      _results = [];
535
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
536
        binding = _ref[_i];
537
        _results.push(binding.sync());
538
      }
539
      return _results;
540
    };
541

  
542
    View.prototype.publish = function() {
543
      var binding, _i, _len, _ref, _results;
544

  
545
      _ref = this.select(function(b) {
546
        return b.binder.publishes;
547
      });
548
      _results = [];
549
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
550
        binding = _ref[_i];
551
        _results.push(binding.publish());
552
      }
553
      return _results;
554
    };
555

  
556
    View.prototype.update = function(models) {
557
      var binding, key, model, _i, _len, _ref, _results;
558

  
559
      if (models == null) {
560
        models = {};
561
      }
562
      for (key in models) {
563
        model = models[key];
564
        this.models[key] = model;
565
      }
566
      _ref = this.bindings;
567
      _results = [];
568
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
569
        binding = _ref[_i];
570
        _results.push(binding.update(models));
571
      }
572
      return _results;
573
    };
574

  
575
    return View;
576

  
577
  })();
578

  
579
  Rivets.TextTemplateParser = (function() {
580
    function TextTemplateParser() {}
581

  
582
    TextTemplateParser.types = {
583
      text: 0,
584
      binding: 1
585
    };
586

  
587
    TextTemplateParser.parse = function(template, delimiters) {
588
      var index, lastIndex, lastToken, length, substring, tokens, value;
589

  
590
      tokens = [];
591
      length = template.length;
592
      index = 0;
593
      lastIndex = 0;
594
      while (lastIndex < length) {
595
        index = template.indexOf(delimiters[0], lastIndex);
596
        if (index < 0) {
597
          tokens.push({
598
            type: this.types.text,
599
            value: template.slice(lastIndex)
600
          });
601
          break;
602
        } else {
603
          if (index > 0 && lastIndex < index) {
604
            tokens.push({
605
              type: this.types.text,
606
              value: template.slice(lastIndex, index)
607
            });
608
          }
609
          lastIndex = index + 2;
610
          index = template.indexOf(delimiters[1], lastIndex);
611
          if (index < 0) {
612
            substring = template.slice(lastIndex - 2);
613
            lastToken = tokens[tokens.length - 1];
614
            if ((lastToken != null ? lastToken.type : void 0) === this.types.text) {
615
              lastToken.value += substring;
616
            } else {
617
              tokens.push({
618
                type: this.types.text,
619
                value: substring
620
              });
621
            }
622
            break;
623
          }
624
          value = template.slice(lastIndex, index).trim();
625
          tokens.push({
626
            type: this.types.binding,
627
            value: value
628
          });
629
          lastIndex = index + 2;
630
        }
631
      }
632
      return tokens;
633
    };
634

  
635
    return TextTemplateParser;
636

  
637
  })();
638

  
639
  Rivets.Util = {
640
    bindEvent: function(el, event, handler) {
641
      if (window.jQuery != null) {
642
        el = jQuery(el);
643
        if (el.on != null) {
644
          return el.on(event, handler);
645
        } else {
646
          return el.bind(event, handler);
647
        }
648
      } else if (window.addEventListener != null) {
649
        return el.addEventListener(event, handler, false);
650
      } else {
651
        event = 'on' + event;
652
        return el.attachEvent(event, handler);
653
      }
654
    },
655
    unbindEvent: function(el, event, handler) {
656
      if (window.jQuery != null) {
657
        el = jQuery(el);
658
        if (el.off != null) {
659
          return el.off(event, handler);
660
        } else {
661
          return el.unbind(event, handler);
662
        }
663
      } else if (window.removeEventListener != null) {
664
        return el.removeEventListener(event, handler, false);
665
      } else {
666
        event = 'on' + event;
667
        return el.detachEvent(event, handler);
668
      }
669
    },
670
    getInputValue: function(el) {
671
      var o, _i, _len, _results;
672

  
673
      if (window.jQuery != null) {
674
        el = jQuery(el);
675
        switch (el[0].type) {
676
          case 'checkbox':
677
            return el.is(':checked');
678
          default:
679
            return el.val();
680
        }
681
      } else {
682
        switch (el.type) {
683
          case 'checkbox':
684
            return el.checked;
685
          case 'select-multiple':
686
            _results = [];
687
            for (_i = 0, _len = el.length; _i < _len; _i++) {
688
              o = el[_i];
689
              if (o.selected) {
690
                _results.push(o.value);
691
              }
692
            }
693
            return _results;
694
            break;
695
          default:
696
            return el.value;
697
        }
698
      }
699
    }
700
  };
701

  
702
  Rivets.binders = {
703
    enabled: function(el, value) {
704
      return el.disabled = !value;
705
    },
706
    disabled: function(el, value) {
707
      return el.disabled = !!value;
708
    },
709
    checked: {
710
      publishes: true,
711
      bind: function(el) {
712
        return Rivets.Util.bindEvent(el, 'change', this.publish);
713
      },
714
      unbind: function(el) {
715
        return Rivets.Util.unbindEvent(el, 'change', this.publish);
716
      },
717
      routine: function(el, value) {
718
        var _ref;
719

  
720
        if (el.type === 'radio') {
721
          return el.checked = ((_ref = el.value) != null ? _ref.toString() : void 0) === (value != null ? value.toString() : void 0);
722
        } else {
723
          return el.checked = !!value;
724
        }
725
      }
726
    },
727
    unchecked: {
728
      publishes: true,
729
      bind: function(el) {
730
        return Rivets.Util.bindEvent(el, 'change', this.publish);
731
      },
732
      unbind: function(el) {
733
        return Rivets.Util.unbindEvent(el, 'change', this.publish);
734
      },
735
      routine: function(el, value) {
736
        var _ref;
737

  
738
        if (el.type === 'radio') {
739
          return el.checked = ((_ref = el.value) != null ? _ref.toString() : void 0) !== (value != null ? value.toString() : void 0);
740
        } else {
741
          return el.checked = !value;
742
        }
743
      }
744
    },
745
    show: function(el, value) {
746
      return el.style.display = value ? '' : 'none';
747
    },
748
    hide: function(el, value) {
749
      return el.style.display = value ? 'none' : '';
750
    },
751
    html: function(el, value) {
752
      return el.innerHTML = value != null ? value : '';
753
    },
754
    value: {
755
      publishes: true,
756
      bind: function(el) {
757
        return Rivets.Util.bindEvent(el, 'change', this.publish);
758
      },
759
      unbind: function(el) {
760
        return Rivets.Util.unbindEvent(el, 'change', this.publish);
761
      },
762
      routine: function(el, value) {
763
        var o, _i, _len, _ref, _ref1, _ref2, _results;
764

  
765
        if (window.jQuery != null) {
766
          el = jQuery(el);
767
          if ((value != null ? value.toString() : void 0) !== ((_ref = el.val()) != null ? _ref.toString() : void 0)) {
768
            return el.val(value != null ? value : '');
769
          }
770
        } else {
771
          if (el.type === 'select-multiple') {
772
            if (value != null) {
773
              _results = [];
774
              for (_i = 0, _len = el.length; _i < _len; _i++) {
775
                o = el[_i];
776
                _results.push(o.selected = (_ref1 = o.value, __indexOf.call(value, _ref1) >= 0));
777
              }
778
              return _results;
779
            }
780
          } else if ((value != null ? value.toString() : void 0) !== ((_ref2 = el.value) != null ? _ref2.toString() : void 0)) {
781
            return el.value = value != null ? value : '';
782
          }
783
        }
784
      }
785
    },
786
    text: function(el, value) {
787
      if (el.innerText != null) {
788
        return el.innerText = value != null ? value : '';
789
      } else {
790
        return el.textContent = value != null ? value : '';
791
      }
792
    },
793
    "if": {
794
      block: true,
795
      bind: function(el) {
796
        var attr, declaration;
797

  
798
        if (this.marker == null) {
799
          attr = ['data', this.view.config.prefix, this.type].join('-').replace('--', '-');
800
          declaration = el.getAttribute(attr);
801
          this.marker = document.createComment(" rivets: " + this.type + " " + declaration + " ");
802
          el.removeAttribute(attr);
803
          el.parentNode.insertBefore(this.marker, el);
804
          return el.parentNode.removeChild(el);
805
        }
806
      },
807
      unbind: function() {
808
        var _ref;
809

  
810
        return (_ref = this.nested) != null ? _ref.unbind() : void 0;
811
      },
812
      routine: function(el, value) {
813
        var key, model, models, options, _ref;
814

  
815
        if (!!value === (this.nested == null)) {
816
          if (value) {
817
            models = {};
818
            _ref = this.view.models;
819
            for (key in _ref) {
820
              model = _ref[key];
821
              models[key] = model;
822
            }
823
            options = {
824
              binders: this.view.options.binders,
825
              formatters: this.view.options.formatters,
826
              config: this.view.options.config
827
            };
828
            (this.nested = new Rivets.View(el, models, options)).bind();
829
            return this.marker.parentNode.insertBefore(el, this.marker.nextSibling);
830
          } else {
831
            el.parentNode.removeChild(el);
832
            this.nested.unbind();
833
            return delete this.nested;
834
          }
835
        }
836
      },
837
      update: function(models) {
838
        return this.nested.update(models);
839
      }
840
    },
841
    unless: {
842
      block: true,
843
      bind: function(el) {
844
        return Rivets.binders["if"].bind.call(this, el);
845
      },
846
      unbind: function() {
847
        return Rivets.binders["if"].unbind.call(this);
848
      },
849
      routine: function(el, value) {
850
        return Rivets.binders["if"].routine.call(this, el, !value);
851
      },
852
      update: function(models) {
853
        return Rivets.binders["if"].update.call(this, models);
854
      }
855
    },
856
    "on-*": {
857
      "function": true,
858
      unbind: function(el) {
859
        if (this.handler) {
860
          return Rivets.Util.unbindEvent(el, this.args[0], this.handler);
861
        }
862
      },
863
      routine: function(el, value) {
864
        if (this.handler) {
865
          Rivets.Util.unbindEvent(el, this.args[0], this.handler);
866
        }
867
        return Rivets.Util.bindEvent(el, this.args[0], this.handler = this.eventHandler(value));
868
      }
869
    },
870
    "each-*": {
871
      block: true,
872
      bind: function(el) {
873
        var attr;
874
        if (this.marker == null) {
875
          attr = ['data', this.view.config.prefix, this.type].join('-').replace('--', '-');
876
          this.marker = document.createComment(" rivets: " + this.type + " ");
877
          this.iterated = [];
878
          el.removeAttribute(attr);
879
          el.parentNode.insertBefore(this.marker, el);
880
          return el.parentNode.removeChild(el);
881
        }
882
      },
883
      unbind: function(el) {
884
        var view, _i, _len, _ref, _results;
885

  
886
        if (this.iterated != null) {
887
          _ref = this.iterated;
888
          _results = [];
889
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
890
            view = _ref[_i];
891
            _results.push(view.unbind());
892
          }
893
          return _results;
894
        }
895
      },
896
      routine: function(el, collection) {
897
        var data, i, index, k, key, model, modelName, options, previous, template, v, view, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3, _results;
898

  
899
        modelName = this.args[0];
900
        collection = collection || [];
901
        if (this.iterated.length > collection.length) {
902
          _ref = Array(this.iterated.length - collection.length);
903
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
904
            i = _ref[_i];
905
            view = this.iterated.pop();
906
            view.unbind();
907
            this.marker.parentNode.removeChild(view.els[0]);
908
          }
909
        }
910
        _results = [];
911
        for (index = _j = 0, _len1 = collection.length; _j < _len1; index = ++_j) {
912
          model = collection[index];
913
          data = {};
914
          data[modelName] = model;
915
          if (this.iterated[index] == null) {
916
            _ref1 = this.view.models;
917
            for (key in _ref1) {
918
              model = _ref1[key];
919
              if ((_ref2 = data[key]) == null) {
920
                data[key] = model;
921
              }
922
            }
923
            previous = this.iterated.length ? this.iterated[this.iterated.length - 1].els[0] : this.marker;
924
            options = {
925
              binders: this.view.options.binders,
926
              formatters: this.view.options.formatters,
927
              config: {}
928
            };
929
            _ref3 = this.view.options.config;
930
            for (k in _ref3) {
931
              v = _ref3[k];
932
              options.config[k] = v;
933
            }
934
            options.config.preloadData = true;
935
            template = el.cloneNode(true);
936
            view = new Rivets.View(template, data, options);
937
            view.bind();
938
            this.iterated.push(view);
939
            _results.push(this.marker.parentNode.insertBefore(template, previous.nextSibling));
940
          } else if (this.iterated[index].models[modelName] !== model) {
941
            _results.push(this.iterated[index].update(data));
942
          } else {
943
            _results.push(void 0);
944
          }
945
        }
946
        return _results;
947
      },
948
      update: function(models) {
949
        var data, key, model, view, _i, _len, _ref, _results;
950

  
951
        data = {};
952
        for (key in models) {
953
          model = models[key];
954
          if (key !== this.args[0]) {
955
            data[key] = model;
956
          }
957
        }
958
        _ref = this.iterated;
959
        _results = [];
960
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
961
          view = _ref[_i];
962
          _results.push(view.update(data));
963
        }
964
        return _results;
965
      }
966
    },
967
    "class-*": function(el, value) {
968
      var elClass;
969

  
970
      elClass = " " + el.className + " ";
971
      if (!value === (elClass.indexOf(" " + this.args[0] + " ") !== -1)) {
972
        return el.className = value ? "" + el.className + " " + this.args[0] : elClass.replace(" " + this.args[0] + " ", ' ').trim();
973
      }
974
    },
975
    "*": function(el, value) {
976
      if (value) {
977
        return el.setAttribute(this.type, value);
978
      } else {
979
        return el.removeAttribute(this.type);
980
      }
981
    }
982
  };
983

  
984
  Rivets.internalBinders = {
985
    textNode: function(node, value) {
986
      return node.data = value != null ? value : '';
987
    }
988
  };
989

  
990
  Rivets.components = {};
991

  
992
  Rivets.config = {
993
    preloadData: true,
994
    handler: function(context, ev, binding) {
995
      return this.call(context, ev, binding.view.models);
996
    }
997
  };
998

  
999
  Rivets.formatters = {};
1000

  
1001
  Rivets.factory = function(exports) {
1002
    exports._ = Rivets;
1003
    exports.binders = Rivets.binders;
1004
    exports.components = Rivets.components;
1005
    exports.formatters = Rivets.formatters;
1006
    exports.config = Rivets.config;
1007
    exports.configure = function(options) {
1008
      var property, value;
1009

  
1010
      if (options == null) {
1011
        options = {};
1012
      }
1013
      for (property in options) {
1014
        value = options[property];
1015
        Rivets.config[property] = value;
1016
      }
1017
    };
1018
    return exports.bind = function(el, models, options) {
1019
      var view;
1020

  
1021
      if (models == null) {
1022
        models = {};
1023
      }
1024
      if (options == null) {
1025
        options = {};
1026
      }
1027
      view = new Rivets.View(el, models, options);
1028
      view.bind();
1029
      return view;
1030
    };
1031
  };
1032

  
1033
  if (typeof exports === 'object') {
1034
    Rivets.factory(exports);
1035
  } else if (typeof define === 'function' && define.amd) {
1036
    define(['exports'], function(exports) {
1037
      Rivets.factory(this.rivets = exports);
1038
      return exports;
1039
    });
1040
  } else {
1041
    Rivets.factory(this.rivets = {});
1042
  }
1043

  
1044
}).call(this);

Also available in: Unified diff