batman-rails 0.15.4 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 827c6fe292b697c321c92b6ee0f3034720f0081c
4
- data.tar.gz: ca9f83be4e3331e7b3dab5eb4cdc80ee0296ff7d
3
+ metadata.gz: 9c8deae7a6dad1d59f1cb67e92a798e50414598d
4
+ data.tar.gz: a7b266ed03e94f18b5db5b585e4de18138e9510a
5
5
  SHA512:
6
- metadata.gz: a00bce8ce04bcdbac33a8ff75f6b32159613a1840555f4430341733c0933d32aed1789c26d9df0871a91e1979b37d0af5776b9f559e5a91b5cb2d2582e797956
7
- data.tar.gz: 92998e811e228de5b19e0731b77c47b36af61328156109f8f4ce28aa137c81b6d45f423803932c637ebc92f4ea3ee7dfc6ec0caf21919f643f6b8cd605facb3a
6
+ metadata.gz: a69ac93e5f28fc771eae9177305d3cc7c821d8a8f15a01220785b1562930f16b0577187f62c54185739cbf82af8ce27b435bc381eee4d795a18c136f276e896e
7
+ data.tar.gz: 2bd249704f4a95b3dc1bf3fc4acf8e94a7d09788cf362ff4b01b833a4924a4b0cec9883d16707fa134c972226732413609077ae29d682da5ea2bee6e282f0991
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Batman::Rails
2
2
 
3
- Easily setup and use batman.js (0.15) with Rails 4
3
+ Easily setup and use batman.js (0.16) with Rails 4
4
4
 
5
5
  ## Installation
6
6
 
@@ -15,25 +15,32 @@ And then execute:
15
15
 
16
16
  ### Layout and namespacing
17
17
 
18
- Running `rails generate batman:app` will create the following directory structure under `app/assets/app_name/`:
18
+ Running `rails generate batman:app` will:
19
19
 
20
- models/
21
- views/
22
- controllers/
23
- html/
24
- lib/
20
+ - create the following directory structure under `app/assets/batman/`:
21
+
22
+ ```
23
+ - models/
24
+ - views/
25
+ - controllers/
26
+ - html/
27
+ - lib/
28
+ - app_name.coffee # initial setup & requires
29
+ ```
25
30
 
26
- It will also create a toplevel `app_name.coffee` file to setup namespacing and setup initial requires.
31
+ - create a controller & route to server your batman.js app from `/` and serve HTML
32
+ - configure the asset pipeline to precompile `app_name.coffee`
27
33
 
28
34
  ## Generators
29
- Batman-Rails provides 3 simple generators to help get you started using Batman.js with Rails.
30
- The generators will only create client side code (CoffeeScript).
35
+
36
+ Batman-Rails provides 3 simple generators to help get you started using batman.js with Rails.
37
+ The generators will only create client-side code (CoffeeScript).
31
38
 
32
39
  ### Model Generator
33
40
 
34
41
  rails generate batman:model
35
42
 
36
- This generator creates a batman model inside `app/assets/app_name/models` to be used to talk to the Rails backend.
43
+ This generator creates a batman model inside `app/assets/batman/models` to be used to talk to the Rails backend.
37
44
 
38
45
  ### Controllers
39
46
 
@@ -67,6 +74,22 @@ Install the gem and generate scaffolding.
67
74
 
68
75
  You now have installed the `batman-rails` gem, setup a default directory structure for your frontend batman code. Then you generated the usual Rails server side scaffolding and finally generated Batman.js code to provide a simple single page app.
69
76
 
77
+ ## Precompiling Views
78
+
79
+ In production, you may want to send all your HTML templates with the first request rather than sending them as-needed. `batman-rails` includes a view helper to do this. Add it to your application layout:
80
+
81
+ ```erb
82
+ <%= batman_define_views %>
83
+ ```
84
+
85
+ It will gather HTML from `app/assets/batman/html` and interpolate them into JS code to preload `Batman.View.store`. If your HTML is in another directory, pass that directory as an option:
86
+
87
+ ```erb
88
+ <%= batman_define_views(path_to_html: "app/assets/templates/">
89
+ ```
90
+
91
+ Now it will gather HTML from `app/assets/templates`!
92
+
70
93
  ## Contributing
71
94
 
72
95
  1. Fork it
@@ -1,8 +1,14 @@
1
1
  require "batman-rails/version"
2
+ require "batman-rails/helpers/define_view_helper"
2
3
 
3
4
  module Batman
4
5
  module Rails
5
6
  class Engine < ::Rails::Engine
7
+ initializer 'batman_rails.action_controller' do |app|
8
+ ActiveSupport.on_load :action_controller do
9
+ helper Batman::DefineViewHelper
10
+ end
11
+ end
6
12
  end
7
13
  end
8
14
  end
@@ -0,0 +1,45 @@
1
+ module Batman
2
+ module DefineViewHelper
3
+ def batman_define_views(options={})
4
+ content_tag :script do
5
+ template_js_string = BatmanView.all(self, options).map(&:inline_preload_javascript).join
6
+ raw %Q[(function(){#{template_js_string}})();]
7
+ end
8
+ end
9
+
10
+ class BatmanView
11
+ PATH_TO_HTML = "app/assets/batman/html/"
12
+
13
+ attr_accessor :pathname, :render_context, :path_to_html
14
+
15
+ def self.all(render_context, options={})
16
+ path_to_html = options[:path_to_html] || PATH_TO_HTML
17
+ glob_path = File.join(path_to_html, "**/*")
18
+ valid_files = Dir.glob(glob_path).select { |pathname| File.file?(pathname) }
19
+ valid_files.collect do |pathname|
20
+ new(pathname, render_context, path_to_html: path_to_html)
21
+ end
22
+ end
23
+
24
+ def initialize(pathname, render_context, options={})
25
+ @pathname = pathname
26
+ @render_context = render_context
27
+ @path_to_html = options[:path_to_html] || PATH_TO_HTML
28
+ end
29
+
30
+ def inline_preload_javascript
31
+ %Q{Batman.View.store.set(#{name.to_json}, #{content.to_json});}
32
+ end
33
+
34
+ private
35
+
36
+ def name
37
+ pathname.split(path_to_html).last.gsub(/\.html\.[a-z]+$/, '').to_s
38
+ end
39
+
40
+ def content
41
+ render_context.render(file: pathname)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,6 +1,6 @@
1
1
  module Batman
2
2
  module Rails
3
- VERSION = "0.15.4"
4
- BATMAN_VERSION = "0.15.0"
3
+ VERSION = "0.16.0"
4
+ BATMAN_VERSION = "0.16.0"
5
5
  end
6
6
  end
@@ -26,6 +26,10 @@ module Batman
26
26
  inject_into_file "config/routes.rb", :after => "#{js_application_name}::Application.routes.draw do\n" do
27
27
  route_catchall
28
28
  end
29
+
30
+ inject_into_file "config/application.rb", :after => "class Application < Rails::Application\n" do
31
+ precompile_app
32
+ end
29
33
  end
30
34
 
31
35
  end
@@ -72,6 +76,12 @@ module Batman
72
76
  CODE
73
77
  end
74
78
 
79
+ def precompile_app
80
+ <<-CODE
81
+ \n config.assets.precompile += ['#{application_name}.js']\n
82
+ CODE
83
+ end
84
+
75
85
  def es5_requires
76
86
  <<-CODE
77
87
  #= require batman/es5-shim\n
@@ -51,7 +51,7 @@ module Batman
51
51
  module ClassMethods
52
52
  def requires_app_name
53
53
  class_option :app_name, :type => :string, :optional => true,
54
- :desc => "Name of the Batman app (defaults to the Rails app name"
54
+ :desc => "Name of the Batman app (defaults to the Rails app name)"
55
55
  end
56
56
  end
57
57
  end
@@ -1,7 +1,13 @@
1
1
  class <%= js_app_name %>Controller < ApplicationController
2
2
 
3
3
  def index
4
- render nothing: true, layout: '<%= app_name %>'
4
+ if request.xhr?
5
+ prefix_length = Rails.application.config.assets.prefix.length + 1
6
+ path = request.path[prefix_length..-1]
7
+ render :text => Rails.application.assets[path]
8
+ else
9
+ render nothing: true, layout: '<%= app_name %>'
10
+ end
5
11
  end
6
12
 
7
13
  end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+ require 'generators/batman/view_generator'
3
+
4
+ class DefineViewHelperTest < ActionView::TestCase
5
+ include Batman::DefineViewHelper
6
+ tests Batman::DefineViewHelper
7
+
8
+ test "it defaults to looking in app/assets/batman/html" do
9
+ File.expects(:join).with("app/assets/batman/html/", "**/*").returns("app/assets/batman/html/**/*")
10
+ batman_define_views
11
+ end
12
+
13
+ test "it can be given override path" do
14
+ File.expects(:join).with("app/assets/batman/templates/", "**/*").returns("app/assets/batman/templates/**/*")
15
+ batman_define_views(path_to_html: "app/assets/batman/templates")
16
+ end
17
+
18
+ end
@@ -0,0 +1,140 @@
1
+ (function() {
2
+ var __hasProp = {}.hasOwnProperty,
3
+ __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; };
4
+
5
+ Batman.I18N = (function(_super) {
6
+ __extends(I18N, _super);
7
+
8
+ I18N.defaultLocale = "en";
9
+
10
+ I18N.useFallback = false;
11
+
12
+ I18N.classAccessor('locale', {
13
+ get: function() {
14
+ return this.locale || this.get('defaultLocale');
15
+ },
16
+ set: function(k, v) {
17
+ return this.locale = v;
18
+ },
19
+ unset: function() {
20
+ var x;
21
+ x = this.locale;
22
+ delete this.locale;
23
+ return x;
24
+ }
25
+ });
26
+
27
+ I18N.classAccessor('translations', function() {
28
+ return this.get("locales." + (this.get('locale')));
29
+ });
30
+
31
+ I18N.classAccessor('defaultTranslations', function() {
32
+ return this.get("locales." + this.defaultLocale);
33
+ });
34
+
35
+ I18N.translate = function(key, values) {
36
+ var translation;
37
+ translation = this.get("translations." + key);
38
+ if (this.useFallback) {
39
+ translation || (translation = this.get("defaultTranslations." + key));
40
+ }
41
+ if (translation == null) {
42
+ Batman.developer.warn("Warning, undefined translation " + key + " when in locale " + (this.get('locale')));
43
+ return "";
44
+ }
45
+ if (!values) {
46
+ return translation;
47
+ }
48
+ return Batman.helpers.interpolate(translation, values);
49
+ };
50
+
51
+ I18N.enable = function() {
52
+ var _this = this;
53
+ this._oldTranslation = Batman.translate;
54
+ this.locales.set('en', Batman.translate.messages);
55
+ return Batman.translate = function() {
56
+ return _this.translate.apply(_this, arguments);
57
+ };
58
+ };
59
+
60
+ I18N.disable = function() {
61
+ return Batman.translate = this._oldTranslation;
62
+ };
63
+
64
+ function I18N() {
65
+ Batman.developer.error("Can't instantiate i18n!");
66
+ }
67
+
68
+ return I18N;
69
+
70
+ })(Batman.Object);
71
+
72
+ Batman.I18N.LocalesStorage = (function(_super) {
73
+ __extends(LocalesStorage, _super);
74
+
75
+ function LocalesStorage() {
76
+ this.isStorage = true;
77
+ this._storage = {};
78
+ LocalesStorage.__super__.constructor.apply(this, arguments);
79
+ }
80
+
81
+ LocalesStorage.accessor({
82
+ get: function(k) {
83
+ var _this = this;
84
+ if (!this._storage[k]) {
85
+ this._storage[k] = {};
86
+ new Batman.Request({
87
+ url: "/locales/" + k + ".json",
88
+ success: function(data) {
89
+ return _this.set(k, data[k]);
90
+ },
91
+ error: function(xhr) {
92
+ throw new Error("Couldn't load locale file " + k + "!");
93
+ }
94
+ });
95
+ }
96
+ return this._storage[k];
97
+ },
98
+ set: function(k, v) {
99
+ return this._storage[k] = v;
100
+ },
101
+ unset: function(k) {
102
+ var x;
103
+ x = this._storage[k];
104
+ delete this._storage[k];
105
+ return x;
106
+ }
107
+ });
108
+
109
+ return LocalesStorage;
110
+
111
+ })(Batman.Object);
112
+
113
+ Batman.I18N.set('locales', new Batman.I18N.LocalesStorage);
114
+
115
+ Batman.Filters.t = Batman.Filters.translate = function(string, interpolationKeypaths, binding) {
116
+ var translated;
117
+ if (!binding) {
118
+ binding = interpolationKeypaths;
119
+ interpolationKeypaths = void 0;
120
+ }
121
+ if (string == null) {
122
+ return "";
123
+ }
124
+ if (!(binding.key && binding.key.substr(0, 2) === "t.")) {
125
+ translated = Batman.I18N.translate(string);
126
+ if (translated) {
127
+ string = translated;
128
+ }
129
+ }
130
+ return Batman.Filters.interpolate.call(this, string, interpolationKeypaths, binding);
131
+ };
132
+
133
+ Batman.config.translations = true;
134
+
135
+ }).call(this);
136
+
137
+ (function() {
138
+
139
+
140
+ }).call(this);
@@ -12,7 +12,7 @@
12
12
  })(Batman.Object, mixins, function(){});
13
13
  };
14
14
 
15
- Batman.version = '0.14.1';
15
+ Batman.version = '0.16.0';
16
16
 
17
17
  Batman.config = {
18
18
  pathToApp: '/',
@@ -433,6 +433,7 @@
433
433
  "<": "&lt;",
434
434
  ">": "&gt;",
435
435
  "\"": "&#34;",
436
+ "/": "&#47;",
436
437
  "'": "&#39;"
437
438
  };
438
439
 
@@ -624,11 +625,11 @@
624
625
  }).call(this);
625
626
 
626
627
  (function() {
627
- var Inflector, camelize_rx, capitalize_rx, humanize_rx1, humanize_rx2, humanize_rx3, underscore_rx1, underscore_rx2;
628
+ var Inflector, camelize_rx, humanize_rx1, humanize_rx2, humanize_rx3, titleize_rx, underscore_rx1, underscore_rx2;
628
629
 
629
630
  camelize_rx = /(?:^|_|\-)(.)/g;
630
631
 
631
- capitalize_rx = /(^|\s)([a-z])/g;
632
+ titleize_rx = /(^|\s)([a-z])/g;
632
633
 
633
634
  underscore_rx1 = /([A-Z]+)([A-Z][a-z])/g;
634
635
 
@@ -636,7 +637,7 @@
636
637
 
637
638
  humanize_rx1 = /_id$/;
638
639
 
639
- humanize_rx2 = /_|-/g;
640
+ humanize_rx2 = /_|-|\./g;
640
641
 
641
642
  humanize_rx3 = /^\w/g;
642
643
 
@@ -675,11 +676,15 @@
675
676
  underscore: function(string) {
676
677
  return string.replace(underscore_rx1, '$1_$2').replace(underscore_rx2, '$1_$2').replace('-', '_').toLowerCase();
677
678
  },
678
- capitalize: function(string) {
679
- return string.replace(capitalize_rx, function(m, p1, p2) {
679
+ titleize: function(string) {
680
+ return string.replace(titleize_rx, function(m, p1, p2) {
680
681
  return p1 + p2.toUpperCase();
681
682
  });
682
683
  },
684
+ capitalize: function(string) {
685
+ Batman.developer.deprecated('capitalize', 'Renamed to titleize.');
686
+ return Batman.helpers.titleize(string);
687
+ },
683
688
  trim: function(string) {
684
689
  if (string) {
685
690
  return string.trim();
@@ -709,6 +714,17 @@
709
714
  return string.replace(humanize_rx1, '').replace(humanize_rx2, ' ').replace(humanize_rx3, function(match) {
710
715
  return match.toUpperCase();
711
716
  });
717
+ },
718
+ toSentence: function(array) {
719
+ var itemString, last;
720
+ if (array.length < 3) {
721
+ return array.join(' and ');
722
+ } else {
723
+ last = array.pop();
724
+ itemString = array.join(', ');
725
+ itemString += ", and " + last;
726
+ return itemString;
727
+ }
712
728
  }
713
729
  };
714
730
 
@@ -1050,7 +1066,7 @@
1050
1066
  };
1051
1067
 
1052
1068
  Event.prototype.allowAndFire = function() {
1053
- return this.allowAndFireWithContext(this.handlerContext, arguments);
1069
+ return this.allowAndFireWithContext(this.handlerContext(), arguments);
1054
1070
  };
1055
1071
 
1056
1072
  Event.prototype.allowAndFireWithContext = function(context, args) {
@@ -1298,7 +1314,7 @@
1298
1314
  }
1299
1315
  result = true;
1300
1316
  this.forEach(function() {
1301
- return result = result && f.apply(ctx, arguments);
1317
+ return result = result && !!f.apply(ctx, arguments);
1302
1318
  });
1303
1319
  return result;
1304
1320
  },
@@ -1309,7 +1325,7 @@
1309
1325
  }
1310
1326
  result = false;
1311
1327
  this.forEach(function() {
1312
- return result = result || f.apply(ctx, arguments);
1328
+ return result = result || !!f.apply(ctx, arguments);
1313
1329
  });
1314
1330
  return result;
1315
1331
  },
@@ -1690,7 +1706,24 @@
1690
1706
  return obj;
1691
1707
  };
1692
1708
 
1693
- SimpleHash.prototype.toJSON = SimpleHash.prototype.toObject;
1709
+ SimpleHash.prototype.toJSON = function() {
1710
+ var key, obj, objectKey, value, values, _ref, _ref1, _ref2;
1711
+ obj = {};
1712
+ _ref = this._storage;
1713
+ for (key in _ref) {
1714
+ value = _ref[key];
1715
+ obj[this.unprefixedKey(key)] = (value != null ? typeof value.toJSON === "function" ? value.toJSON() : void 0 : void 0) || value;
1716
+ }
1717
+ if (this._objectStorage) {
1718
+ _ref1 = this._objectStorage;
1719
+ for (key in _ref1) {
1720
+ values = _ref1[key];
1721
+ _ref2 = values[0], objectKey = _ref2[0], value = _ref2[1];
1722
+ obj[key] = (value != null ? typeof value.toJSON === "function" ? value.toJSON() : void 0 : void 0) || value;
1723
+ }
1724
+ }
1725
+ return obj;
1726
+ };
1694
1727
 
1695
1728
  return SimpleHash;
1696
1729
 
@@ -1732,6 +1765,14 @@
1732
1765
  return this.get(label);
1733
1766
  };
1734
1767
 
1768
+ AssociationCurator.prototype.getAll = function() {
1769
+ var allAssociations, typeSets, _ref;
1770
+ typeSets = this._byTypeStorage.map(function(label, typeSet) {
1771
+ return typeSet;
1772
+ });
1773
+ return allAssociations = (_ref = new Batman.SimpleSet).merge.apply(_ref, typeSets);
1774
+ };
1775
+
1735
1776
  AssociationCurator.prototype.reset = function() {
1736
1777
  this.forEach(function(label, association) {
1737
1778
  return association.reset();
@@ -1921,6 +1962,14 @@
1921
1962
  return merged;
1922
1963
  };
1923
1964
 
1965
+ SimpleSet.prototype.mappedTo = function(key) {
1966
+ var _this = this;
1967
+ this._mappings || (this._mappings = new Batman.SimpleHash);
1968
+ return this._mappings.getOrSet(key, function() {
1969
+ return new Batman.SetMapping(_this, key);
1970
+ });
1971
+ };
1972
+
1924
1973
  SimpleSet.prototype.indexedBy = function(key) {
1925
1974
  this._indexes || (this._indexes = new Batman.SimpleHash);
1926
1975
  return this._indexes.get(key) || this._indexes.set(key, new Batman.SetIndex(this, key));
@@ -2100,6 +2149,8 @@
2100
2149
 
2101
2150
  Property.prototype.isDead = false;
2102
2151
 
2152
+ Property.prototype.isBatchingChanges = false;
2153
+
2103
2154
  Property.prototype.registerAsMutableSource = function() {
2104
2155
  return Batman.Property.registerSource(this);
2105
2156
  };
@@ -2241,7 +2292,9 @@
2241
2292
 
2242
2293
  Property.prototype.sourceChangeHandler = function() {
2243
2294
  var _this = this;
2244
- this._sourceChangeHandler || (this._sourceChangeHandler = this._handleSourceChange.bind(this));
2295
+ this._sourceChangeHandler || (this._sourceChangeHandler = function() {
2296
+ return _this._handleSourceChange();
2297
+ });
2245
2298
  Batman.developer["do"](function() {
2246
2299
  return _this._sourceChangeHandler.property = _this;
2247
2300
  });
@@ -2256,7 +2309,7 @@
2256
2309
  } else if (!this.isFinal() && !this.hasObservers()) {
2257
2310
  this.cached = false;
2258
2311
  return this._removeHandlers();
2259
- } else {
2312
+ } else if (!this.isBatchingChanges) {
2260
2313
  return this.refresh();
2261
2314
  }
2262
2315
  };
@@ -2750,6 +2803,10 @@
2750
2803
  definition.invert = true;
2751
2804
  return new Batman.DOM.InsertionBinding(definition);
2752
2805
  },
2806
+ deferif: function(definition) {
2807
+ definition.invert = true;
2808
+ return new Batman.DOM.DeferredRenderBinding(definition);
2809
+ },
2753
2810
  renderif: function(definition) {
2754
2811
  return new Batman.DOM.DeferredRenderBinding(definition);
2755
2812
  },
@@ -2827,9 +2884,9 @@
2827
2884
  __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; };
2828
2885
 
2829
2886
  Batman.DOM.events = {
2830
- click: function(node, callback, view, eventName, preventDefault) {
2887
+ primaryInteractionEvent: function(node, callback, view, eventName, preventDefault) {
2831
2888
  if (eventName == null) {
2832
- eventName = 'click';
2889
+ eventName = Batman.DOM.primaryInteractionEventName;
2833
2890
  }
2834
2891
  if (preventDefault == null) {
2835
2892
  preventDefault = true;
@@ -2853,6 +2910,15 @@
2853
2910
  }
2854
2911
  return node;
2855
2912
  },
2913
+ click: function(node, callback, view, eventName, preventDefault) {
2914
+ if (eventName == null) {
2915
+ eventName = 'click';
2916
+ }
2917
+ if (preventDefault == null) {
2918
+ preventDefault = true;
2919
+ }
2920
+ return Batman.DOM.events.primaryInteractionEvent(node, callback, view, eventName, preventDefault);
2921
+ },
2856
2922
  doubleclick: function(node, callback, view) {
2857
2923
  return Batman.DOM.events.click(node, callback, view, 'dblclick');
2858
2924
  },
@@ -2943,6 +3009,8 @@
2943
3009
  return true;
2944
3010
  };
2945
3011
 
3012
+ Batman.DOM.primaryInteractionEventName = 'click';
3013
+
2946
3014
  }).call(this);
2947
3015
 
2948
3016
  (function() {
@@ -3001,6 +3069,13 @@
3001
3069
  event: function(definition) {
3002
3070
  return new Batman.DOM.EventBinding(definition);
3003
3071
  },
3072
+ track: function(definition) {
3073
+ if (definition.attr === 'view') {
3074
+ return new Batman.DOM.ViewTrackingBinding(definition);
3075
+ } else if (definition.attr === 'click') {
3076
+ return new Batman.DOM.ClickTrackingBinding(definition);
3077
+ }
3078
+ },
3004
3079
  addclass: function(definition) {
3005
3080
  return new Batman.DOM.AddClassBinding(definition);
3006
3081
  },
@@ -3211,6 +3286,62 @@
3211
3286
  return this._batmanID();
3212
3287
  });
3213
3288
 
3289
+ BatmanObject.delegate = function() {
3290
+ var options, properties, _i,
3291
+ _this = this;
3292
+ properties = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), options = arguments[_i++];
3293
+ if (options == null) {
3294
+ options = {};
3295
+ }
3296
+ if (!options.to) {
3297
+ Batman.developer.warn('delegate must include to option', this, properties);
3298
+ }
3299
+ return properties.forEach(function(property) {
3300
+ return _this.accessor(property, {
3301
+ get: function() {
3302
+ var _ref;
3303
+ return (_ref = this.get(options.to)) != null ? _ref.get(property) : void 0;
3304
+ },
3305
+ set: function(key, value) {
3306
+ var _ref;
3307
+ return (_ref = this.get(options.to)) != null ? _ref.set(property, value) : void 0;
3308
+ },
3309
+ unset: function() {
3310
+ var _ref;
3311
+ return (_ref = this.get(options.to)) != null ? _ref.unset(property) : void 0;
3312
+ }
3313
+ });
3314
+ });
3315
+ };
3316
+
3317
+ BatmanObject.classDelegate = function() {
3318
+ var options, properties, _i,
3319
+ _this = this;
3320
+ properties = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), options = arguments[_i++];
3321
+ if (options == null) {
3322
+ options = {};
3323
+ }
3324
+ if (!options.to) {
3325
+ Batman.developer.warn('delegate must include to option', this, properties);
3326
+ }
3327
+ return properties.forEach(function(property) {
3328
+ return _this.classAccessor(property, {
3329
+ get: function() {
3330
+ var _ref;
3331
+ return (_ref = this.get(options.to)) != null ? _ref.get(property) : void 0;
3332
+ },
3333
+ set: function(key, value) {
3334
+ var _ref;
3335
+ return (_ref = this.get(options.to)) != null ? _ref.set(property, value) : void 0;
3336
+ },
3337
+ unset: function() {
3338
+ var _ref;
3339
+ return (_ref = this.get(options.to)) != null ? _ref.unset(property) : void 0;
3340
+ }
3341
+ });
3342
+ });
3343
+ };
3344
+
3214
3345
  function BatmanObject() {
3215
3346
  var mixins;
3216
3347
  mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
@@ -3250,6 +3381,23 @@
3250
3381
  return obj;
3251
3382
  };
3252
3383
 
3384
+ BatmanObject.prototype.batchAccessorChanges = function() {
3385
+ var i, key, properties, property, result, wrappedFunction, _i, _j, _k, _len, _len1;
3386
+ properties = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), wrappedFunction = arguments[_i++];
3387
+ for (i = _j = 0, _len = properties.length; _j < _len; i = ++_j) {
3388
+ key = properties[i];
3389
+ property = properties[i] = this.property(key);
3390
+ property.isBatchingChanges = true;
3391
+ }
3392
+ result = wrappedFunction.call(this);
3393
+ for (_k = 0, _len1 = properties.length; _k < _len1; _k++) {
3394
+ property = properties[_k];
3395
+ property.isBatchingChanges = false;
3396
+ property.refresh();
3397
+ }
3398
+ return result;
3399
+ };
3400
+
3253
3401
  return BatmanObject;
3254
3402
 
3255
3403
  })(Object);
@@ -3274,7 +3422,7 @@
3274
3422
  this.parseTree(this.node);
3275
3423
  }
3276
3424
 
3277
- bindingSortOrder = ["defineview", "foreach", "renderif", "view", "formfor", "context", "bind", "source", "target"];
3425
+ bindingSortOrder = ["defineview", "foreach", "renderif", "view", "formfor", "context", "bind", "source", "target", "track", "event"];
3278
3426
 
3279
3427
  viewBackedBindings = ["foreach", "renderif", "formfor", "context"];
3280
3428
 
@@ -3407,7 +3555,7 @@
3407
3555
  });
3408
3556
  } else {
3409
3557
  return Batman.t('errors.format', {
3410
- attribute: Batman.helpers.humanize(this.attribute),
3558
+ attribute: Batman.helpers.humanize(Batman.ValidationError.singularizeAssociated(this.attribute)),
3411
3559
  message: this.message
3412
3560
  });
3413
3561
  }
@@ -3420,6 +3568,15 @@
3420
3568
  });
3421
3569
  }
3422
3570
 
3571
+ ValidationError.singularizeAssociated = function(attribute) {
3572
+ var i, parts, _i, _ref;
3573
+ parts = attribute.split(".");
3574
+ for (i = _i = 0, _ref = parts.length - 1; _i < _ref; i = _i += 1) {
3575
+ parts[i] = Batman.helpers.singularize(parts[i]);
3576
+ }
3577
+ return parts.join(" ");
3578
+ };
3579
+
3423
3580
  return ValidationError;
3424
3581
 
3425
3582
  })(Batman.Object);
@@ -3474,6 +3631,19 @@
3474
3631
 
3475
3632
  })(StorageAdapter.StorageError);
3476
3633
 
3634
+ StorageAdapter.UnauthorizedError = (function(_super1) {
3635
+ __extends(UnauthorizedError, _super1);
3636
+
3637
+ UnauthorizedError.prototype.name = 'UnauthorizedError';
3638
+
3639
+ function UnauthorizedError(message) {
3640
+ UnauthorizedError.__super__.constructor.call(this, message || "Storage operation denied due to invalid credentials!");
3641
+ }
3642
+
3643
+ return UnauthorizedError;
3644
+
3645
+ })(StorageAdapter.StorageError);
3646
+
3477
3647
  StorageAdapter.NotAllowedError = (function(_super1) {
3478
3648
  __extends(NotAllowedError, _super1);
3479
3649
 
@@ -3500,6 +3670,19 @@
3500
3670
 
3501
3671
  })(StorageAdapter.StorageError);
3502
3672
 
3673
+ StorageAdapter.EntityTooLargeError = (function(_super1) {
3674
+ __extends(EntityTooLargeError, _super1);
3675
+
3676
+ EntityTooLargeError.prototype.name = "EntityTooLargeError";
3677
+
3678
+ function EntityTooLargeError(message) {
3679
+ EntityTooLargeError.__super__.constructor.call(this, message || "Storage operation denied due to size constraints!");
3680
+ }
3681
+
3682
+ return EntityTooLargeError;
3683
+
3684
+ })(StorageAdapter.StorageError);
3685
+
3503
3686
  StorageAdapter.UnprocessableRecordError = (function(_super1) {
3504
3687
  __extends(UnprocessableRecordError, _super1);
3505
3688
 
@@ -3539,6 +3722,19 @@
3539
3722
 
3540
3723
  })(StorageAdapter.StorageError);
3541
3724
 
3725
+ StorageAdapter.BadGatewayError = (function(_super1) {
3726
+ __extends(BadGatewayError, _super1);
3727
+
3728
+ BadGatewayError.prototype.name = "BadGatewayError";
3729
+
3730
+ function BadGatewayError(message) {
3731
+ BadGatewayError.__super__.constructor.call(this, message || "Storage operation failed due to unavailability of the backend!");
3732
+ }
3733
+
3734
+ return BadGatewayError;
3735
+
3736
+ })(StorageAdapter.StorageError);
3737
+
3542
3738
  function StorageAdapter(model) {
3543
3739
  var constructor;
3544
3740
  StorageAdapter.__super__.constructor.call(this, {
@@ -3555,6 +3751,25 @@
3555
3751
 
3556
3752
  StorageAdapter.prototype.isStorageAdapter = true;
3557
3753
 
3754
+ StorageAdapter.prototype.onlyCertainAttributes = function(json, only) {
3755
+ var key;
3756
+ for (key in json) {
3757
+ if (only.indexOf(key) < 0) {
3758
+ delete json[key];
3759
+ }
3760
+ }
3761
+ return json;
3762
+ };
3763
+
3764
+ StorageAdapter.prototype.exceptCertainAttributes = function(json, except) {
3765
+ var key, _i, _len;
3766
+ for (_i = 0, _len = except.length; _i < _len; _i++) {
3767
+ key = except[_i];
3768
+ delete json[key];
3769
+ }
3770
+ return json;
3771
+ };
3772
+
3558
3773
  StorageAdapter.prototype.storageKey = function(record) {
3559
3774
  var model;
3560
3775
  model = (record != null ? record.constructor : void 0) || this.model;
@@ -3565,14 +3780,14 @@
3565
3780
  if (constructor == null) {
3566
3781
  constructor = this.model;
3567
3782
  }
3568
- return constructor._makeOrFindRecordFromData(attributes);
3783
+ return constructor.createFromJSON(attributes);
3569
3784
  };
3570
3785
 
3571
3786
  StorageAdapter.prototype.getRecordsFromData = function(attributeSet, constructor) {
3572
3787
  if (constructor == null) {
3573
3788
  constructor = this.model;
3574
3789
  }
3575
- return constructor._makeOrFindRecordsFromData(attributeSet);
3790
+ return constructor.createMultipleFromJSON(attributeSet);
3576
3791
  };
3577
3792
 
3578
3793
  StorageAdapter.skipIfError = function(f) {
@@ -3744,7 +3959,7 @@
3744
3959
  }
3745
3960
  this.url = function(options) {
3746
3961
  var childSegment, parentID, plural;
3747
- childSegment = Batman.helpers.pluralize(this.get('resourceName').toLowerCase());
3962
+ childSegment = this.storageKey || Batman.helpers.pluralize(this.get('resourceName').toLowerCase());
3748
3963
  for (key in parents) {
3749
3964
  plural = parents[key];
3750
3965
  parentID = options.data[key];
@@ -3757,7 +3972,7 @@
3757
3972
  };
3758
3973
  return this.prototype.url = function() {
3759
3974
  var childSegment, id, parentID, plural, url;
3760
- childSegment = Batman.helpers.pluralize(this.constructor.get('resourceName').toLowerCase());
3975
+ childSegment = this.constructor.storageKey || Batman.helpers.pluralize(this.constructor.get('resourceName').toLowerCase());
3761
3976
  for (key in parents) {
3762
3977
  plural = parents[key];
3763
3978
  parentID = this.get('dirtyKeys').get(key);
@@ -3916,6 +4131,12 @@
3916
4131
  RestStorage.prototype.before('create', 'update', RestStorage.skipIfError(function(env, next) {
3917
4132
  var data, json, namespace;
3918
4133
  json = env.subject.toJSON();
4134
+ if (env.options.only) {
4135
+ json = this.onlyCertainAttributes(json, env.options.only);
4136
+ }
4137
+ if (env.options.except) {
4138
+ json = this.exceptCertainAttributes(json, env.options.except);
4139
+ }
3919
4140
  if (namespace = this.recordJsonNamespace(env.subject)) {
3920
4141
  data = {};
3921
4142
  data[namespace] = json;
@@ -4033,13 +4254,16 @@
4033
4254
 
4034
4255
  RestStorage._statusCodeErrors = {
4035
4256
  '0': RestStorage.CommunicationError,
4257
+ '401': RestStorage.UnauthorizedError,
4036
4258
  '403': RestStorage.NotAllowedError,
4037
4259
  '404': RestStorage.NotFoundError,
4038
4260
  '406': RestStorage.NotAcceptableError,
4039
4261
  '409': RestStorage.RecordExistsError,
4262
+ '413': RestStorage.EntityTooLargeError,
4040
4263
  '422': RestStorage.UnprocessableRecordError,
4041
4264
  '500': RestStorage.InternalStorageError,
4042
- '501': RestStorage.NotImplementedError
4265
+ '501': RestStorage.NotImplementedError,
4266
+ '502': RestStorage.BadGatewayError
4043
4267
  };
4044
4268
 
4045
4269
  RestStorage.prototype._errorFor = function(error, env) {
@@ -4151,7 +4375,15 @@
4151
4375
  }));
4152
4376
 
4153
4377
  LocalStorage.prototype.before('create', 'update', LocalStorage.skipIfError(function(env, next) {
4154
- env.recordAttributes = JSON.stringify(env.subject);
4378
+ var json;
4379
+ json = env.subject.toJSON();
4380
+ if (env.options.only) {
4381
+ json = this.onlyCertainAttributes(json, env.options.only);
4382
+ }
4383
+ if (env.options.except) {
4384
+ json = this.exceptCertainAttributes(json, env.options.except);
4385
+ }
4386
+ env.recordAttributes = JSON.stringify(json);
4155
4387
  return next();
4156
4388
  }));
4157
4389
 
@@ -4516,7 +4748,7 @@
4516
4748
  return {
4517
4749
  controller: resourceNameFromModel(argument.constructor),
4518
4750
  action: 'show',
4519
- id: argument.get('id')
4751
+ id: (typeof argument.toParam === "function" ? argument.toParam() : void 0) || argument.get('id')
4520
4752
  };
4521
4753
  } else {
4522
4754
  return {};
@@ -4542,7 +4774,7 @@
4542
4774
  ControllerDirectory.accessor('__app', Batman.Property.defaultAccessor);
4543
4775
 
4544
4776
  ControllerDirectory.accessor(function(key) {
4545
- return this.get("__app." + (Batman.helpers.capitalize(key)) + "Controller.sharedController");
4777
+ return this.get("__app." + (Batman.helpers.titleize(key)) + "Controller.sharedController");
4546
4778
  });
4547
4779
 
4548
4780
  return ControllerDirectory;
@@ -4689,7 +4921,9 @@
4689
4921
  for (index = _i = 0, _len = matches.length; _i < _len; index = ++_i) {
4690
4922
  match = matches[index];
4691
4923
  name = namedArguments[index];
4692
- params[name] = match;
4924
+ if (match != null) {
4925
+ params[name] = decodeURIComponent(match);
4926
+ }
4693
4927
  }
4694
4928
  return Batman.extend(params, uri.queryParams);
4695
4929
  };
@@ -4791,9 +5025,13 @@
4791
5025
 
4792
5026
  function ControllerActionRoute(templatePath, options) {
4793
5027
  this.callback = __bind(this.callback, this);
4794
- var action, controller, _ref;
5028
+ var action, controller, _ref, _ref1;
4795
5029
  if (options.signature) {
4796
- _ref = options.signature.split('#'), controller = _ref[0], action = _ref[1];
5030
+ if (Batman.typeOf(options.signature) === 'String') {
5031
+ _ref = options.signature.split('#'), controller = _ref[0], action = _ref[1];
5032
+ } else {
5033
+ _ref1 = options.signature, controller = _ref1.controller, action = _ref1.action;
5034
+ }
4797
5035
  action || (action = 'index');
4798
5036
  options.controller = controller;
4799
5037
  options.action = action;
@@ -5279,6 +5517,9 @@
5279
5517
  _results = [];
5280
5518
  for (_i = 0, _len = value.length; _i < _len; _i++) {
5281
5519
  handler = value[_i];
5520
+ if (typeof handler === 'string') {
5521
+ handler = _this[handler];
5522
+ }
5282
5523
  _results.push(handler.call(_this, error));
5283
5524
  }
5284
5525
  return _results;
@@ -5379,16 +5620,14 @@
5379
5620
  }
5380
5621
  } else {
5381
5622
  if (Batman.typeOf(url) === 'Object') {
5382
- if (!url.controller) {
5383
- url.controller = this;
5384
- }
5623
+ url.controller || (url.controller = this.get('routingKey'));
5385
5624
  }
5386
5625
  return Batman.redirect(url);
5387
5626
  }
5388
5627
  };
5389
5628
 
5390
5629
  Controller.prototype.render = function(options) {
5391
- var action, frame, view, yieldContentView, yieldName, _ref, _ref1, _ref2, _ref3;
5630
+ var action, frame, view, yieldContentView, yieldName, _ref, _ref1, _ref2, _ref3, _ref4;
5392
5631
  if (options == null) {
5393
5632
  options = {};
5394
5633
  }
@@ -5404,7 +5643,7 @@
5404
5643
  options.view = null;
5405
5644
  } else {
5406
5645
  options.viewClass || (options.viewClass = this._viewClassForAction(action));
5407
- options.source || (options.source = Batman.helpers.underscore(this.get('routingKey') + '/' + action));
5646
+ options.source || (options.source = ((_ref1 = options.viewClass) != null ? _ref1.prototype.source : void 0) || Batman.helpers.underscore(this.get('routingKey') + '/' + action));
5408
5647
  view = this.renderCache.viewForOptions(options);
5409
5648
  }
5410
5649
  if (view) {
@@ -5421,10 +5660,10 @@
5421
5660
  view.set('contentFor', yieldName);
5422
5661
  }
5423
5662
  view.set('controller', this);
5424
- if ((_ref1 = Batman.currentApp) != null) {
5425
- if ((_ref2 = _ref1.layout) != null) {
5426
- if ((_ref3 = _ref2.subviews) != null) {
5427
- _ref3.add(view);
5663
+ if ((_ref2 = Batman.currentApp) != null) {
5664
+ if ((_ref3 = _ref2.layout) != null) {
5665
+ if ((_ref4 = _ref3.subviews) != null) {
5666
+ _ref4.add(view);
5428
5667
  }
5429
5668
  }
5430
5669
  }
@@ -5526,7 +5765,7 @@
5526
5765
 
5527
5766
  Set._applySetAccessors(Set);
5528
5767
 
5529
- _ref = ['indexedBy', 'indexedByUnique', 'sortedBy', 'equality', '_indexOfItem'];
5768
+ _ref = ['indexedBy', 'indexedByUnique', 'sortedBy', 'equality', '_indexOfItem', 'mappedTo'];
5530
5769
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
5531
5770
  k = _ref[_i];
5532
5771
  Set.prototype[k] = Batman.SimpleSet.prototype[k];
@@ -5544,7 +5783,11 @@
5544
5783
  _fn(k);
5545
5784
  }
5546
5785
 
5547
- Set.prototype.toJSON = Set.prototype.toArray;
5786
+ Set.prototype.toJSON = function() {
5787
+ return this.map(function(value) {
5788
+ return (typeof value.toJSON === "function" ? value.toJSON() : void 0) || value;
5789
+ });
5790
+ };
5548
5791
 
5549
5792
  Set.prototype.add = Set.mutation(function() {
5550
5793
  var addedItems;
@@ -6005,7 +6248,7 @@
6005
6248
  predicateKeys = [];
6006
6249
  definePredicate = function(state) {
6007
6250
  var key;
6008
- key = "is" + (Batman.helpers.capitalize(state));
6251
+ key = "is" + (Batman.helpers.titleize(state));
6009
6252
  if (_this.prototype[key] != null) {
6010
6253
  return;
6011
6254
  }
@@ -6259,7 +6502,9 @@
6259
6502
  if ((matches = validatorClass.matches(optionsOrFunction))) {
6260
6503
  validators.push({
6261
6504
  keys: keys,
6262
- validator: new validatorClass(matches)
6505
+ validator: new validatorClass(matches),
6506
+ "if": optionsOrFunction["if"],
6507
+ unless: optionsOrFunction.unless
6263
6508
  });
6264
6509
  }
6265
6510
  }
@@ -6333,16 +6578,25 @@
6333
6578
  };
6334
6579
 
6335
6580
  Model.findWithOptions = function(id, options, callback) {
6336
- var record;
6581
+ var record,
6582
+ _this = this;
6337
6583
  if (options == null) {
6338
6584
  options = {};
6339
6585
  }
6340
6586
  Batman.developer.assert(callback, "Must call find with a callback!");
6341
- record = new this;
6342
- record._withoutDirtyTracking(function() {
6343
- return this.set('id', id);
6587
+ this._pending || (this._pending = {});
6588
+ record = this._loadIdentity(id) || this._pending[id];
6589
+ if (record == null) {
6590
+ record = new this;
6591
+ record._withoutDirtyTracking(function() {
6592
+ return this.set('id', id);
6593
+ });
6594
+ this._pending[id] = record;
6595
+ }
6596
+ record.loadWithOptions(options, function() {
6597
+ delete _this._pending[id];
6598
+ return callback.apply(_this, arguments);
6344
6599
  });
6345
- record.loadWithOptions(options, callback);
6346
6600
  return record;
6347
6601
  };
6348
6602
 
@@ -6400,6 +6654,10 @@
6400
6654
  return this._makeOrFindRecordFromData(json);
6401
6655
  };
6402
6656
 
6657
+ Model.createMultipleFromJSON = function(array) {
6658
+ return this._makeOrFindRecordsFromData(array);
6659
+ };
6660
+
6403
6661
  Model._loadIdentity = function(id) {
6404
6662
  return this.get('loaded.indexedByUnique.id').get(id);
6405
6663
  };
@@ -6625,10 +6883,7 @@
6625
6883
  }
6626
6884
  },
6627
6885
  set: function(key, value) {
6628
- var parsedValue, primaryKey;
6629
- if ((typeof value === "string") && (value.match(/[^0-9]/) === null) && (("" + (parsedValue = parseInt(value, 10))) === value)) {
6630
- value = parsedValue;
6631
- }
6886
+ var primaryKey;
6632
6887
  primaryKey = this.constructor.primaryKey;
6633
6888
  if (primaryKey === 'id') {
6634
6889
  this._willSet(key);
@@ -6664,22 +6919,20 @@
6664
6919
  Model.prototype.toJSON = function() {
6665
6920
  var encoders, obj,
6666
6921
  _this = this;
6667
- obj = {};
6668
6922
  encoders = this._batman.get('encoders');
6669
- if (!(!encoders || encoders.isEmpty())) {
6670
- encoders.forEach(function(key, encoder) {
6671
- var encodedVal, val;
6672
- if (encoder.encode) {
6673
- val = _this.get(key);
6674
- if (typeof val !== 'undefined') {
6675
- encodedVal = encoder.encode(val, key, obj, _this);
6676
- if (typeof encodedVal !== 'undefined') {
6677
- return obj[encoder.as] = encodedVal;
6678
- }
6679
- }
6680
- }
6681
- });
6923
+ if (!encoders || encoders.isEmpty()) {
6924
+ return {};
6682
6925
  }
6926
+ obj = {};
6927
+ encoders.forEach(function(key, encoder) {
6928
+ var encodedVal, val, _ref2;
6929
+ if (!encoder.encode || (val = _this.get(key)) === void 0) {
6930
+ return;
6931
+ }
6932
+ if ((encodedVal = encoder.encode(val, key, obj, _this)) !== void 0) {
6933
+ return obj[(_ref2 = typeof encoder.as === "function" ? encoder.as(key, val, obj, _this) : void 0) != null ? _ref2 : encoder.as] = encodedVal;
6934
+ }
6935
+ });
6683
6936
  return obj;
6684
6937
  };
6685
6938
 
@@ -6697,9 +6950,16 @@
6697
6950
  }
6698
6951
  } else {
6699
6952
  encoders.forEach(function(key, encoder) {
6700
- if (encoder.decode && typeof data[encoder.as] !== 'undefined') {
6701
- return obj[key] = encoder.decode(data[encoder.as], encoder.as, data, obj, _this);
6953
+ var as, _ref2;
6954
+ if (!encoder.decode) {
6955
+ return;
6956
+ }
6957
+ as = (_ref2 = typeof encoder.as === "function" ? encoder.as(key, data[key], obj, _this) : void 0) != null ? _ref2 : encoder.as;
6958
+ value = data[as];
6959
+ if (value === void 0 || (value === null && (_this._associationForAttribute(as) != null))) {
6960
+ return;
6702
6961
  }
6962
+ return obj[key] = encoder.decode(value, as, data, obj, _this);
6703
6963
  });
6704
6964
  }
6705
6965
  if (this.constructor.primaryKey !== 'id') {
@@ -6785,11 +7045,12 @@
6785
7045
  _ref3 = isNew ? ['create', 'create', 'created'] : ['save', 'update', 'saved'], startState = _ref3[0], storageOperation = _ref3[1], endState = _ref3[2];
6786
7046
  if (this.get('lifecycle').startTransition(startState)) {
6787
7047
  return this.validate(function(error, errors) {
6788
- var associations;
7048
+ var associations, payload;
6789
7049
  if (error || errors.length) {
6790
7050
  _this.get('lifecycle').failedValidation();
6791
7051
  return typeof callback === "function" ? callback(error || errors, _this) : void 0;
6792
7052
  }
7053
+ _this.fire('validated');
6793
7054
  associations = _this.constructor._batman.get('associations');
6794
7055
  _this._withoutDirtyTracking(function() {
6795
7056
  var _ref4,
@@ -6798,9 +7059,10 @@
6798
7059
  return association.apply(_this);
6799
7060
  }) : void 0 : void 0;
6800
7061
  });
6801
- return _this._doStorageOperation(storageOperation, {
7062
+ payload = Batman.extend({}, options, {
6802
7063
  data: options
6803
- }, function(err, record, env) {
7064
+ });
7065
+ return _this._doStorageOperation(storageOperation, payload, function(err, record, env) {
6804
7066
  if (!err) {
6805
7067
  _this.get('dirtyKeys').clear();
6806
7068
  _this.get('_dirtiedKeys').clear();
@@ -6858,7 +7120,7 @@
6858
7120
  };
6859
7121
 
6860
7122
  Model.prototype.validate = function(callback) {
6861
- var args, count, e, errors, finishedValidation, key, validator, validators, _j, _k, _len1, _len2, _ref2;
7123
+ var args, condition, count, e, errors, finishedValidation, key, validator, validators, _j, _k, _len1, _len2, _ref2;
6862
7124
  errors = this.get('errors');
6863
7125
  errors.clear();
6864
7126
  validators = this._batman.get('validators') || [];
@@ -6878,6 +7140,20 @@
6878
7140
  };
6879
7141
  for (_j = 0, _len1 = validators.length; _j < _len1; _j++) {
6880
7142
  validator = validators[_j];
7143
+ if (validator["if"]) {
7144
+ condition = typeof validator["if"] === 'string' ? this.get(validator["if"]) : validator["if"].call(this, errors, this, key);
7145
+ if (!condition) {
7146
+ finishedValidation();
7147
+ continue;
7148
+ }
7149
+ }
7150
+ if (validator.unless) {
7151
+ condition = typeof validator.unless === 'string' ? this.get(validator.unless) : validator.unless.call(this, errors, this, key);
7152
+ if (condition) {
7153
+ finishedValidation();
7154
+ continue;
7155
+ }
7156
+ }
6881
7157
  _ref2 = validator.keys;
6882
7158
  for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
6883
7159
  key = _ref2[_k];
@@ -6921,6 +7197,11 @@
6921
7197
  }
6922
7198
  };
6923
7199
 
7200
+ Model.prototype._associationForAttribute = function(attribute) {
7201
+ var _ref2;
7202
+ return (_ref2 = this.constructor._batman.get('associations')) != null ? _ref2.get(attribute) : void 0;
7203
+ };
7204
+
6924
7205
  Model.prototype._doStorageOperation = function(operation, options, callback) {
6925
7206
  var adapter,
6926
7207
  _this = this;
@@ -6948,6 +7229,74 @@
6948
7229
  Model.prototype[functionName] = Batman.Property.wrapTrackingPrevention(Model.prototype[functionName]);
6949
7230
  }
6950
7231
 
7232
+ Model.prototype.reflectOnAllAssociations = function(associationType) {
7233
+ var associations;
7234
+ associations = this.constructor._batman.get('associations');
7235
+ if (associationType != null) {
7236
+ return associations != null ? associations.getByType(associationType) : void 0;
7237
+ } else {
7238
+ return associations != null ? associations.getAll() : void 0;
7239
+ }
7240
+ };
7241
+
7242
+ Model.prototype.reflectOnAssociation = function(associationLabel) {
7243
+ var _ref3;
7244
+ return (_ref3 = this.constructor._batman.get('associations')) != null ? _ref3.getByLabel(associationLabel) : void 0;
7245
+ };
7246
+
7247
+ Model.prototype.transaction = function() {
7248
+ return this._transaction([], []);
7249
+ };
7250
+
7251
+ Model.prototype._transaction = function(visited, stack) {
7252
+ var attributes, hasManys, index, key, label, newValues, transaction, value, _k, _len2, _ref3, _ref4;
7253
+ index = visited.indexOf(this);
7254
+ if (index !== -1) {
7255
+ return stack[index];
7256
+ }
7257
+ visited.push(this);
7258
+ stack.push(transaction = new this.constructor);
7259
+ if (hasManys = this.reflectOnAllAssociations('hasMany')) {
7260
+ hasManys = hasManys.filter(function(association) {
7261
+ return association.options.includeInTransaction;
7262
+ });
7263
+ _ref3 = hasManys.mapToProperty('label');
7264
+ for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
7265
+ label = _ref3[_k];
7266
+ this.get(label);
7267
+ }
7268
+ }
7269
+ attributes = this.get('attributes').toObject();
7270
+ for (key in attributes) {
7271
+ if (!__hasProp.call(attributes, key)) continue;
7272
+ value = attributes[key];
7273
+ if (value instanceof Batman.Model && !value.isTransaction) {
7274
+ attributes[key] = value._transaction(visited, stack);
7275
+ } else if (value instanceof Batman.AssociationSet && !value.isTransaction) {
7276
+ newValues = new Batman.TransactionAssociationSet(value, visited, stack);
7277
+ attributes[key] = newValues;
7278
+ } else if (Batman.typeOf(value) === 'Object') {
7279
+ Batman.developer.warn("You're passing a mutable object (" + key + ", " + value.constructor.name + ") in a " + this.constructor.name + " transaction:", value);
7280
+ }
7281
+ }
7282
+ transaction._withoutDirtyTracking(function() {
7283
+ return transaction.updateAttributes(attributes);
7284
+ });
7285
+ transaction._batman.base = this;
7286
+ _ref4 = Batman.Transaction;
7287
+ for (key in _ref4) {
7288
+ value = _ref4[key];
7289
+ transaction[key] = value;
7290
+ }
7291
+ transaction.accessor('isTransaction', function() {
7292
+ return this.isTransaction;
7293
+ });
7294
+ transaction.accessor('base', function() {
7295
+ return this.base();
7296
+ });
7297
+ return transaction;
7298
+ };
7299
+
6951
7300
  return Model;
6952
7301
 
6953
7302
  }).call(this, Batman.Object);
@@ -6964,7 +7313,7 @@
6964
7313
  var collection, _base;
6965
7314
  Batman.initializeObject(this);
6966
7315
  collection = (_base = this._batman).associations || (_base.associations = new Batman.AssociationCurator(this));
6967
- return collection.add(new Batman["" + (Batman.helpers.capitalize(k)) + "Association"](this, label, scope));
7316
+ return collection.add(new Batman["" + (Batman.helpers.titleize(k)) + "Association"](this, label, scope));
6968
7317
  };
6969
7318
  };
6970
7319
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -7131,11 +7480,8 @@
7131
7480
  loadOptions.urlContext = this.model;
7132
7481
  }
7133
7482
  return this.association.getRelatedModel().loadWithOptions(loadOptions, function(error, loadedRecords) {
7134
- if (error) {
7135
- throw error;
7136
- }
7137
7483
  if (!loadedRecords || loadedRecords.length <= 0) {
7138
- return callback(new Error("Couldn't find related record!"), void 0);
7484
+ return callback(error || new Error("Couldn't find related record!"), void 0);
7139
7485
  } else {
7140
7486
  return callback(void 0, loadedRecords[0]);
7141
7487
  }
@@ -7170,18 +7516,12 @@
7170
7516
  };
7171
7517
 
7172
7518
  BelongsToProxy.prototype.fetchFromRemote = function(callback) {
7173
- var loadOptions,
7174
- _this = this;
7519
+ var loadOptions;
7175
7520
  loadOptions = {};
7176
7521
  if (this.association.options.url) {
7177
7522
  loadOptions.recordUrl = this.association.options.url;
7178
7523
  }
7179
- return this.association.getRelatedModel().findWithOptions(this.get('foreignValue'), loadOptions, function(error, loadedRecord) {
7180
- if (error) {
7181
- throw error;
7182
- }
7183
- return callback(void 0, loadedRecord);
7184
- });
7524
+ return this.association.getRelatedModel().findWithOptions(this.get('foreignValue'), loadOptions, callback);
7185
7525
  };
7186
7526
 
7187
7527
  return BelongsToProxy;
@@ -7212,18 +7552,12 @@
7212
7552
  };
7213
7553
 
7214
7554
  PolymorphicBelongsToProxy.prototype.fetchFromRemote = function(callback) {
7215
- var loadOptions,
7216
- _this = this;
7555
+ var loadOptions;
7217
7556
  loadOptions = {};
7218
7557
  if (this.association.options.url) {
7219
7558
  loadOptions.recordUrl = this.association.options.url;
7220
7559
  }
7221
- return this.association.getRelatedModelForType(this.get('foreignTypeValue')).findWithOptions(this.get('foreignValue'), loadOptions, function(error, loadedRecord) {
7222
- if (error) {
7223
- throw error;
7224
- }
7225
- return callback(void 0, loadedRecord);
7226
- });
7560
+ return this.association.getRelatedModelForType(this.get('foreignTypeValue')).findWithOptions(this.get('foreignValue'), loadOptions, callback);
7227
7561
  };
7228
7562
 
7229
7563
  return PolymorphicBelongsToProxy;
@@ -7599,10 +7933,24 @@
7599
7933
  }
7600
7934
  }
7601
7935
 
7602
- Request.prototype.send = function() {
7603
- return Batman.developer.error("Please source a dependency file for a request implementation");
7936
+ Request.prototype.send = function(data) {
7937
+ return Batman.developer.error("You must add a platform library to implement Batman.Request (for example, batman.jquery)");
7604
7938
  };
7605
7939
 
7940
+ Request.set('pendingRequestCount', 0);
7941
+
7942
+ Request.classAccessor('requestIsPending', function() {
7943
+ return this.get('pendingRequestCount') > 0;
7944
+ });
7945
+
7946
+ Request.prototype.on('loading', function() {
7947
+ return Batman.Request.set('pendingRequestCount', Batman.Request.get('pendingRequestCount') + 1);
7948
+ });
7949
+
7950
+ Request.prototype.on('loaded', function() {
7951
+ return Batman.Request.set('pendingRequestCount', Batman.Request.get('pendingRequestCount') - 1);
7952
+ });
7953
+
7606
7954
  return Request;
7607
7955
 
7608
7956
  })(Batman.Object);
@@ -7675,7 +8023,7 @@
7675
8023
 
7676
8024
  SetObserver.prototype._manageObserversForItem = function(item, method) {
7677
8025
  var key, _i, _len, _ref;
7678
- if (item.isObservable) {
8026
+ if (item != null ? item.isObservable : void 0) {
7679
8027
  _ref = this.observedItemKeys;
7680
8028
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
7681
8029
  key = _ref[_i];
@@ -8003,16 +8351,23 @@
8003
8351
 
8004
8352
  AssociationSet.accessor('loaded', Batman.Property.defaultAccessor);
8005
8353
 
8006
- AssociationSet.prototype.load = function(callback) {
8007
- var _this = this;
8354
+ AssociationSet.prototype.load = function(options, callback) {
8355
+ var loadOptions,
8356
+ _this = this;
8357
+ loadOptions = this._getLoadOptions();
8358
+ if (!callback) {
8359
+ callback = options;
8360
+ } else {
8361
+ loadOptions.data = Batman.extend(loadOptions.data, options);
8362
+ }
8008
8363
  if (this.foreignKeyValue == null) {
8009
8364
  return callback(void 0, this);
8010
8365
  }
8011
- return this.association.getRelatedModel().loadWithOptions(this._getLoadOptions(), function(err, records) {
8366
+ return this.association.getRelatedModel().loadWithOptions(loadOptions, function(err, records, env) {
8012
8367
  if (!err) {
8013
8368
  _this.markAsLoaded();
8014
8369
  }
8015
- return callback(err, _this);
8370
+ return callback(err, _this, env);
8016
8371
  });
8017
8372
  };
8018
8373
 
@@ -8034,6 +8389,35 @@
8034
8389
  return this.fire('loaded');
8035
8390
  };
8036
8391
 
8392
+ AssociationSet.accessor('parentRecord', function() {
8393
+ var parentClass, parentPrimaryKey, parentPrimaryKeyValue, query;
8394
+ parentClass = this.get('association.model');
8395
+ parentPrimaryKey = parentClass.get('primaryKey');
8396
+ parentPrimaryKeyValue = this.get('foreignKeyValue');
8397
+ query = {};
8398
+ query[parentPrimaryKey] = parentPrimaryKeyValue;
8399
+ return parentClass.createFromJSON(query);
8400
+ });
8401
+
8402
+ AssociationSet.prototype.build = function(attrs) {
8403
+ var associatedClass, initParams, mixedAttrs, newObj, options, scope;
8404
+ if (attrs == null) {
8405
+ attrs = {};
8406
+ }
8407
+ initParams = {};
8408
+ initParams[this.get('association.foreignKey')] = this.get('foreignKeyValue');
8409
+ options = this.get('association.options');
8410
+ if (options.inverseOf != null) {
8411
+ initParams[options.inverseOf] = this.get('parentRecord');
8412
+ }
8413
+ scope = options.namespace || Batman.currentApp;
8414
+ associatedClass = scope[options.name];
8415
+ mixedAttrs = Batman.extend(attrs, initParams);
8416
+ newObj = new associatedClass(mixedAttrs);
8417
+ this.add(newObj);
8418
+ return newObj;
8419
+ };
8420
+
8037
8421
  return AssociationSet;
8038
8422
 
8039
8423
  })(Batman.SetSort);
@@ -8074,6 +8458,107 @@
8074
8458
 
8075
8459
  }).call(this);
8076
8460
 
8461
+ (function() {
8462
+ var __hasProp = {}.hasOwnProperty,
8463
+ __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; };
8464
+
8465
+ Batman.SetMapping = (function(_super) {
8466
+ __extends(SetMapping, _super);
8467
+
8468
+ function SetMapping(base, key) {
8469
+ var initialValues,
8470
+ _this = this;
8471
+ this.base = base;
8472
+ this.key = key;
8473
+ initialValues = this.base.mapToProperty(this.key);
8474
+ this._counter = new this.constructor.PresenceCounter(initialValues);
8475
+ SetMapping.__super__.constructor.apply(this, initialValues);
8476
+ this._setObserver = new Batman.SetObserver(this.base);
8477
+ this._setObserver.observedItemKeys = [this.key];
8478
+ this._setObserver.observerForItemAndKey = function(item) {
8479
+ return function(newValue, oldValue) {
8480
+ return _this._handleItemModified(item, newValue, oldValue);
8481
+ };
8482
+ };
8483
+ this._setObserver.on('itemsWereAdded', this._handleItemsAdded.bind(this));
8484
+ this._setObserver.on('itemsWereRemoved', this._handleItemsRemoved.bind(this));
8485
+ this._setObserver.startObserving();
8486
+ }
8487
+
8488
+ SetMapping.prototype._handleItemsAdded = function(items, indexes) {
8489
+ var item, _i, _len, _results;
8490
+ _results = [];
8491
+ for (_i = 0, _len = items.length; _i < _len; _i++) {
8492
+ item = items[_i];
8493
+ _results.push(this._addValueInstance(item.get(this.key)));
8494
+ }
8495
+ return _results;
8496
+ };
8497
+
8498
+ SetMapping.prototype._handleItemsRemoved = function(items, indexes) {
8499
+ var item, _i, _len, _results;
8500
+ _results = [];
8501
+ for (_i = 0, _len = items.length; _i < _len; _i++) {
8502
+ item = items[_i];
8503
+ _results.push(this._removeValueInstance(item.get(this.key)));
8504
+ }
8505
+ return _results;
8506
+ };
8507
+
8508
+ SetMapping.prototype._handleItemModified = function(item, newValue, oldValue) {
8509
+ this._removeValueInstance(oldValue);
8510
+ return this._addValueInstance(newValue);
8511
+ };
8512
+
8513
+ SetMapping.prototype._addValueInstance = function(mappedValue) {
8514
+ this._counter.increment(mappedValue);
8515
+ return this.add(mappedValue);
8516
+ };
8517
+
8518
+ SetMapping.prototype._removeValueInstance = function(mappedValue) {
8519
+ var remaining;
8520
+ remaining = this._counter.decrement(mappedValue);
8521
+ if (remaining === 0) {
8522
+ return this.remove(mappedValue);
8523
+ }
8524
+ };
8525
+
8526
+ return SetMapping;
8527
+
8528
+ })(Batman.Set);
8529
+
8530
+ Batman.SetMapping.PresenceCounter = (function() {
8531
+ function PresenceCounter(initialValues) {
8532
+ var value, _i, _len;
8533
+ this._storage = new Batman.SimpleHash;
8534
+ for (_i = 0, _len = initialValues.length; _i < _len; _i++) {
8535
+ value = initialValues[_i];
8536
+ this.increment(value);
8537
+ }
8538
+ }
8539
+
8540
+ PresenceCounter.prototype.increment = function(value) {
8541
+ var count;
8542
+ count = this._storage.get(value);
8543
+ if (!count) {
8544
+ return this._storage.set(value, 1);
8545
+ } else {
8546
+ return this._storage.set(value, count + 1);
8547
+ }
8548
+ };
8549
+
8550
+ PresenceCounter.prototype.decrement = function(value) {
8551
+ var count;
8552
+ count = this._storage.get(value);
8553
+ return this._storage.set(value, count - 1);
8554
+ };
8555
+
8556
+ return PresenceCounter;
8557
+
8558
+ })();
8559
+
8560
+ }).call(this);
8561
+
8077
8562
  (function() {
8078
8563
  var __hasProp = {}.hasOwnProperty,
8079
8564
  __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; };
@@ -8944,6 +9429,9 @@
8944
9429
  };
8945
9430
 
8946
9431
  RouteMapBuilder.prototype.root = function(signature, options) {
9432
+ if (options == null) {
9433
+ options = {};
9434
+ }
8947
9435
  return this.route('/', signature, options);
8948
9436
  };
8949
9437
 
@@ -9018,7 +9506,10 @@
9018
9506
  klass = options.callback ? Batman.CallbackActionRoute : Batman.ControllerActionRoute;
9019
9507
  options.app = this.app;
9020
9508
  route = new klass(path, options);
9021
- return this.routeMap.addRoute(name, route);
9509
+ this.routeMap.addRoute(name, route);
9510
+ if (name === '') {
9511
+ return this.routeMap.addRoute('root', route);
9512
+ }
9022
9513
  };
9023
9514
 
9024
9515
  RouteMapBuilder.prototype._nameFromPath = function(path) {
@@ -9248,9 +9739,10 @@
9248
9739
  Association.prototype.isPolymorphic = false;
9249
9740
 
9250
9741
  Association.prototype.defaultOptions = {
9251
- saveInline: true,
9742
+ saveInline: false,
9252
9743
  autoload: true,
9253
- nestUrl: false
9744
+ nestUrl: false,
9745
+ includeInTransaction: true
9254
9746
  };
9255
9747
 
9256
9748
  function Association(model, label, options) {
@@ -9626,7 +10118,7 @@
9626
10118
  }
9627
10119
  Batman.developer["do"](function() {
9628
10120
  if ((Batman.currentApp != null) && !relatedModel) {
9629
- return Batman.developer.warn("Related model " + type + " for polymorphic association not found.");
10121
+ return Batman.developer.warn("Related model " + type + " for hasMany polymorphic association " + this.label + " not found.");
9630
10122
  }
9631
10123
  });
9632
10124
  return relatedModel;
@@ -9729,21 +10221,13 @@
9729
10221
  SingularAssociation.prototype.isSingular = true;
9730
10222
 
9731
10223
  SingularAssociation.prototype.getAccessor = function(association, model, label) {
9732
- var proxy, record, recordInAttributes,
9733
- _this = this;
10224
+ var proxy, record, recordInAttributes;
9734
10225
  if (recordInAttributes = association.getFromAttributes(this)) {
9735
10226
  return recordInAttributes;
9736
10227
  }
9737
10228
  if (association.getRelatedModel()) {
9738
10229
  proxy = this.associationProxy(association);
9739
10230
  record = false;
9740
- if (proxy._loadSetter == null) {
9741
- proxy._loadSetter = proxy.once('loaded', function(child) {
9742
- return _this._withoutDirtyTracking(function() {
9743
- return this.set(association.label, child);
9744
- });
9745
- });
9746
- }
9747
10231
  if (!Batman.Property.withoutTracking(function() {
9748
10232
  return proxy.get('loaded');
9749
10233
  })) {
@@ -9935,7 +10419,7 @@
9935
10419
  if (this.options.encodeForeignTypeKey) {
9936
10420
  this.model.encode(this.foreignTypeKey);
9937
10421
  }
9938
- this.typeIndicies = {};
10422
+ this.typeIndices = {};
9939
10423
  }
9940
10424
 
9941
10425
  PolymorphicBelongsToAssociation.prototype.getRelatedModel = false;
@@ -9990,7 +10474,7 @@
9990
10474
  }
9991
10475
  Batman.developer["do"](function() {
9992
10476
  if ((Batman.currentApp != null) && !relatedModel) {
9993
- return Batman.developer.warn("Related model " + type + " for polymorphic association not found.");
10477
+ return Batman.developer.warn("Related model " + type + " for belongsTo polymorphic association " + this.label + " not found.");
9994
10478
  }
9995
10479
  });
9996
10480
  return relatedModel;
@@ -9998,8 +10482,8 @@
9998
10482
 
9999
10483
  PolymorphicBelongsToAssociation.prototype.setIndexForType = function(type) {
10000
10484
  var _base;
10001
- (_base = this.typeIndicies)[type] || (_base[type] = new Batman.PolymorphicUniqueAssociationSetIndex(this, type, this.primaryKey));
10002
- return this.typeIndicies[type];
10485
+ (_base = this.typeIndices)[type] || (_base[type] = new Batman.PolymorphicUniqueAssociationSetIndex(this, type, this.primaryKey));
10486
+ return this.typeIndices[type];
10003
10487
  };
10004
10488
 
10005
10489
  PolymorphicBelongsToAssociation.prototype.inverseForType = function(type) {
@@ -10049,9 +10533,193 @@
10049
10533
  }).call(this);
10050
10534
 
10051
10535
  (function() {
10052
- var __hasProp = {}.hasOwnProperty,
10053
- __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; },
10054
- __slice = [].slice;
10536
+ var __hasProp = {}.hasOwnProperty;
10537
+
10538
+ Batman.Transaction = {
10539
+ isTransaction: true,
10540
+ base: function() {
10541
+ return this._batman.base;
10542
+ },
10543
+ applyChanges: function(visited) {
10544
+ var attributes, base, key, updatedAssociationSet, value, _ref;
10545
+ if (visited == null) {
10546
+ visited = [];
10547
+ }
10548
+ if (visited.indexOf(this) !== -1) {
10549
+ return this.base();
10550
+ }
10551
+ visited.push(this);
10552
+ attributes = this.get('attributes').toObject();
10553
+ for (key in attributes) {
10554
+ if (!__hasProp.call(attributes, key)) continue;
10555
+ value = attributes[key];
10556
+ if (value instanceof Batman.Model && value.isTransaction) {
10557
+ value.applyChanges(visited);
10558
+ delete attributes[key];
10559
+ } else if (value instanceof Batman.TransactionAssociationSet) {
10560
+ updatedAssociationSet = value.applyChanges(visited);
10561
+ attributes[key] = updatedAssociationSet;
10562
+ }
10563
+ }
10564
+ base = this.base();
10565
+ base.mixin(attributes);
10566
+ return (_ref = typeof base.applyChanges === "function" ? base.applyChanges() : void 0) != null ? _ref : base;
10567
+ },
10568
+ save: function(options, callback) {
10569
+ var finish, validated, _ref,
10570
+ _this = this;
10571
+ if (!callback) {
10572
+ _ref = [{}, options], options = _ref[0], callback = _ref[1];
10573
+ }
10574
+ this.once('validated', validated = function() {
10575
+ return _this.applyChanges();
10576
+ });
10577
+ finish = function() {
10578
+ _this.off('validated', validated);
10579
+ return typeof callback === "function" ? callback.apply(null, arguments) : void 0;
10580
+ };
10581
+ return this.constructor.prototype.save.call(this, options, function(err, result) {
10582
+ if (!err) {
10583
+ result = _this.base();
10584
+ result.get('dirtyKeys').clear();
10585
+ result.get('_dirtiedKeys').clear();
10586
+ result.get('lifecycle').startTransition('save');
10587
+ result.get('lifecycle').startTransition('saved');
10588
+ }
10589
+ return finish(err, result);
10590
+ });
10591
+ }
10592
+ };
10593
+
10594
+ }).call(this);
10595
+
10596
+ (function() {
10597
+ var __hasProp = {}.hasOwnProperty,
10598
+ __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; },
10599
+ __slice = [].slice;
10600
+
10601
+ Batman.TransactionAssociationSet = (function(_super) {
10602
+ __extends(TransactionAssociationSet, _super);
10603
+
10604
+ TransactionAssociationSet.prototype.isTransaction = true;
10605
+
10606
+ function TransactionAssociationSet(associationSet, visited, stack) {
10607
+ this.set('associationSet', associationSet);
10608
+ this._loader = this._addFromAssociationSet.bind(this);
10609
+ associationSet.on('itemsWereAdded', this._loader);
10610
+ this._visited = visited;
10611
+ this._stack = stack;
10612
+ this._storage = [];
10613
+ this._originalStorage = [];
10614
+ this._removedStorage = [];
10615
+ this.add.apply(this, associationSet.toArray());
10616
+ }
10617
+
10618
+ TransactionAssociationSet.delegate('association', 'foreignKeyValue', {
10619
+ to: 'associationSet'
10620
+ });
10621
+
10622
+ TransactionAssociationSet.prototype._addFromAssociationSet = function(items, indexes) {
10623
+ return this.add.apply(this, items);
10624
+ };
10625
+
10626
+ TransactionAssociationSet.prototype.add = TransactionAssociationSet.mutation(function() {
10627
+ var addedTransactions, item, items, originalIndex, removedIndex, transactionItem, _i, _len;
10628
+ items = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
10629
+ addedTransactions = [];
10630
+ for (_i = 0, _len = items.length; _i < _len; _i++) {
10631
+ item = items[_i];
10632
+ if (!(item instanceof Batman.Model && !item.isTransaction)) {
10633
+ Batman.developer.warn("You tried to add a " + item.constructor.name + " to a TransactionAssociationSet (" + (this.get('association.label')) + ")", item);
10634
+ continue;
10635
+ }
10636
+ transactionItem = item._transaction(this._visited, this._stack);
10637
+ this._storage.push(transactionItem);
10638
+ addedTransactions.push(transactionItem);
10639
+ originalIndex = this._originalStorage.indexOf(item);
10640
+ if (originalIndex === -1) {
10641
+ this._originalStorage.push(item);
10642
+ }
10643
+ removedIndex = this._removedStorage.indexOf(item);
10644
+ if (removedIndex > -1) {
10645
+ this._removedStorage.splice(removedIndex, 1);
10646
+ }
10647
+ }
10648
+ this.length = this._storage.length;
10649
+ if (addedTransactions.length) {
10650
+ this.fire('itemsWereAdded', addedTransactions);
10651
+ }
10652
+ return addedTransactions;
10653
+ });
10654
+
10655
+ TransactionAssociationSet.prototype.remove = TransactionAssociationSet.mutation(function() {
10656
+ var item, originalIndex, removedTransactions, transactionIndex, transactionItem, transactions, _i, _len;
10657
+ transactions = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
10658
+ removedTransactions = [];
10659
+ for (_i = 0, _len = transactions.length; _i < _len; _i++) {
10660
+ transactionItem = transactions[_i];
10661
+ if (!transactionItem.isTransaction) {
10662
+ throw "Tried to remove real item from transaction set: " + (t.toJSON());
10663
+ }
10664
+ transactionIndex = this._storage.indexOf(transactionItem);
10665
+ if (transactionIndex > -1) {
10666
+ this._storage.splice(transactionIndex, 1);
10667
+ removedTransactions.push(transactionItem);
10668
+ }
10669
+ item = transactionItem.base();
10670
+ originalIndex = this._originalStorage.indexOf(item);
10671
+ if (originalIndex > -1) {
10672
+ this._removedStorage.push(item);
10673
+ this._originalStorage.splice(originalIndex, 1);
10674
+ }
10675
+ }
10676
+ this.length = this._storage.length;
10677
+ if (removedTransactions.length) {
10678
+ this.fire('itemsWereRemoved', removedTransactions);
10679
+ }
10680
+ return removedTransactions;
10681
+ });
10682
+
10683
+ TransactionAssociationSet.prototype.applyChanges = function(visited) {
10684
+ var originals, target, transactionItem, _i, _len, _ref;
10685
+ _ref = this._storage;
10686
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
10687
+ transactionItem = _ref[_i];
10688
+ transactionItem.applyChanges(visited);
10689
+ }
10690
+ originals = (function(func, args, ctor) {
10691
+ ctor.prototype = func.prototype;
10692
+ var child = new ctor, result = func.apply(child, args);
10693
+ return Object(result) === result ? result : child;
10694
+ })(Batman.Set, this._originalStorage, function(){});
10695
+ target = this.get('associationSet');
10696
+ target.off('itemsWereAdded', this._loader);
10697
+ target.replace(originals);
10698
+ target.set('removedItems', (function(func, args, ctor) {
10699
+ ctor.prototype = func.prototype;
10700
+ var child = new ctor, result = func.apply(child, args);
10701
+ return Object(result) === result ? result : child;
10702
+ })(Batman.Set, this._removedStorage, function(){}));
10703
+ return target;
10704
+ };
10705
+
10706
+ TransactionAssociationSet.accessor('length', function() {
10707
+ this.registerAsMutableSource();
10708
+ return this.length;
10709
+ });
10710
+
10711
+ TransactionAssociationSet.prototype.build = Batman.AssociationSet.prototype.build;
10712
+
10713
+ return TransactionAssociationSet;
10714
+
10715
+ })(Batman.Set);
10716
+
10717
+ }).call(this);
10718
+
10719
+ (function() {
10720
+ var __hasProp = {}.hasOwnProperty,
10721
+ __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; },
10722
+ __slice = [].slice;
10055
10723
 
10056
10724
  Batman.Validator = (function(_super) {
10057
10725
  __extends(Validator, _super);
@@ -10102,12 +10770,20 @@
10102
10770
  Validator.__super__.constructor.apply(this, mixins);
10103
10771
  }
10104
10772
 
10105
- Validator.prototype.validate = function(record) {
10106
- return Batman.developer.error("You must override validate in Batman.Validator subclasses.");
10773
+ Validator.prototype.validateEach = function(record) {
10774
+ return Batman.developer.error("You must override validateEach in Batman.Validator subclasses.");
10107
10775
  };
10108
10776
 
10109
- Validator.prototype.format = function(key, messageKey, interpolations) {
10110
- return Batman.t("errors.messages." + messageKey, interpolations);
10777
+ Validator.prototype.format = function(attr, messageKey, interpolations, record) {
10778
+ if (this.options.message) {
10779
+ if (typeof this.options.message === 'function') {
10780
+ return this.options.message.call(record, attr, messageKey);
10781
+ } else {
10782
+ return this.options.message;
10783
+ }
10784
+ } else {
10785
+ return Batman.t("errors.messages." + messageKey, interpolations);
10786
+ }
10111
10787
  };
10112
10788
 
10113
10789
  Validator.prototype.handleBlank = function(value) {
@@ -10142,10 +10818,13 @@
10142
10818
  equal_to: "must be equal to %{count}",
10143
10819
  less_than: "must be less than %{count}",
10144
10820
  less_than_or_equal_to: "must be less than or equal to %{count}",
10821
+ not_an_integer: "must be an integer",
10145
10822
  not_matching: "is not valid",
10146
10823
  invalid_association: "is not valid",
10147
10824
  not_included_in_list: "is not included in the list",
10148
- included_in_list: "is included in the list"
10825
+ included_in_list: "is included in the list",
10826
+ not_an_email: "is not a valid email address",
10827
+ confirmation_does_not_match: 'and confirmation do not match'
10149
10828
  }
10150
10829
  }
10151
10830
  });
@@ -10176,7 +10855,7 @@
10176
10855
  return callback();
10177
10856
  }
10178
10857
  if ((value == null) || value === '' || !this.regexp.test(value)) {
10179
- errors.add(key, this.format(key, 'not_matching'));
10858
+ errors.add(key, this.format(key, 'not_matching', {}, record));
10180
10859
  }
10181
10860
  return callback();
10182
10861
  };
@@ -10208,7 +10887,7 @@
10208
10887
  var value;
10209
10888
  value = record.get(key);
10210
10889
  if (!this.isPresent(value)) {
10211
- errors.add(key, this.format(key, 'blank'));
10890
+ errors.add(key, this.format(key, 'blank', {}, record));
10212
10891
  }
10213
10892
  return callback();
10214
10893
  };
@@ -10238,7 +10917,7 @@
10238
10917
  return _ref;
10239
10918
  }
10240
10919
 
10241
- NumericValidator.triggers('numeric', 'greaterThan', 'greaterThanOrEqualTo', 'equalTo', 'lessThan', 'lessThanOrEqualTo');
10920
+ NumericValidator.triggers('numeric', 'greaterThan', 'greaterThanOrEqualTo', 'equalTo', 'lessThan', 'lessThanOrEqualTo', 'onlyInteger');
10242
10921
 
10243
10922
  NumericValidator.options('allowBlank');
10244
10923
 
@@ -10250,32 +10929,34 @@
10250
10929
  return callback();
10251
10930
  }
10252
10931
  if ((value == null) || !(this.isNumeric(value) || this.canCoerceToNumeric(value))) {
10253
- errors.add(key, this.format(key, 'not_numeric'));
10932
+ errors.add(key, this.format(key, 'not_numeric', {}, record));
10933
+ } else if (options.onlyInteger && !this.isInteger(value)) {
10934
+ errors.add(key, this.format(key, 'not_an_integer', {}, record));
10254
10935
  } else {
10255
10936
  if ((options.greaterThan != null) && value <= options.greaterThan) {
10256
10937
  errors.add(key, this.format(key, 'greater_than', {
10257
10938
  count: options.greaterThan
10258
- }));
10939
+ }, record));
10259
10940
  }
10260
10941
  if ((options.greaterThanOrEqualTo != null) && value < options.greaterThanOrEqualTo) {
10261
10942
  errors.add(key, this.format(key, 'greater_than_or_equal_to', {
10262
10943
  count: options.greaterThanOrEqualTo
10263
- }));
10944
+ }, record));
10264
10945
  }
10265
10946
  if ((options.equalTo != null) && value !== options.equalTo) {
10266
10947
  errors.add(key, this.format(key, 'equal_to', {
10267
10948
  count: options.equalTo
10268
- }));
10949
+ }, record));
10269
10950
  }
10270
10951
  if ((options.lessThan != null) && value >= options.lessThan) {
10271
10952
  errors.add(key, this.format(key, 'less_than', {
10272
10953
  count: options.lessThan
10273
- }));
10954
+ }, record));
10274
10955
  }
10275
10956
  if ((options.lessThanOrEqualTo != null) && value > options.lessThanOrEqualTo) {
10276
10957
  errors.add(key, this.format(key, 'less_than_or_equal_to', {
10277
10958
  count: options.lessThanOrEqualTo
10278
- }));
10959
+ }, record));
10279
10960
  }
10280
10961
  }
10281
10962
  return callback();
@@ -10285,6 +10966,10 @@
10285
10966
  return !isNaN(parseFloat(value)) && isFinite(value);
10286
10967
  };
10287
10968
 
10969
+ NumericValidator.prototype.isInteger = function(value) {
10970
+ return parseFloat(value) === (value | 0);
10971
+ };
10972
+
10288
10973
  NumericValidator.prototype.canCoerceToNumeric = function(value) {
10289
10974
  return (value - 0) == value && value.length > 0;
10290
10975
  };
@@ -10323,7 +11008,7 @@
10323
11008
  var options, value;
10324
11009
  options = this.options;
10325
11010
  value = record.get(key);
10326
- if (value !== '' && this.handleBlank(value)) {
11011
+ if (this.handleBlank(value)) {
10327
11012
  return callback();
10328
11013
  }
10329
11014
  if (value == null) {
@@ -10332,17 +11017,17 @@
10332
11017
  if (options.minLength && value.length < options.minLength) {
10333
11018
  errors.add(key, this.format(key, 'too_short', {
10334
11019
  count: options.minLength
10335
- }));
11020
+ }, record));
10336
11021
  }
10337
11022
  if (options.maxLength && value.length > options.maxLength) {
10338
11023
  errors.add(key, this.format(key, 'too_long', {
10339
11024
  count: options.maxLength
10340
- }));
11025
+ }, record));
10341
11026
  }
10342
11027
  if (options.length && value.length !== options.length) {
10343
11028
  errors.add(key, this.format(key, 'wrong_length', {
10344
11029
  count: options.length
10345
- }));
11030
+ }, record));
10346
11031
  }
10347
11032
  return callback();
10348
11033
  };
@@ -10364,14 +11049,18 @@
10364
11049
 
10365
11050
  InclusionValidator.triggers('inclusion');
10366
11051
 
11052
+ InclusionValidator.options('allowBlank');
11053
+
10367
11054
  function InclusionValidator(options) {
10368
11055
  this.acceptableValues = options.inclusion["in"];
10369
11056
  InclusionValidator.__super__.constructor.apply(this, arguments);
10370
11057
  }
10371
11058
 
10372
11059
  InclusionValidator.prototype.validateEach = function(errors, record, key, callback) {
10373
- if (this.acceptableValues.indexOf(record.get(key)) === -1) {
10374
- errors.add(key, this.format(key, 'not_included_in_list'));
11060
+ var value;
11061
+ value = record.get(key);
11062
+ if (!this.handleBlank(value) && this.acceptableValues.indexOf(value) === -1) {
11063
+ errors.add(key, this.format(key, 'not_included_in_list', {}, record));
10375
11064
  }
10376
11065
  return callback();
10377
11066
  };
@@ -10400,7 +11089,7 @@
10400
11089
 
10401
11090
  ExclusionValidator.prototype.validateEach = function(errors, record, key, callback) {
10402
11091
  if (this.unacceptableValues.indexOf(record.get(key)) >= 0) {
10403
- errors.add(key, this.format(key, 'included_in_list'));
11092
+ errors.add(key, this.format(key, 'included_in_list', {}, record));
10404
11093
  }
10405
11094
  return callback();
10406
11095
  };
@@ -10413,6 +11102,82 @@
10413
11102
 
10414
11103
  }).call(this);
10415
11104
 
11105
+ (function() {
11106
+ var _ref,
11107
+ __hasProp = {}.hasOwnProperty,
11108
+ __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; };
11109
+
11110
+ Batman.EmailValidator = (function(_super) {
11111
+ __extends(EmailValidator, _super);
11112
+
11113
+ function EmailValidator() {
11114
+ _ref = EmailValidator.__super__.constructor.apply(this, arguments);
11115
+ return _ref;
11116
+ }
11117
+
11118
+ EmailValidator.triggers('email');
11119
+
11120
+ EmailValidator.prototype.emailRegexp = /[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*/;
11121
+
11122
+ EmailValidator.prototype.validateEach = function(errors, record, key, callback) {
11123
+ var value;
11124
+ value = record.get(key);
11125
+ if ((value == null) || value === '' || !this.emailRegexp.test(value)) {
11126
+ errors.add(key, this.format(key, 'not_an_email', {}, record));
11127
+ }
11128
+ return callback();
11129
+ };
11130
+
11131
+ return EmailValidator;
11132
+
11133
+ })(Batman.Validator);
11134
+
11135
+ Batman.Validators.push(Batman.EmailValidator);
11136
+
11137
+ }).call(this);
11138
+
11139
+ (function() {
11140
+ var _ref,
11141
+ __hasProp = {}.hasOwnProperty,
11142
+ __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; };
11143
+
11144
+ Batman.ConfirmationValidator = (function(_super) {
11145
+ __extends(ConfirmationValidator, _super);
11146
+
11147
+ function ConfirmationValidator() {
11148
+ _ref = ConfirmationValidator.__super__.constructor.apply(this, arguments);
11149
+ return _ref;
11150
+ }
11151
+
11152
+ ConfirmationValidator.triggers('confirmation');
11153
+
11154
+ ConfirmationValidator.prototype.validateEach = function(errors, record, key, callback) {
11155
+ var confirmation_key, confirmation_value, options, value;
11156
+ options = this.options;
11157
+ if (!options.confirmation) {
11158
+ return;
11159
+ }
11160
+ if (Batman.typeOf(this.options.confirmation) === "String") {
11161
+ confirmation_key = this.options.confirmation;
11162
+ } else {
11163
+ confirmation_key = key + "_confirmation";
11164
+ }
11165
+ value = record.get(key);
11166
+ confirmation_value = record.get(confirmation_key);
11167
+ if (value !== confirmation_value) {
11168
+ errors.add(key, this.format(key, 'confirmation_does_not_match', {}, record));
11169
+ }
11170
+ return callback();
11171
+ };
11172
+
11173
+ return ConfirmationValidator;
11174
+
11175
+ })(Batman.Validator);
11176
+
11177
+ Batman.Validators.push(Batman.ConfirmationValidator);
11178
+
11179
+ }).call(this);
11180
+
10416
11181
  (function() {
10417
11182
  var _ref,
10418
11183
  __hasProp = {}.hasOwnProperty,
@@ -10439,7 +11204,7 @@
10439
11204
  count = 1;
10440
11205
  childFinished = function(err, childErrors) {
10441
11206
  if (childErrors.length > 0) {
10442
- errors.add(key, _this.format(key, 'invalid_association'));
11207
+ errors.add(key, _this.format(key, 'invalid_association', {}, record));
10443
11208
  }
10444
11209
  if (--count === 0) {
10445
11210
  return callback();
@@ -10468,6 +11233,63 @@
10468
11233
 
10469
11234
  }).call(this);
10470
11235
 
11236
+ (function() {
11237
+ var _ref,
11238
+ __hasProp = {}.hasOwnProperty,
11239
+ __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; };
11240
+
11241
+ Batman.AssociatedFieldValidator = (function(_super) {
11242
+ __extends(AssociatedFieldValidator, _super);
11243
+
11244
+ function AssociatedFieldValidator() {
11245
+ _ref = AssociatedFieldValidator.__super__.constructor.apply(this, arguments);
11246
+ return _ref;
11247
+ }
11248
+
11249
+ AssociatedFieldValidator.triggers('associatedFields');
11250
+
11251
+ AssociatedFieldValidator.prototype.validateEach = function(errors, record, key, callback) {
11252
+ var childFinished, count, value,
11253
+ _this = this;
11254
+ value = record.get(key);
11255
+ if (value != null) {
11256
+ if (value instanceof Batman.AssociationProxy) {
11257
+ value = typeof value.get === "function" ? value.get('target') : void 0;
11258
+ }
11259
+ count = 1;
11260
+ childFinished = function(err, childErrors) {
11261
+ if (childErrors != null) {
11262
+ childErrors.forEach(function(validationError) {
11263
+ return errors.add(validationError.get('attribute'), validationError.get('message'));
11264
+ });
11265
+ }
11266
+ if (--count === 0) {
11267
+ return callback();
11268
+ }
11269
+ };
11270
+ if ((value != null ? value.forEach : void 0) != null) {
11271
+ value.forEach(function(record) {
11272
+ count += 1;
11273
+ return record.validate(childFinished);
11274
+ });
11275
+ } else if ((value != null ? value.validate : void 0) != null) {
11276
+ count += 1;
11277
+ value.validate(childFinished);
11278
+ }
11279
+ return childFinished(null, []);
11280
+ } else {
11281
+ return callback();
11282
+ }
11283
+ };
11284
+
11285
+ return AssociatedFieldValidator;
11286
+
11287
+ })(Batman.Validator);
11288
+
11289
+ Batman.Validators.push(Batman.AssociatedFieldValidator);
11290
+
11291
+ }).call(this);
11292
+
10471
11293
  (function() {
10472
11294
  var __hasProp = {}.hasOwnProperty,
10473
11295
  __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; };
@@ -10629,6 +11451,14 @@
10629
11451
  return this._batman.set('options', keys);
10630
11452
  };
10631
11453
 
11454
+ View.filter = function(key, filter) {
11455
+ var filters;
11456
+ Batman.initializeObject(this.prototype);
11457
+ filters = this.prototype._batman.filters || {};
11458
+ filters[key] = filter;
11459
+ return this.prototype._batman.set('filters', filters);
11460
+ };
11461
+
10632
11462
  View.viewForNode = function(node, climbTree) {
10633
11463
  var view;
10634
11464
  if (climbTree == null) {
@@ -10702,16 +11532,19 @@
10702
11532
  };
10703
11533
 
10704
11534
  View.prototype._addSubview = function(subview) {
10705
- var subviewController, yieldName, yieldObject;
11535
+ var filters, subviewController, yieldName, yieldObject;
10706
11536
  subviewController = subview.controller;
10707
11537
  subview.removeFromSuperview();
10708
11538
  subview.set('controller', subviewController || this.controller);
10709
11539
  subview.set('superview', this);
10710
- subview.fire('viewDidMoveToSuperview');
11540
+ subview.fireAndCall('viewDidMoveToSuperview');
10711
11541
  if ((yieldName = subview.contentFor) && !subview.parentNode) {
10712
11542
  yieldObject = Batman.DOM.Yield.withName(yieldName);
10713
11543
  yieldObject.set('contentView', subview);
10714
11544
  }
11545
+ if (filters = this._batman.get('filters')) {
11546
+ subview._batman.set('filters', Batman.mixin({}, filters, subview._batman.get('filters')));
11547
+ }
10715
11548
  this.get('node');
10716
11549
  subview.get('node');
10717
11550
  this.observe('node', subview._nodesChanged);
@@ -10725,7 +11558,7 @@
10725
11558
  if (!this.superview) {
10726
11559
  return;
10727
11560
  }
10728
- this.fire('viewWillRemoveFromSuperview');
11561
+ this.fireAndCall('viewWillRemoveFromSuperview');
10729
11562
  this.forget('node', this._nodesChanged);
10730
11563
  this.forget('parentNode', this._nodesChanged);
10731
11564
  this.superview.forget('node', this._nodesChanged);
@@ -10806,10 +11639,7 @@
10806
11639
  if (value != null) {
10807
11640
  this.set(eventName, value);
10808
11641
  } else {
10809
- this.fire(eventName);
10810
- if (typeof this[eventName] === "function") {
10811
- this[eventName]();
10812
- }
11642
+ this.fireAndCall(eventName);
10813
11643
  }
10814
11644
  _ref = this.subviews._storage;
10815
11645
  _results = [];
@@ -10874,7 +11704,7 @@
10874
11704
  if (node) {
10875
11705
  this.set('node', node);
10876
11706
  }
10877
- this.fire('viewDidLoad');
11707
+ this.fireAndCall('viewDidLoad');
10878
11708
  }
10879
11709
  return this.node;
10880
11710
  },
@@ -10910,8 +11740,7 @@
10910
11740
  }
10911
11741
  new Batman.BindingParser(this);
10912
11742
  this.set('isBound', true);
10913
- this.fire('ready');
10914
- return typeof this.ready === "function" ? this.ready() : void 0;
11743
+ return this.fireAndCall('ready');
10915
11744
  };
10916
11745
 
10917
11746
  View.prototype.destroyBindings = function() {
@@ -10945,11 +11774,14 @@
10945
11774
  Batman.developer.warn("Tried to die() a view more than once.");
10946
11775
  return;
10947
11776
  }
10948
- this.fire('destroy');
11777
+ this.fireAndCall('destroy');
11778
+ this.destroyBindings();
11779
+ this.destroySubviews();
10949
11780
  if (this.node) {
10950
11781
  this.wasInDOM = Batman.DOM.containsNode(this.node);
10951
11782
  Batman.DOM.destroyNode(this.node);
10952
11783
  }
11784
+ this.removeFromSuperview();
10953
11785
  this.forget();
10954
11786
  if ((_ref = this._batman.properties) != null) {
10955
11787
  _ref.forEach(function(key, property) {
@@ -10963,9 +11795,6 @@
10963
11795
  event.clearHandlers();
10964
11796
  }
10965
11797
  }
10966
- this.destroyBindings();
10967
- this.destroySubviews();
10968
- this.removeFromSuperview();
10969
11798
  this.node = null;
10970
11799
  this.parentNode = null;
10971
11800
  this.subviews = null;
@@ -10987,22 +11816,19 @@
10987
11816
  };
10988
11817
 
10989
11818
  View.prototype.targetForKeypath = function(keypath, forceTarget) {
10990
- var controller, lookupNode, nearestNonBackingView, proxiedObject;
10991
- proxiedObject = this.get('proxiedObject');
10992
- lookupNode = proxiedObject || this;
11819
+ var controller, lookupNode, nearestNonBackingView, target;
11820
+ lookupNode = this;
10993
11821
  while (lookupNode) {
10994
- if (typeof Batman.get(lookupNode, keypath) !== 'undefined') {
10995
- return lookupNode;
11822
+ if (target = this._testTargetForKeypath(lookupNode, keypath)) {
11823
+ return target;
10996
11824
  }
10997
11825
  if (forceTarget && !nearestNonBackingView && !lookupNode.isBackingView) {
10998
- nearestNonBackingView = lookupNode;
11826
+ nearestNonBackingView = Batman.get(lookupNode, 'proxiedObject') || lookupNode;
10999
11827
  }
11000
11828
  if (!controller && lookupNode.isView && lookupNode.controller) {
11001
11829
  controller = lookupNode.controller;
11002
11830
  }
11003
- if (proxiedObject && lookupNode === proxiedObject) {
11004
- lookupNode = this;
11005
- } else if (lookupNode.isView && lookupNode.superview) {
11831
+ if (lookupNode.isView && lookupNode.superview) {
11006
11832
  lookupNode = lookupNode.superview;
11007
11833
  } else if (controller) {
11008
11834
  lookupNode = controller;
@@ -11022,6 +11848,18 @@
11022
11848
  return nearestNonBackingView;
11023
11849
  };
11024
11850
 
11851
+ View.prototype._testTargetForKeypath = function(object, keypath) {
11852
+ var proxiedObject;
11853
+ if (proxiedObject = Batman.get(object, 'proxiedObject')) {
11854
+ if (typeof Batman.get(proxiedObject, keypath) !== 'undefined') {
11855
+ return proxiedObject;
11856
+ }
11857
+ }
11858
+ if (typeof Batman.get(object, keypath) !== 'undefined') {
11859
+ return object;
11860
+ }
11861
+ };
11862
+
11025
11863
  View.prototype.lookupKeypath = function(keypath) {
11026
11864
  var base, target;
11027
11865
  base = this.baseForKeypath(keypath);
@@ -11041,6 +11879,11 @@
11041
11879
  return (_ref = Batman.Property.forBaseAndKey(target, keypath)) != null ? _ref.setValue(value) : void 0;
11042
11880
  };
11043
11881
 
11882
+ View.prototype.fireAndCall = function(key) {
11883
+ this.fire(key);
11884
+ return typeof this[key] === "function" ? this[key]() : void 0;
11885
+ };
11886
+
11044
11887
  return View;
11045
11888
 
11046
11889
  })(Batman.Object);
@@ -11247,7 +12090,7 @@
11247
12090
  };
11248
12091
 
11249
12092
  AbstractBinding.prototype.parseFilter = function() {
11250
- var args, e, filter, filterName, filterString, filters, key, keyPath, orig, split;
12093
+ var args, e, filter, filterName, filterString, filters, key, keyPath, orig, split, _ref, _ref1;
11251
12094
  this.filterFunctions = [];
11252
12095
  this.filterArguments = [];
11253
12096
  keyPath = this.keyPath;
@@ -11275,7 +12118,7 @@
11275
12118
  }
11276
12119
  filterName = filterString.substr(0, split);
11277
12120
  args = filterString.substr(split);
11278
- if (!(filter = Batman.Filters[filterName])) {
12121
+ if (!(filter = ((_ref = this.view) != null ? (_ref1 = _ref._batman.get('filters')) != null ? _ref1[filterName] : void 0 : void 0) || Batman.Filters[filterName])) {
11279
12122
  return Batman.developer.error("Unrecognized filter '" + filterName + "' in key \"" + this.keyPath + "\"!");
11280
12123
  }
11281
12124
  this.filterFunctions.push(filter);
@@ -11433,7 +12276,7 @@
11433
12276
  if (_this.isDataChanging) {
11434
12277
  return;
11435
12278
  }
11436
- return _this.view.set(_this.keyPath, value);
12279
+ return _this.view.setKeypath(_this.keyPath, value);
11437
12280
  });
11438
12281
  }
11439
12282
 
@@ -11509,16 +12352,16 @@
11509
12352
  view = Batman.View.viewForNode(this.node, false);
11510
12353
  if (!!value === !this.invert) {
11511
12354
  if (view != null) {
11512
- view.fire('viewWillShow');
12355
+ view.fireAndCall('viewWillShow');
11513
12356
  }
11514
12357
  this.node.style.display = this.originalDisplay;
11515
- return view != null ? view.fire('viewDidShow') : void 0;
12358
+ return view != null ? view.fireAndCall('viewDidShow') : void 0;
11516
12359
  } else {
11517
12360
  if (view != null) {
11518
- view.fire('viewWillHide');
12361
+ view.fireAndCall('viewWillHide');
11519
12362
  }
11520
12363
  Batman.DOM.setStyleProperty(this.node, 'display', 'none', 'important');
11521
- return view != null ? view.fire('viewDidHide') : void 0;
12364
+ return view != null ? view.fireAndCall('viewDidHide') : void 0;
11522
12365
  }
11523
12366
  };
11524
12367
 
@@ -11692,20 +12535,13 @@
11692
12535
  }).call(this);
11693
12536
 
11694
12537
  (function() {
11695
- var _ref,
11696
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
12538
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
11697
12539
  __hasProp = {}.hasOwnProperty,
11698
12540
  __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; };
11699
12541
 
11700
12542
  Batman.DOM.RouteBinding = (function(_super) {
11701
12543
  __extends(RouteBinding, _super);
11702
12544
 
11703
- function RouteBinding() {
11704
- this.routeClick = __bind(this.routeClick, this);
11705
- _ref = RouteBinding.__super__.constructor.apply(this, arguments);
11706
- return _ref;
11707
- }
11708
-
11709
12545
  RouteBinding.prototype.onAnchorTag = false;
11710
12546
 
11711
12547
  RouteBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.Data;
@@ -11714,34 +12550,44 @@
11714
12550
  return this.view.lookupKeypath('dispatcher') || Batman.App.get('current.dispatcher');
11715
12551
  });
11716
12552
 
12553
+ function RouteBinding() {
12554
+ this.routeClick = __bind(this.routeClick, this);
12555
+ var definition, paramKeypath;
12556
+ RouteBinding.__super__.constructor.apply(this, arguments);
12557
+ if (paramKeypath = this.node.getAttribute('data-route-params')) {
12558
+ definition = new Batman.DOM.ReaderBindingDefinition(this.node, paramKeypath, this.view);
12559
+ this.set('queryParams', new Batman.DOM.RouteParamsBinding(definition, this));
12560
+ }
12561
+ }
12562
+
11717
12563
  RouteBinding.prototype.bind = function() {
11718
- var _ref1;
11719
- if ((_ref1 = this.node.nodeName) === 'a' || _ref1 === 'A') {
12564
+ var _ref;
12565
+ if ((_ref = this.node.nodeName) === 'a' || _ref === 'A') {
11720
12566
  this.onAnchorTag = true;
11721
12567
  }
11722
12568
  RouteBinding.__super__.bind.apply(this, arguments);
11723
12569
  if (this.onAnchorTag && this.node.getAttribute('target')) {
11724
12570
  return;
11725
12571
  }
11726
- return Batman.DOM.events.click(this.node, this.routeClick);
12572
+ return Batman.DOM.events.primaryInteractionEvent(this.node, this.routeClick);
11727
12573
  };
11728
12574
 
11729
12575
  RouteBinding.prototype.routeClick = function(node, event) {
11730
- var params;
12576
+ var path;
11731
12577
  if (event.__batmanActionTaken) {
11732
12578
  return;
11733
12579
  }
11734
12580
  event.__batmanActionTaken = true;
11735
- params = this.pathFromValue(this.get('filteredValue'));
11736
- if (params != null) {
11737
- return Batman.redirect(params);
12581
+ path = this.generatePath(this.get('filteredValue'), this.get('queryParams.filteredValue'));
12582
+ if (path != null) {
12583
+ return Batman.redirect(path);
11738
12584
  }
11739
12585
  };
11740
12586
 
11741
- RouteBinding.prototype.dataChange = function(value) {
12587
+ RouteBinding.prototype.dataChange = function(value, node, queryParams) {
11742
12588
  var path;
11743
12589
  if (value) {
11744
- path = this.pathFromValue(value);
12590
+ path = this.generatePath(value, queryParams || this.get('queryParams.filteredValue'));
11745
12591
  }
11746
12592
  if (this.onAnchorTag) {
11747
12593
  if (path && Batman.navigator) {
@@ -11753,21 +12599,46 @@
11753
12599
  }
11754
12600
  };
11755
12601
 
11756
- RouteBinding.prototype.pathFromValue = function(value) {
11757
- var _ref1;
11758
- if (value) {
11759
- if (value.isNamedRouteQuery) {
11760
- return value.get('path');
11761
- } else {
11762
- return (_ref1 = this.get('dispatcher')) != null ? _ref1.pathFromParams(value) : void 0;
12602
+ RouteBinding.prototype.generatePath = function(value, params) {
12603
+ var path, uri, _ref;
12604
+ path = (value != null ? value.isNamedRouteQuery : void 0) ? value.get('path') : (_ref = this.get('dispatcher')) != null ? _ref.pathFromParams(value) : void 0;
12605
+ if (!params || !path) {
12606
+ return path;
12607
+ }
12608
+ if (Batman.typeOf(params) === 'Object') {
12609
+ if (params.toObject != null) {
12610
+ params = params.toObject();
11763
12611
  }
12612
+ } else {
12613
+ params = Batman.URI.paramsFromQuery(params);
11764
12614
  }
12615
+ uri = new Batman.URI(path);
12616
+ uri.queryParams = params;
12617
+ return uri.toString();
11765
12618
  };
11766
12619
 
11767
12620
  return RouteBinding;
11768
12621
 
11769
12622
  })(Batman.DOM.AbstractBinding);
11770
12623
 
12624
+ Batman.DOM.RouteParamsBinding = (function(_super) {
12625
+ __extends(RouteParamsBinding, _super);
12626
+
12627
+ RouteParamsBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.Data;
12628
+
12629
+ function RouteParamsBinding(definition, routeBinding) {
12630
+ this.routeBinding = routeBinding;
12631
+ RouteParamsBinding.__super__.constructor.call(this, definition);
12632
+ }
12633
+
12634
+ RouteParamsBinding.prototype.dataChange = function(value) {
12635
+ return this.routeBinding.dataChange(this.routeBinding.get('filteredValue'), this.node, value);
12636
+ };
12637
+
12638
+ return RouteParamsBinding;
12639
+
12640
+ })(Batman.DOM.AbstractBinding);
12641
+
11771
12642
  }).call(this);
11772
12643
 
11773
12644
  (function() {
@@ -11846,7 +12717,7 @@
11846
12717
  }).call(this);
11847
12718
 
11848
12719
  (function() {
11849
- var _ref, _ref1,
12720
+ var _ref,
11850
12721
  __hasProp = {}.hasOwnProperty,
11851
12722
  __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; };
11852
12723
 
@@ -11867,20 +12738,21 @@
11867
12738
  Batman.DOM.DeferredRenderBinding = (function(_super) {
11868
12739
  __extends(DeferredRenderBinding, _super);
11869
12740
 
11870
- function DeferredRenderBinding() {
11871
- _ref1 = DeferredRenderBinding.__super__.constructor.apply(this, arguments);
11872
- return _ref1;
11873
- }
11874
-
11875
12741
  DeferredRenderBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.Data;
11876
12742
 
11877
12743
  DeferredRenderBinding.prototype.backWithView = Batman.DeferredRenderView;
11878
12744
 
11879
12745
  DeferredRenderBinding.prototype.skipChildren = true;
11880
12746
 
12747
+ function DeferredRenderBinding(definition) {
12748
+ this.invert = definition.invert;
12749
+ this.attributeName = this.invert ? 'data-deferif' : 'data-renderif';
12750
+ DeferredRenderBinding.__super__.constructor.apply(this, arguments);
12751
+ }
12752
+
11881
12753
  DeferredRenderBinding.prototype.dataChange = function(value) {
11882
- if (value && !this.backingView.isBound) {
11883
- this.node.removeAttribute('data-renderif');
12754
+ if (!!value === !this.invert && !this.backingView.isBound) {
12755
+ this.node.removeAttribute(this.attributeName);
11884
12756
  return this.backingView.initializeBindings();
11885
12757
  }
11886
12758
  };
@@ -11937,22 +12809,79 @@
11937
12809
  var __hasProp = {}.hasOwnProperty,
11938
12810
  __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; };
11939
12811
 
11940
- Batman.DOM.EventBinding = (function(_super) {
11941
- __extends(EventBinding, _super);
12812
+ Batman.Tracking = {
12813
+ loadTracker: function() {
12814
+ if (Batman.Tracking.tracker) {
12815
+ return Batman.Tracking.tracker;
12816
+ }
12817
+ Batman.Tracking.tracker = Batman.currentApp.EventTracker ? new Batman.currentApp.EventTracker : (Batman.developer.warn("Define " + Batman.currentApp.name + ".EventTracker to use data-track"), {
12818
+ track: function() {}
12819
+ });
12820
+ return Batman.Tracking.tracker;
12821
+ },
12822
+ trackEvent: function(type, data, node) {
12823
+ return Batman.Tracking.loadTracker().track(type, data, node);
12824
+ }
12825
+ };
11942
12826
 
11943
- EventBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.Data;
12827
+ Batman.DOM.ClickTrackingBinding = (function(_super) {
12828
+ __extends(ClickTrackingBinding, _super);
11944
12829
 
11945
- EventBinding.prototype.bindImmediately = false;
12830
+ ClickTrackingBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.None;
11946
12831
 
11947
- function EventBinding() {
11948
- var attacher, callback,
12832
+ ClickTrackingBinding.prototype.bindImmediately = false;
12833
+
12834
+ function ClickTrackingBinding() {
12835
+ var callback,
11949
12836
  _this = this;
11950
- EventBinding.__super__.constructor.apply(this, arguments);
12837
+ ClickTrackingBinding.__super__.constructor.apply(this, arguments);
11951
12838
  callback = function() {
11952
- var func, target;
11953
- func = _this.get('filteredValue');
11954
- target = _this.view.targetForKeypath(_this.functionPath || _this.unfilteredKey);
11955
- if (target && _this.functionPath) {
12839
+ return Batman.Tracking.trackEvent('click', _this.get('filteredValue'), _this.node);
12840
+ };
12841
+ Batman.DOM.events.click(this.node, callback, this.view, 'click', false);
12842
+ this.bind();
12843
+ }
12844
+
12845
+ return ClickTrackingBinding;
12846
+
12847
+ })(Batman.DOM.AbstractAttributeBinding);
12848
+
12849
+ Batman.DOM.ViewTrackingBinding = (function(_super) {
12850
+ __extends(ViewTrackingBinding, _super);
12851
+
12852
+ ViewTrackingBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.None;
12853
+
12854
+ function ViewTrackingBinding() {
12855
+ ViewTrackingBinding.__super__.constructor.apply(this, arguments);
12856
+ Batman.Tracking.trackEvent('view', this.get('filteredValue'), this.node);
12857
+ }
12858
+
12859
+ return ViewTrackingBinding;
12860
+
12861
+ })(Batman.DOM.AbstractAttributeBinding);
12862
+
12863
+ }).call(this);
12864
+
12865
+ (function() {
12866
+ var __hasProp = {}.hasOwnProperty,
12867
+ __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; };
12868
+
12869
+ Batman.DOM.EventBinding = (function(_super) {
12870
+ __extends(EventBinding, _super);
12871
+
12872
+ EventBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.None;
12873
+
12874
+ EventBinding.prototype.bindImmediately = false;
12875
+
12876
+ function EventBinding() {
12877
+ var attacher, callback,
12878
+ _this = this;
12879
+ EventBinding.__super__.constructor.apply(this, arguments);
12880
+ callback = function() {
12881
+ var func, target;
12882
+ func = _this.get('filteredValue');
12883
+ target = _this.view.targetForKeypath(_this.functionPath || _this.unfilteredKey);
12884
+ if (target && _this.functionPath) {
11956
12885
  target = Batman.get(target, _this.functionPath);
11957
12886
  }
11958
12887
  return func != null ? func.apply(target, arguments) : void 0;
@@ -11962,7 +12891,7 @@
11962
12891
  } else {
11963
12892
  Batman.DOM.events.other(this.node, this.attributeName, callback, this.view);
11964
12893
  }
11965
- this.view.bindings.push(this);
12894
+ this.bind();
11966
12895
  }
11967
12896
 
11968
12897
  EventBinding.prototype._unfilteredValue = function(key) {
@@ -11999,20 +12928,31 @@
11999
12928
 
12000
12929
  ContextBinding.prototype.bindingName = 'context';
12001
12930
 
12002
- function ContextBinding() {
12003
- var contextAttribute;
12931
+ function ContextBinding(definition) {
12932
+ var contextAttribute,
12933
+ _this = this;
12934
+ this.contextKeypath = definition.attr || 'proxiedObject';
12004
12935
  ContextBinding.__super__.constructor.apply(this, arguments);
12005
12936
  contextAttribute = this.attributeName ? "data-" + this.bindingName + "-" + this.attributeName : "data-" + this.bindingName;
12006
12937
  this.node.removeAttribute(contextAttribute);
12007
12938
  this.node.insertBefore(document.createComment("batman-" + contextAttribute + "=\"" + this.keyPath + "\""), this.node.firstChild);
12939
+ this.backingView.observe(this.contextKeypath, this._updateValue = function(value) {
12940
+ if (_this.isDataChanging) {
12941
+ return;
12942
+ }
12943
+ return _this.view.setKeypath(_this.keyPath, value);
12944
+ });
12008
12945
  }
12009
12946
 
12010
12947
  ContextBinding.prototype.dataChange = function(proxiedObject) {
12011
- return this.backingView.set(this.attributeName || 'proxiedObject', proxiedObject);
12948
+ this.isDataChanging = true;
12949
+ this.backingView.set(this.contextKeypath, proxiedObject);
12950
+ return this.isDataChanging = false;
12012
12951
  };
12013
12952
 
12014
12953
  ContextBinding.prototype.die = function() {
12015
- this.backingView.unset(this.attributeName || 'proxiedObject');
12954
+ this.backingView.forget(this.contextKeypath, this._updateValue);
12955
+ this.backingView.unset(this.contextKeypath);
12016
12956
  return ContextBinding.__super__.die.apply(this, arguments);
12017
12957
  };
12018
12958
 
@@ -12250,6 +13190,22 @@
12250
13190
  return _ref;
12251
13191
  }
12252
13192
 
13193
+ AbstractCollectionBinding.prototype.dataChange = function(collection) {
13194
+ var items, _items;
13195
+ if (collection != null) {
13196
+ if (!this.bindCollection(collection)) {
13197
+ items = (collection != null ? collection.forEach : void 0) ? (_items = [], collection.forEach(function(item) {
13198
+ return _items.push(item);
13199
+ }), _items) : Object.keys(collection);
13200
+ this.handleArrayChanged(items);
13201
+ }
13202
+ } else {
13203
+ this.unbindCollection();
13204
+ this.collection = [];
13205
+ this.handleArrayChanged([]);
13206
+ }
13207
+ };
13208
+
12253
13209
  AbstractCollectionBinding.prototype.bindCollection = function(newCollection) {
12254
13210
  var _ref1;
12255
13211
  if (newCollection instanceof Batman.Hash) {
@@ -12368,6 +13324,9 @@
12368
13324
 
12369
13325
  StyleBinding.prototype.setStyle = function(key, value) {
12370
13326
  key = Batman.helpers.camelize(key.trim(), true);
13327
+ if (key === "") {
13328
+ return false;
13329
+ }
12371
13330
  if (this.oldStyles[key] == null) {
12372
13331
  this.oldStyles[key] = this.node.style[key] || "";
12373
13332
  }
@@ -12435,27 +13394,28 @@
12435
13394
  }).call(this);
12436
13395
 
12437
13396
  (function() {
12438
- var _ref,
12439
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
13397
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
12440
13398
  __hasProp = {}.hasOwnProperty,
12441
13399
  __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; };
12442
13400
 
12443
13401
  Batman.DOM.ClassBinding = (function(_super) {
12444
13402
  __extends(ClassBinding, _super);
12445
13403
 
13404
+ ClassBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.Data;
13405
+
12446
13406
  function ClassBinding() {
12447
13407
  this.handleArrayChanged = __bind(this.handleArrayChanged, this);
12448
- _ref = ClassBinding.__super__.constructor.apply(this, arguments);
12449
- return _ref;
13408
+ this.existingClasses = arguments[0].node.className.split(' ');
13409
+ ClassBinding.__super__.constructor.apply(this, arguments);
12450
13410
  }
12451
13411
 
12452
- ClassBinding.prototype.onlyObserve = Batman.BindingDefinitionOnlyObserve.Data;
12453
-
12454
13412
  ClassBinding.prototype.dataChange = function(value) {
13413
+ var newClasses;
12455
13414
  if (value != null) {
12456
13415
  this.unbindCollection();
12457
13416
  if (typeof value === 'string') {
12458
- return this.node.className = value;
13417
+ newClasses = [value].concat(this.existingClasses);
13418
+ return this.node.className = newClasses.join(' ').trim();
12459
13419
  } else {
12460
13420
  this.bindCollection(value);
12461
13421
  return this.updateFromCollection();
@@ -12464,17 +13424,17 @@
12464
13424
  };
12465
13425
 
12466
13426
  ClassBinding.prototype.updateFromCollection = function() {
12467
- var array, k, v;
13427
+ var array, existingClasses, k, newClasses, v;
12468
13428
  if (this.collection) {
12469
13429
  array = this.collection.map ? this.collection.map(function(x) {
12470
13430
  return x;
12471
13431
  }) : (function() {
12472
- var _ref1, _results;
12473
- _ref1 = this.collection;
13432
+ var _ref, _results;
13433
+ _ref = this.collection;
12474
13434
  _results = [];
12475
- for (k in _ref1) {
12476
- if (!__hasProp.call(_ref1, k)) continue;
12477
- v = _ref1[k];
13435
+ for (k in _ref) {
13436
+ if (!__hasProp.call(_ref, k)) continue;
13437
+ v = _ref[k];
12478
13438
  _results.push(k);
12479
13439
  }
12480
13440
  return _results;
@@ -12482,7 +13442,11 @@
12482
13442
  if (array.toArray != null) {
12483
13443
  array = array.toArray();
12484
13444
  }
12485
- return this.node.className = array.join(' ');
13445
+ existingClasses = this.existingClasses.slice(0);
13446
+ newClasses = array.filter(function(val) {
13447
+ return existingClasses.indexOf(val) === -1;
13448
+ });
13449
+ return this.node.className = existingClasses.concat(newClasses).join(' ').trim();
12486
13450
  }
12487
13451
  };
12488
13452
 
@@ -12523,22 +13487,22 @@
12523
13487
  parentNode = this.placeholderNode.parentNode || this.node.parentNode;
12524
13488
  if (!!value === !this.invert) {
12525
13489
  if (view != null) {
12526
- view.fire('viewWillShow');
13490
+ view.fireAndCall('viewWillShow');
12527
13491
  }
12528
13492
  if (this.node.parentNode == null) {
12529
13493
  parentNode.insertBefore(this.node, this.placeholderNode);
12530
13494
  parentNode.removeChild(this.placeholderNode);
12531
13495
  }
12532
- return view != null ? view.fire('viewDidShow') : void 0;
13496
+ return view != null ? view.fireAndCall('viewDidShow') : void 0;
12533
13497
  } else {
12534
13498
  if (view != null) {
12535
- view.fire('viewWillHide');
13499
+ view.fireAndCall('viewWillHide');
12536
13500
  }
12537
13501
  if (this.node.parentNode != null) {
12538
13502
  parentNode.insertBefore(this.placeholderNode, this.node);
12539
13503
  parentNode.removeChild(this.node);
12540
13504
  }
12541
- return view != null ? view.fire('viewDidHide') : void 0;
13505
+ return view != null ? view.fireAndCall('viewDidHide') : void 0;
12542
13506
  }
12543
13507
  };
12544
13508
 
@@ -12747,8 +13711,12 @@
12747
13711
  IteratorBinding.__super__.constructor.apply(this, arguments);
12748
13712
  this.backingView.set('attributeName', this.attributeName);
12749
13713
  this.view.prevent('ready');
12750
- Batman.setImmediate(function() {
13714
+ this._handle = Batman.setImmediate(function() {
12751
13715
  var parentNode;
13716
+ if (_this.backingView.isDead) {
13717
+ Batman.developer.warn("IteratorBinding (data-foreach-" + _this.iteratorName + "='" + _this.keyPath + "') trying to insert dead backing view into DOM (" + _this.view.constructor.name + ")");
13718
+ return;
13719
+ }
12752
13720
  parentNode = _this.prototypeNode.parentNode;
12753
13721
  parentNode.insertBefore(_this.backingView.get('node'), _this.prototypeNode);
12754
13722
  parentNode.removeChild(_this.prototypeNode);
@@ -12757,22 +13725,6 @@
12757
13725
  });
12758
13726
  }
12759
13727
 
12760
- IteratorBinding.prototype.dataChange = function(collection) {
12761
- var items, _items;
12762
- if (collection != null) {
12763
- if (!this.bindCollection(collection)) {
12764
- items = (collection != null ? collection.forEach : void 0) ? (_items = [], collection.forEach(function(item) {
12765
- return _items.push(item);
12766
- }), _items) : Object.keys(collection);
12767
- this.handleArrayChanged(items);
12768
- }
12769
- } else {
12770
- this.unbindCollection();
12771
- this.collection = [];
12772
- this.handleArrayChanged([]);
12773
- }
12774
- };
12775
-
12776
13728
  IteratorBinding.prototype.handleArrayChanged = function(newItems) {
12777
13729
  if (!this.backingView.isDead) {
12778
13730
  this.backingView.destroySubviews();
@@ -12806,6 +13758,9 @@
12806
13758
  };
12807
13759
 
12808
13760
  IteratorBinding.prototype.die = function() {
13761
+ if (this._handle) {
13762
+ Batman.clearImmediate(this._handle);
13763
+ }
12809
13764
  this.prototypeNode = null;
12810
13765
  return IteratorBinding.__super__.die.apply(this, arguments);
12811
13766
  };
@@ -13074,6 +14029,24 @@
13074
14029
  equals: buntUndefined(function(lhs, rhs, binding) {
13075
14030
  return lhs === rhs;
13076
14031
  }),
14032
+ eq: function(lhs, rhs) {
14033
+ return lhs === rhs;
14034
+ },
14035
+ neq: function(lhs, rhs) {
14036
+ return lhs !== rhs;
14037
+ },
14038
+ lt: buntUndefined(function(lhs, rhs) {
14039
+ return lhs < rhs;
14040
+ }),
14041
+ gt: buntUndefined(function(lhs, rhs) {
14042
+ return lhs > rhs;
14043
+ }),
14044
+ lteq: buntUndefined(function(lhs, rhs) {
14045
+ return lhs <= rhs;
14046
+ }),
14047
+ gteq: buntUndefined(function(lhs, rhs) {
14048
+ return lhs >= rhs;
14049
+ }),
13077
14050
  and: function(lhs, rhs) {
13078
14051
  return lhs && rhs;
13079
14052
  },
@@ -13083,6 +14056,27 @@
13083
14056
  not: function(value, binding) {
13084
14057
  return !value;
13085
14058
  },
14059
+ ceil: function(value) {
14060
+ return Math.ceil(value);
14061
+ },
14062
+ floor: function(value) {
14063
+ return Math.floor(value);
14064
+ },
14065
+ round: function(value) {
14066
+ return Math.round(value);
14067
+ },
14068
+ precision: function(value, p) {
14069
+ return parseFloat(value).toPrecision(p);
14070
+ },
14071
+ fixed: function(value, f) {
14072
+ return parseFloat(value).toFixed(f);
14073
+ },
14074
+ delimitNumber: function(value) {
14075
+ value = value.toString();
14076
+ return value.replace(/(^|[^\w.])(\d{4,})/g, function($0, $1, $2) {
14077
+ return $1 + $2.replace(/\d(?=(?:\d\d\d)+(?!\d))/g, "$&,");
14078
+ });
14079
+ },
13086
14080
  trim: buntUndefined(function(value, binding) {
13087
14081
  return value.trim();
13088
14082
  }),
@@ -13193,7 +14187,7 @@
13193
14187
  values = {};
13194
14188
  for (k in interpolationKeypaths) {
13195
14189
  v = interpolationKeypaths[k];
13196
- values[k] = this.get(v);
14190
+ values[k] = this.lookupKeypath(v);
13197
14191
  if (values[k] == null) {
13198
14192
  Batman.developer.warn("Warning! Undefined interpolation key " + k + " for interpolation", string);
13199
14193
  values[k] = '';
@@ -13207,18 +14201,14 @@
13207
14201
  if (!block) {
13208
14202
  return;
13209
14203
  }
13210
- return function() {
13211
- var regularArgs;
13212
- regularArgs = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
13213
- return block.call.apply(block, [this].concat(__slice.call(curryArgs), __slice.call(regularArgs)));
13214
- };
14204
+ if (typeof block === "function") {
14205
+ return function() {
14206
+ return block.call.apply(block, [this].concat(__slice.call(curryArgs), __slice.call(arguments)));
14207
+ };
14208
+ } else if (typeof block.get === "function") {
14209
+ return block.get.apply(block, curryArgs);
14210
+ }
13215
14211
  },
13216
- routeToAction: buntUndefined(function(model, action) {
13217
- var params;
13218
- params = Batman.Dispatcher.paramsFromArgument(model);
13219
- params.action = action;
13220
- return params;
13221
- }),
13222
14212
  escape: buntUndefined(Batman.escapeHTML)
13223
14213
  };
13224
14214
 
@@ -13240,4 +14230,1455 @@
13240
14230
  (function() {
13241
14231
 
13242
14232
 
14233
+ }).call(this);
14234
+
14235
+ /**
14236
+ * Zest (https://github.com/chjj/zest)
14237
+ * A css selector engine.
14238
+ * Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed)
14239
+ */
14240
+
14241
+ // TODO
14242
+ // - Recognize the TR subject selector when parsing.
14243
+ // - Pass context to scope.
14244
+ // - Add :column pseudo-classes.
14245
+
14246
+ ;(function() {
14247
+
14248
+ /**
14249
+ * Shared
14250
+ */
14251
+
14252
+ var window = this
14253
+ , document = this.document
14254
+ , old = this.zest;
14255
+
14256
+ /**
14257
+ * Helpers
14258
+ */
14259
+
14260
+ var compareDocumentPosition = (function() {
14261
+ if (document.compareDocumentPosition) {
14262
+ return function(a, b) {
14263
+ return a.compareDocumentPosition(b);
14264
+ };
14265
+ }
14266
+ return function(a, b) {
14267
+ var el = a.ownerDocument.getElementsByTagName('*')
14268
+ , i = el.length;
14269
+
14270
+ while (i--) {
14271
+ if (el[i] === a) return 2;
14272
+ if (el[i] === b) return 4;
14273
+ }
14274
+
14275
+ return 1;
14276
+ };
14277
+ })();
14278
+
14279
+ var order = function(a, b) {
14280
+ return compareDocumentPosition(a, b) & 2 ? 1 : -1;
14281
+ };
14282
+
14283
+ var next = function(el) {
14284
+ while ((el = el.nextSibling)
14285
+ && el.nodeType !== 1);
14286
+ return el;
14287
+ };
14288
+
14289
+ var prev = function(el) {
14290
+ while ((el = el.previousSibling)
14291
+ && el.nodeType !== 1);
14292
+ return el;
14293
+ };
14294
+
14295
+ var child = function(el) {
14296
+ if (el = el.firstChild) {
14297
+ while (el.nodeType !== 1
14298
+ && (el = el.nextSibling));
14299
+ }
14300
+ return el;
14301
+ };
14302
+
14303
+ var lastChild = function(el) {
14304
+ if (el = el.lastChild) {
14305
+ while (el.nodeType !== 1
14306
+ && (el = el.previousSibling));
14307
+ }
14308
+ return el;
14309
+ };
14310
+
14311
+ var unquote = function(str) {
14312
+ if (!str) return str;
14313
+ var ch = str[0];
14314
+ return ch === '"' || ch === '\''
14315
+ ? str.slice(1, -1)
14316
+ : str;
14317
+ };
14318
+
14319
+ var indexOf = (function() {
14320
+ if (Array.prototype.indexOf) {
14321
+ return Array.prototype.indexOf;
14322
+ }
14323
+ return function(obj, item) {
14324
+ var i = this.length;
14325
+ while (i--) {
14326
+ if (this[i] === item) return i;
14327
+ }
14328
+ return -1;
14329
+ };
14330
+ })();
14331
+
14332
+ var makeInside = function(start, end) {
14333
+ var regex = rules.inside.source
14334
+ .replace(/</g, start)
14335
+ .replace(/>/g, end);
14336
+
14337
+ return new RegExp(regex);
14338
+ };
14339
+
14340
+ var replace = function(regex, name, val) {
14341
+ regex = regex.source;
14342
+ regex = regex.replace(name, val.source || val);
14343
+ return new RegExp(regex);
14344
+ };
14345
+
14346
+ var truncateUrl = function(url, num) {
14347
+ return url
14348
+ .replace(/^(?:\w+:\/\/|\/+)/, '')
14349
+ .replace(/(?:\/+|\/*#.*?)$/, '')
14350
+ .split('/', num)
14351
+ .join('/');
14352
+ };
14353
+
14354
+ /**
14355
+ * Handle `nth` Selectors
14356
+ */
14357
+
14358
+ var parseNth = function(param, test) {
14359
+ var param = param.replace(/\s+/g, '')
14360
+ , cap;
14361
+
14362
+ if (param === 'even') {
14363
+ param = '2n+0';
14364
+ } else if (param === 'odd') {
14365
+ param = '2n+1';
14366
+ } else if (!~param.indexOf('n')) {
14367
+ param = '0n' + param;
14368
+ }
14369
+
14370
+ cap = /^([+-])?(\d+)?n([+-])?(\d+)?$/.exec(param);
14371
+
14372
+ return {
14373
+ group: cap[1] === '-'
14374
+ ? -(cap[2] || 1)
14375
+ : +(cap[2] || 1),
14376
+ offset: cap[4]
14377
+ ? (cap[3] === '-' ? -cap[4] : +cap[4])
14378
+ : 0
14379
+ };
14380
+ };
14381
+
14382
+ var nth = function(param, test, last) {
14383
+ var param = parseNth(param)
14384
+ , group = param.group
14385
+ , offset = param.offset
14386
+ , find = !last ? child : lastChild
14387
+ , advance = !last ? next : prev;
14388
+
14389
+ return function(el) {
14390
+ if (el.parentNode.nodeType !== 1) return;
14391
+
14392
+ var rel = find(el.parentNode)
14393
+ , pos = 0;
14394
+
14395
+ while (rel) {
14396
+ if (test(rel, el)) pos++;
14397
+ if (rel === el) {
14398
+ pos -= offset;
14399
+ return group && pos
14400
+ ? !(pos % group) && (pos < 0 === group < 0)
14401
+ : !pos;
14402
+ }
14403
+ rel = advance(rel);
14404
+ }
14405
+ };
14406
+ };
14407
+
14408
+ /**
14409
+ * Simple Selectors
14410
+ */
14411
+
14412
+ var selectors = {
14413
+ '*': (function() {
14414
+ if (function() {
14415
+ var el = document.createElement('div');
14416
+ el.appendChild(document.createComment(''));
14417
+ return !!el.getElementsByTagName('*')[0];
14418
+ }()) {
14419
+ return function(el) {
14420
+ if (el.nodeType === 1) return true;
14421
+ };
14422
+ }
14423
+ return function() {
14424
+ return true;
14425
+ };
14426
+ })(),
14427
+ 'type': function(type) {
14428
+ type = type.toLowerCase();
14429
+ return function(el) {
14430
+ return el.nodeName.toLowerCase() === type;
14431
+ };
14432
+ },
14433
+ 'attr': function(key, op, val, i) {
14434
+ op = operators[op];
14435
+ return function(el) {
14436
+ var attr;
14437
+ switch (key) {
14438
+ case 'for':
14439
+ attr = el.htmlFor;
14440
+ break;
14441
+ case 'class':
14442
+ // className is '' when non-existent
14443
+ // getAttribute('class') is null
14444
+ attr = el.className;
14445
+ if (attr === '' && el.getAttribute('class') == null) {
14446
+ attr = null;
14447
+ }
14448
+ break;
14449
+ case 'href':
14450
+ attr = el.getAttribute('href', 2);
14451
+ break;
14452
+ case 'title':
14453
+ // getAttribute('title') can be '' when non-existent sometimes?
14454
+ attr = el.getAttribute('title') || null;
14455
+ break;
14456
+ case 'id':
14457
+ if (el.getAttribute) {
14458
+ attr = el.getAttribute('id');
14459
+ break;
14460
+ }
14461
+ default:
14462
+ attr = el[key] != null
14463
+ ? el[key]
14464
+ : el.getAttribute && el.getAttribute(key);
14465
+ break;
14466
+ }
14467
+ if (attr == null) return;
14468
+ attr = attr + '';
14469
+ if (i) {
14470
+ attr = attr.toLowerCase();
14471
+ val = val.toLowerCase();
14472
+ }
14473
+ return op(attr, val);
14474
+ };
14475
+ },
14476
+ ':first-child': function(el) {
14477
+ return !prev(el) && el.parentNode.nodeType === 1;
14478
+ },
14479
+ ':last-child': function(el) {
14480
+ return !next(el) && el.parentNode.nodeType === 1;
14481
+ },
14482
+ ':only-child': function(el) {
14483
+ return !prev(el) && !next(el)
14484
+ && el.parentNode.nodeType === 1;
14485
+ },
14486
+ ':nth-child': function(param, last) {
14487
+ return nth(param, function() {
14488
+ return true;
14489
+ }, last);
14490
+ },
14491
+ ':nth-last-child': function(param) {
14492
+ return selectors[':nth-child'](param, true);
14493
+ },
14494
+ ':root': function(el) {
14495
+ return el.ownerDocument.documentElement === el;
14496
+ },
14497
+ ':empty': function(el) {
14498
+ return !el.firstChild;
14499
+ },
14500
+ ':not': function(sel) {
14501
+ var test = compileGroup(sel);
14502
+ return function(el) {
14503
+ return !test(el);
14504
+ };
14505
+ },
14506
+ ':first-of-type': function(el) {
14507
+ if (el.parentNode.nodeType !== 1) return;
14508
+ var type = el.nodeName;
14509
+ while (el = prev(el)) {
14510
+ if (el.nodeName === type) return;
14511
+ }
14512
+ return true;
14513
+ },
14514
+ ':last-of-type': function(el) {
14515
+ if (el.parentNode.nodeType !== 1) return;
14516
+ var type = el.nodeName;
14517
+ while (el = next(el)) {
14518
+ if (el.nodeName === type) return;
14519
+ }
14520
+ return true;
14521
+ },
14522
+ ':only-of-type': function(el) {
14523
+ return selectors[':first-of-type'](el)
14524
+ && selectors[':last-of-type'](el);
14525
+ },
14526
+ ':nth-of-type': function(param, last) {
14527
+ return nth(param, function(rel, el) {
14528
+ return rel.nodeName === el.nodeName;
14529
+ }, last);
14530
+ },
14531
+ ':nth-last-of-type': function(param) {
14532
+ return selectors[':nth-of-type'](param, true);
14533
+ },
14534
+ ':checked': function(el) {
14535
+ return !!(el.checked || el.selected);
14536
+ },
14537
+ ':indeterminate': function(el) {
14538
+ return !selectors[':checked'](el);
14539
+ },
14540
+ ':enabled': function(el) {
14541
+ return !el.disabled && el.type !== 'hidden';
14542
+ },
14543
+ ':disabled': function(el) {
14544
+ return !!el.disabled;
14545
+ },
14546
+ ':target': function(el) {
14547
+ return el.id === window.location.hash.substring(1);
14548
+ },
14549
+ ':focus': function(el) {
14550
+ return el === el.ownerDocument.activeElement;
14551
+ },
14552
+ ':matches': function(sel) {
14553
+ return compileGroup(sel);
14554
+ },
14555
+ ':nth-match': function(param, last) {
14556
+ var args = param.split(/\s*,\s*/)
14557
+ , arg = args.shift()
14558
+ , test = compileGroup(args.join(','));
14559
+
14560
+ return nth(arg, test, last);
14561
+ },
14562
+ ':nth-last-match': function(param) {
14563
+ return selectors[':nth-match'](param, true);
14564
+ },
14565
+ ':links-here': function(el) {
14566
+ return el + '' === window.location + '';
14567
+ },
14568
+ ':lang': function(param) {
14569
+ return function(el) {
14570
+ while (el) {
14571
+ if (el.lang) return el.lang.indexOf(param) === 0;
14572
+ el = el.parentNode;
14573
+ }
14574
+ };
14575
+ },
14576
+ ':dir': function(param) {
14577
+ return function(el) {
14578
+ while (el) {
14579
+ if (el.dir) return el.dir === param;
14580
+ el = el.parentNode;
14581
+ }
14582
+ };
14583
+ },
14584
+ ':scope': function(el, con) {
14585
+ var context = con || el.ownerDocument;
14586
+ if (context.nodeType === 9) {
14587
+ return el === context.documentElement;
14588
+ }
14589
+ return el === context;
14590
+ },
14591
+ ':any-link': function(el) {
14592
+ return typeof el.href === 'string';
14593
+ },
14594
+ ':local-link': function(el) {
14595
+ if (el.nodeName) {
14596
+ return el.href && el.host === window.location.host;
14597
+ }
14598
+ var param = +el + 1;
14599
+ return function(el) {
14600
+ if (!el.href) return;
14601
+
14602
+ var url = window.location + ''
14603
+ , href = el + '';
14604
+
14605
+ return truncateUrl(url, param) === truncateUrl(href, param);
14606
+ };
14607
+ },
14608
+ ':default': function(el) {
14609
+ return !!el.defaultSelected;
14610
+ },
14611
+ ':valid': function(el) {
14612
+ return el.willValidate || (el.validity && el.validity.valid);
14613
+ },
14614
+ ':invalid': function(el) {
14615
+ return !selectors[':valid'](el);
14616
+ },
14617
+ ':in-range': function(el) {
14618
+ return el.value > el.min && el.value <= el.max;
14619
+ },
14620
+ ':out-of-range': function(el) {
14621
+ return !selectors[':in-range'](el);
14622
+ },
14623
+ ':required': function(el) {
14624
+ return !!el.required;
14625
+ },
14626
+ ':optional': function(el) {
14627
+ return !el.required;
14628
+ },
14629
+ ':read-only': function(el) {
14630
+ if (el.readOnly) return true;
14631
+
14632
+ var attr = el.getAttribute('contenteditable')
14633
+ , prop = el.contentEditable
14634
+ , name = el.nodeName.toLowerCase();
14635
+
14636
+ name = name !== 'input' && name !== 'textarea';
14637
+
14638
+ return (name || el.disabled) && attr == null && prop !== 'true';
14639
+ },
14640
+ ':read-write': function(el) {
14641
+ return !selectors[':read-only'](el);
14642
+ },
14643
+ ':hover': function() {
14644
+ throw new Error(':hover is not supported.');
14645
+ },
14646
+ ':active': function() {
14647
+ throw new Error(':active is not supported.');
14648
+ },
14649
+ ':link': function() {
14650
+ throw new Error(':link is not supported.');
14651
+ },
14652
+ ':visited': function() {
14653
+ throw new Error(':visited is not supported.');
14654
+ },
14655
+ ':column': function() {
14656
+ throw new Error(':column is not supported.');
14657
+ },
14658
+ ':nth-column': function() {
14659
+ throw new Error(':nth-column is not supported.');
14660
+ },
14661
+ ':nth-last-column': function() {
14662
+ throw new Error(':nth-last-column is not supported.');
14663
+ },
14664
+ ':current': function() {
14665
+ throw new Error(':current is not supported.');
14666
+ },
14667
+ ':past': function() {
14668
+ throw new Error(':past is not supported.');
14669
+ },
14670
+ ':future': function() {
14671
+ throw new Error(':future is not supported.');
14672
+ },
14673
+ // Non-standard, for compatibility purposes.
14674
+ ':contains': function(param) {
14675
+ return function(el) {
14676
+ var text = el.innerText || el.textContent || el.value || '';
14677
+ return !!~text.indexOf(param);
14678
+ };
14679
+ },
14680
+ ':has': function(param) {
14681
+ return function(el) {
14682
+ return zest(param, el).length > 0;
14683
+ };
14684
+ }
14685
+ // Potentially add more pseudo selectors for
14686
+ // compatibility with sizzle and most other
14687
+ // selector engines (?).
14688
+ };
14689
+
14690
+ /**
14691
+ * Attribute Operators
14692
+ */
14693
+
14694
+ var operators = {
14695
+ '-': function() {
14696
+ return true;
14697
+ },
14698
+ '=': function(attr, val) {
14699
+ return attr === val;
14700
+ },
14701
+ '*=': function(attr, val) {
14702
+ return attr.indexOf(val) !== -1;
14703
+ },
14704
+ '~=': function(attr, val) {
14705
+ var i = attr.indexOf(val)
14706
+ , f
14707
+ , l;
14708
+
14709
+ if (i === -1) return;
14710
+ f = attr[i - 1];
14711
+ l = attr[i + val.length];
14712
+
14713
+ return (!f || f === ' ') && (!l || l === ' ');
14714
+ },
14715
+ '|=': function(attr, val) {
14716
+ var i = attr.indexOf(val)
14717
+ , l;
14718
+
14719
+ if (i !== 0) return;
14720
+ l = attr[i + val.length];
14721
+
14722
+ return l === '-' || !l;
14723
+ },
14724
+ '^=': function(attr, val) {
14725
+ return attr.indexOf(val) === 0;
14726
+ },
14727
+ '$=': function(attr, val) {
14728
+ return attr.indexOf(val) + val.length === attr.length;
14729
+ },
14730
+ // non-standard
14731
+ '!=': function(attr, val) {
14732
+ return attr !== val;
14733
+ }
14734
+ };
14735
+
14736
+ /**
14737
+ * Combinator Logic
14738
+ */
14739
+
14740
+ var combinators = {
14741
+ ' ': function(test) {
14742
+ return function(el) {
14743
+ while (el = el.parentNode) {
14744
+ if (test(el)) return el;
14745
+ }
14746
+ };
14747
+ },
14748
+ '>': function(test) {
14749
+ return function(el) {
14750
+ return test(el = el.parentNode) && el;
14751
+ };
14752
+ },
14753
+ '+': function(test) {
14754
+ return function(el) {
14755
+ return test(el = prev(el)) && el;
14756
+ };
14757
+ },
14758
+ '~': function(test) {
14759
+ return function(el) {
14760
+ while (el = prev(el)) {
14761
+ if (test(el)) return el;
14762
+ }
14763
+ };
14764
+ },
14765
+ 'noop': function(test) {
14766
+ return function(el) {
14767
+ return test(el) && el;
14768
+ };
14769
+ },
14770
+ 'ref': function(test, name) {
14771
+ var node;
14772
+
14773
+ function ref(el) {
14774
+ var doc = el.ownerDocument
14775
+ , nodes = doc.getElementsByTagName('*')
14776
+ , i = nodes.length;
14777
+
14778
+ while (i--) {
14779
+ node = nodes[i];
14780
+ if (ref.test(el)) {
14781
+ node = null;
14782
+ return true;
14783
+ }
14784
+ }
14785
+
14786
+ node = null;
14787
+ }
14788
+
14789
+ ref.combinator = function(el) {
14790
+ if (!node || !node.getAttribute) return;
14791
+
14792
+ var attr = node.getAttribute(name) || '';
14793
+ if (attr[0] === '#') attr = attr.substring(1);
14794
+
14795
+ if (attr === el.id && test(node)) {
14796
+ return node;
14797
+ }
14798
+ };
14799
+
14800
+ return ref;
14801
+ }
14802
+ };
14803
+
14804
+ /**
14805
+ * Grammar
14806
+ */
14807
+
14808
+ var rules = {
14809
+ qname: /^ *([\w\-]+|\*)/,
14810
+ simple: /^(?:([.#][\w\-]+)|pseudo|attr)/,
14811
+ ref: /^ *\/([\w\-]+)\/ */,
14812
+ combinator: /^(?: +([^ \w*]) +|( )+|([^ \w*]))(?! *$)/,
14813
+ attr: /^\[([\w\-]+)(?:([^\w]?=)(inside))?\]/,
14814
+ pseudo: /^(:[\w\-]+)(?:\((inside)\))?/,
14815
+ inside: /(?:"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|<[^"'>]*>|\\["'>]|[^"'>])*/
14816
+ };
14817
+
14818
+ rules.inside = replace(rules.inside, '[^"\'>]*', rules.inside);
14819
+ rules.attr = replace(rules.attr, 'inside', makeInside('\\[', '\\]'));
14820
+ rules.pseudo = replace(rules.pseudo, 'inside', makeInside('\\(', '\\)'));
14821
+ rules.simple = replace(rules.simple, 'pseudo', rules.pseudo);
14822
+ rules.simple = replace(rules.simple, 'attr', rules.attr);
14823
+
14824
+ /**
14825
+ * Compiling
14826
+ */
14827
+
14828
+ var compile = function(sel) {
14829
+ var sel = sel.replace(/^\s+|\s+$/g, '')
14830
+ , test
14831
+ , filter = []
14832
+ , buff = []
14833
+ , subject
14834
+ , qname
14835
+ , cap
14836
+ , op
14837
+ , ref;
14838
+
14839
+ while (sel) {
14840
+ if (cap = rules.qname.exec(sel)) {
14841
+ sel = sel.substring(cap[0].length);
14842
+ qname = cap[1];
14843
+ buff.push(tok(qname, true));
14844
+ } else if (cap = rules.simple.exec(sel)) {
14845
+ sel = sel.substring(cap[0].length);
14846
+ qname = '*';
14847
+ buff.push(tok(qname, true));
14848
+ buff.push(tok(cap));
14849
+ } else {
14850
+ throw new Error('Invalid selector.');
14851
+ }
14852
+
14853
+ while (cap = rules.simple.exec(sel)) {
14854
+ sel = sel.substring(cap[0].length);
14855
+ buff.push(tok(cap));
14856
+ }
14857
+
14858
+ if (sel[0] === '!') {
14859
+ sel = sel.substring(1);
14860
+ subject = makeSubject();
14861
+ subject.qname = qname;
14862
+ buff.push(subject.simple);
14863
+ }
14864
+
14865
+ if (cap = rules.ref.exec(sel)) {
14866
+ sel = sel.substring(cap[0].length);
14867
+ ref = combinators.ref(makeSimple(buff), cap[1]);
14868
+ filter.push(ref.combinator);
14869
+ buff = [];
14870
+ continue;
14871
+ }
14872
+
14873
+ if (cap = rules.combinator.exec(sel)) {
14874
+ sel = sel.substring(cap[0].length);
14875
+ op = cap[1] || cap[2] || cap[3];
14876
+ if (op === ',') {
14877
+ filter.push(combinators.noop(makeSimple(buff)));
14878
+ break;
14879
+ }
14880
+ } else {
14881
+ op = 'noop';
14882
+ }
14883
+
14884
+ filter.push(combinators[op](makeSimple(buff)));
14885
+ buff = [];
14886
+ }
14887
+
14888
+ test = makeTest(filter);
14889
+ test.qname = qname;
14890
+ test.sel = sel;
14891
+
14892
+ if (subject) {
14893
+ subject.lname = test.qname;
14894
+
14895
+ subject.test = test;
14896
+ subject.qname = subject.qname;
14897
+ subject.sel = test.sel;
14898
+ test = subject;
14899
+ }
14900
+
14901
+ if (ref) {
14902
+ ref.test = test;
14903
+ ref.qname = test.qname;
14904
+ ref.sel = test.sel;
14905
+ test = ref;
14906
+ }
14907
+
14908
+ return test;
14909
+ };
14910
+
14911
+ var tok = function(cap, qname) {
14912
+ // qname
14913
+ if (qname) {
14914
+ return cap === '*'
14915
+ ? selectors['*']
14916
+ : selectors.type(cap);
14917
+ }
14918
+
14919
+ // class/id
14920
+ if (cap[1]) {
14921
+ return cap[1][0] === '.'
14922
+ ? selectors.attr('class', '~=', cap[1].substring(1))
14923
+ : selectors.attr('id', '=', cap[1].substring(1));
14924
+ }
14925
+
14926
+ // pseudo-name
14927
+ // inside-pseudo
14928
+ if (cap[2]) {
14929
+ return cap[3]
14930
+ ? selectors[cap[2]](unquote(cap[3]))
14931
+ : selectors[cap[2]];
14932
+ }
14933
+
14934
+ // attr name
14935
+ // attr op
14936
+ // attr value
14937
+ if (cap[4]) {
14938
+ var i;
14939
+ if (cap[6]) {
14940
+ i = cap[6].length;
14941
+ cap[6] = cap[6].replace(/ +i$/, '');
14942
+ i = i > cap[6].length;
14943
+ }
14944
+ return selectors.attr(cap[4], cap[5] || '-', unquote(cap[6]), i);
14945
+ }
14946
+
14947
+ throw new Error('Unknown Selector.');
14948
+ };
14949
+
14950
+ var makeSimple = function(func) {
14951
+ var l = func.length
14952
+ , i;
14953
+
14954
+ // Potentially make sure
14955
+ // `el` is truthy.
14956
+ if (l < 2) return func[0];
14957
+
14958
+ return function(el) {
14959
+ if (!el) return;
14960
+ for (i = 0; i < l; i++) {
14961
+ if (!func[i](el)) return;
14962
+ }
14963
+ return true;
14964
+ };
14965
+ };
14966
+
14967
+ var makeTest = function(func) {
14968
+ if (func.length < 2) {
14969
+ return function(el) {
14970
+ return !!func[0](el);
14971
+ };
14972
+ }
14973
+ return function(el) {
14974
+ var i = func.length;
14975
+ while (i--) {
14976
+ if (!(el = func[i](el))) return;
14977
+ }
14978
+ return true;
14979
+ };
14980
+ };
14981
+
14982
+ var makeSubject = function() {
14983
+ var target;
14984
+
14985
+ function subject(el) {
14986
+ var node = el.ownerDocument
14987
+ , scope = node.getElementsByTagName(subject.lname)
14988
+ , i = scope.length;
14989
+
14990
+ while (i--) {
14991
+ if (subject.test(scope[i]) && target === el) {
14992
+ target = null;
14993
+ return true;
14994
+ }
14995
+ }
14996
+
14997
+ target = null;
14998
+ }
14999
+
15000
+ subject.simple = function(el) {
15001
+ target = el;
15002
+ return true;
15003
+ };
15004
+
15005
+ return subject;
15006
+ };
15007
+
15008
+ var compileGroup = function(sel) {
15009
+ var test = compile(sel)
15010
+ , tests = [ test ];
15011
+
15012
+ while (test.sel) {
15013
+ test = compile(test.sel);
15014
+ tests.push(test);
15015
+ }
15016
+
15017
+ if (tests.length < 2) return test;
15018
+
15019
+ return function(el) {
15020
+ var l = tests.length
15021
+ , i = 0;
15022
+
15023
+ for (; i < l; i++) {
15024
+ if (tests[i](el)) return true;
15025
+ }
15026
+ };
15027
+ };
15028
+
15029
+ /**
15030
+ * Selection
15031
+ */
15032
+
15033
+ var find = function(sel, node) {
15034
+ var results = []
15035
+ , test = compile(sel)
15036
+ , scope = node.getElementsByTagName(test.qname)
15037
+ , i = 0
15038
+ , el;
15039
+
15040
+ while (el = scope[i++]) {
15041
+ if (test(el)) results.push(el);
15042
+ }
15043
+
15044
+ if (test.sel) {
15045
+ while (test.sel) {
15046
+ test = compile(test.sel);
15047
+ scope = node.getElementsByTagName(test.qname);
15048
+ i = 0;
15049
+ while (el = scope[i++]) {
15050
+ if (test(el) && !~indexOf.call(results, el)) {
15051
+ results.push(el);
15052
+ }
15053
+ }
15054
+ }
15055
+ results.sort(order);
15056
+ }
15057
+
15058
+ return results;
15059
+ };
15060
+
15061
+ /**
15062
+ * Native
15063
+ */
15064
+
15065
+ var select = (function() {
15066
+ var slice = (function() {
15067
+ try {
15068
+ Array.prototype.slice.call(document.getElementsByTagName('zest'));
15069
+ return Array.prototype.slice;
15070
+ } catch(e) {
15071
+ e = null;
15072
+ return function() {
15073
+ var a = [], i = 0, l = this.length;
15074
+ for (; i < l; i++) a.push(this[i]);
15075
+ return a;
15076
+ };
15077
+ }
15078
+ })();
15079
+
15080
+ if (document.querySelectorAll) {
15081
+ return function(sel, node) {
15082
+ try {
15083
+ return slice.call(node.querySelectorAll(sel));
15084
+ } catch(e) {
15085
+ return find(sel, node);
15086
+ }
15087
+ };
15088
+ }
15089
+
15090
+ return function(sel, node) {
15091
+ try {
15092
+ if (sel[0] === '#' && /^#[\w\-]+$/.test(sel)) {
15093
+ return [node.getElementById(sel.substring(1))];
15094
+ }
15095
+ if (sel[0] === '.' && /^\.[\w\-]+$/.test(sel)) {
15096
+ sel = node.getElementsByClassName(sel.substring(1));
15097
+ return slice.call(sel);
15098
+ }
15099
+ if (/^[\w\-]+$/.test(sel)) {
15100
+ return slice.call(node.getElementsByTagName(sel));
15101
+ }
15102
+ } catch(e) {
15103
+ ;
15104
+ }
15105
+ return find(sel, node);
15106
+ };
15107
+ })();
15108
+
15109
+ /**
15110
+ * Zest
15111
+ */
15112
+
15113
+ var zest = function(sel, node) {
15114
+ try {
15115
+ sel = select(sel, node || document);
15116
+ } catch(e) {
15117
+ if (window.ZEST_DEBUG) {
15118
+ console.log(e.stack || e + '');
15119
+ }
15120
+ sel = [];
15121
+ }
15122
+ return sel;
15123
+ };
15124
+
15125
+ /**
15126
+ * Expose
15127
+ */
15128
+
15129
+ zest.selectors = selectors;
15130
+ zest.operators = operators;
15131
+ zest.combinators = combinators;
15132
+ zest.compile = compileGroup;
15133
+
15134
+ zest.matches = function(el, sel) {
15135
+ return !!compileGroup(sel)(el);
15136
+ };
15137
+
15138
+ zest.cache = function() {
15139
+ if (compile.raw) return;
15140
+
15141
+ var raw = compile
15142
+ , cache = {};
15143
+
15144
+ compile = function(sel) {
15145
+ return cache[sel]
15146
+ || (cache[sel] = raw(sel));
15147
+ };
15148
+
15149
+ compile.raw = raw;
15150
+ zest._cache = cache;
15151
+ };
15152
+
15153
+ zest.noCache = function() {
15154
+ if (!compile.raw) return;
15155
+ compile = compile.raw;
15156
+ delete zest._cache;
15157
+ };
15158
+
15159
+ zest.noConflict = function() {
15160
+ window.zest = old;
15161
+ return zest;
15162
+ };
15163
+
15164
+ zest.noNative = function() {
15165
+ select = find;
15166
+ };
15167
+
15168
+ if (typeof module !== 'undefined') {
15169
+ module.exports = zest;
15170
+ } else {
15171
+ this.zest = zest;
15172
+ }
15173
+
15174
+ if (window.ZEST_DEBUG) {
15175
+ zest.noNative();
15176
+ } else {
15177
+ zest.cache();
15178
+ }
15179
+
15180
+ }).call(function() {
15181
+ return this || (typeof window !== 'undefined' ? window : global);
15182
+ }());
15183
+
15184
+ /*!
15185
+ * Reqwest! A general purpose XHR connection manager
15186
+ * (c) Dustin Diaz 2011
15187
+ * https://github.com/ded/reqwest
15188
+ * license MIT
15189
+ */
15190
+ !function (name, definition) {
15191
+ if (typeof module != 'undefined') module.exports = definition()
15192
+ else if (typeof define == 'function' && define.amd) define(name, definition)
15193
+ else this[name] = definition()
15194
+ }('reqwest', function () {
15195
+
15196
+ var context = this
15197
+ , win = window
15198
+ , doc = document
15199
+ , old = context.reqwest
15200
+ , twoHundo = /^20\d$/
15201
+ , byTag = 'getElementsByTagName'
15202
+ , readyState = 'readyState'
15203
+ , contentType = 'Content-Type'
15204
+ , requestedWith = 'X-Requested-With'
15205
+ , head = doc[byTag]('head')[0]
15206
+ , uniqid = 0
15207
+ , lastValue // data stored by the most recent JSONP callback
15208
+ , xmlHttpRequest = 'XMLHttpRequest'
15209
+ , isArray = typeof Array.isArray == 'function' ? Array.isArray : function (a) {
15210
+ return a instanceof Array
15211
+ }
15212
+ , defaultHeaders = {
15213
+ contentType: 'application/x-www-form-urlencoded'
15214
+ , accept: {
15215
+ '*': 'text/javascript, text/html, application/xml, text/xml, */*'
15216
+ , xml: 'application/xml, text/xml'
15217
+ , html: 'text/html'
15218
+ , text: 'text/plain'
15219
+ , json: 'application/json, text/javascript'
15220
+ , js: 'application/javascript, text/javascript'
15221
+ }
15222
+ , requestedWith: xmlHttpRequest
15223
+ }
15224
+ , xhr = win[xmlHttpRequest] ?
15225
+ function () {
15226
+ return new XMLHttpRequest()
15227
+ } :
15228
+ function () {
15229
+ return new ActiveXObject('Microsoft.XMLHTTP')
15230
+ }
15231
+
15232
+ function handleReadyState(o, success, error) {
15233
+ return function () {
15234
+ if (o && o[readyState] == 4) {
15235
+ if (twoHundo.test(o.status)) {
15236
+ success(o)
15237
+ } else {
15238
+ error(o)
15239
+ }
15240
+ }
15241
+ }
15242
+ }
15243
+
15244
+ function setHeaders(http, o) {
15245
+ var headers = o.headers || {}, h
15246
+ headers.Accept = headers.Accept || defaultHeaders.accept[o.type] || defaultHeaders.accept['*']
15247
+ // breaks cross-origin requests with legacy browsers
15248
+ if (!o.crossOrigin && !headers[requestedWith]) headers[requestedWith] = defaultHeaders.requestedWith
15249
+ if (!headers[contentType]) headers[contentType] = o.contentType || defaultHeaders.contentType
15250
+ for (h in headers) {
15251
+ headers.hasOwnProperty(h) && http.setRequestHeader(h, headers[h])
15252
+ }
15253
+ }
15254
+
15255
+ function generalCallback(data) {
15256
+ lastValue = data
15257
+ }
15258
+
15259
+ function urlappend(url, s) {
15260
+ return url + (/\?/.test(url) ? '&' : '?') + s
15261
+ }
15262
+
15263
+ function handleJsonp(o, fn, err, url) {
15264
+ var reqId = uniqid++
15265
+ , cbkey = o.jsonpCallback || 'callback' // the 'callback' key
15266
+ , cbval = o.jsonpCallbackName || ('reqwest_' + reqId) // the 'callback' value
15267
+ , cbreg = new RegExp('((^|\\?|&)' + cbkey + ')=([^&]+)')
15268
+ , match = url.match(cbreg)
15269
+ , script = doc.createElement('script')
15270
+ , loaded = 0
15271
+
15272
+ if (match) {
15273
+ if (match[3] === '?') {
15274
+ url = url.replace(cbreg, '$1=' + cbval) // wildcard callback func name
15275
+ } else {
15276
+ cbval = match[3] // provided callback func name
15277
+ }
15278
+ } else {
15279
+ url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em
15280
+ }
15281
+
15282
+ win[cbval] = generalCallback
15283
+
15284
+ script.type = 'text/javascript'
15285
+ script.src = url
15286
+ script.async = true
15287
+ if (typeof script.onreadystatechange !== 'undefined') {
15288
+ // need this for IE due to out-of-order onreadystatechange(), binding script
15289
+ // execution to an event listener gives us control over when the script
15290
+ // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
15291
+ script.event = 'onclick'
15292
+ script.htmlFor = script.id = '_reqwest_' + reqId
15293
+ }
15294
+
15295
+ script.onload = script.onreadystatechange = function () {
15296
+ if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) {
15297
+ return false
15298
+ }
15299
+ script.onload = script.onreadystatechange = null
15300
+ script.onclick && script.onclick()
15301
+ // Call the user callback with the last value stored and clean up values and scripts.
15302
+ o.success && o.success(lastValue)
15303
+ lastValue = undefined
15304
+ head.removeChild(script)
15305
+ loaded = 1
15306
+ }
15307
+
15308
+ // Add the script to the DOM head
15309
+ head.appendChild(script)
15310
+ }
15311
+
15312
+ function getRequest(o, fn, err) {
15313
+ var method = (o.method || 'GET').toUpperCase()
15314
+ , url = typeof o === 'string' ? o : o.url
15315
+ // convert non-string objects to query-string form unless o.processData is false
15316
+ , data = (o.processData !== false && o.data && typeof o.data !== 'string')
15317
+ ? reqwest.toQueryString(o.data)
15318
+ : (o.data || null)
15319
+ , http
15320
+
15321
+ // if we're working on a GET request and we have data then we should append
15322
+ // query string to end of URL and not post data
15323
+ if ((o.type == 'jsonp' || method == 'GET') && data) {
15324
+ url = urlappend(url, data)
15325
+ data = null
15326
+ }
15327
+
15328
+ if (o.type == 'jsonp') return handleJsonp(o, fn, err, url)
15329
+
15330
+ http = xhr()
15331
+ http.open(method, url, true)
15332
+ setHeaders(http, o)
15333
+ http.onreadystatechange = handleReadyState(http, fn, err)
15334
+ o.before && o.before(http)
15335
+ http.send(data)
15336
+ return http
15337
+ }
15338
+
15339
+ function Reqwest(o, fn) {
15340
+ this.o = o
15341
+ this.fn = fn
15342
+ init.apply(this, arguments)
15343
+ }
15344
+
15345
+ function setType(url) {
15346
+ var m = url.match(/\.(json|jsonp|html|xml)(\?|$)/)
15347
+ return m ? m[1] : 'js'
15348
+ }
15349
+
15350
+ function init(o, fn) {
15351
+ this.url = typeof o == 'string' ? o : o.url
15352
+ this.timeout = null
15353
+ var type = o.type || setType(this.url)
15354
+ , self = this
15355
+ fn = fn || function () {}
15356
+
15357
+ if (o.timeout) {
15358
+ this.timeout = setTimeout(function () {
15359
+ self.abort()
15360
+ }, o.timeout)
15361
+ }
15362
+
15363
+ function complete(resp) {
15364
+ o.timeout && clearTimeout(self.timeout)
15365
+ self.timeout = null
15366
+ o.complete && o.complete(resp)
15367
+ }
15368
+
15369
+ function success(resp) {
15370
+ var r = resp.responseText
15371
+ if (r) {
15372
+ switch (type) {
15373
+ case 'json':
15374
+ try {
15375
+ resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')')
15376
+ } catch (err) {
15377
+ return error(resp, 'Could not parse JSON in response', err)
15378
+ }
15379
+ break;
15380
+ case 'js':
15381
+ resp = eval(r)
15382
+ break;
15383
+ case 'html':
15384
+ resp = r
15385
+ break;
15386
+ }
15387
+ }
15388
+
15389
+ fn(resp)
15390
+ o.success && o.success(resp)
15391
+
15392
+ complete(resp)
15393
+ }
15394
+
15395
+ function error(resp, msg, t) {
15396
+ o.error && o.error(resp, msg, t)
15397
+ complete(resp)
15398
+ }
15399
+
15400
+ this.request = getRequest(o, success, error)
15401
+ }
15402
+
15403
+ Reqwest.prototype = {
15404
+ abort: function () {
15405
+ this.request.abort()
15406
+ }
15407
+
15408
+ , retry: function () {
15409
+ init.call(this, this.o, this.fn)
15410
+ }
15411
+ }
15412
+
15413
+ function reqwest(o, fn) {
15414
+ return new Reqwest(o, fn)
15415
+ }
15416
+
15417
+ // normalize newline variants according to spec -> CRLF
15418
+ function normalize(s) {
15419
+ return s ? s.replace(/\r?\n/g, '\r\n') : ''
15420
+ }
15421
+
15422
+ function serial(el, cb) {
15423
+ var n = el.name
15424
+ , t = el.tagName.toLowerCase()
15425
+ , optCb = function(o) {
15426
+ // IE gives value="" even where there is no value attribute
15427
+ // 'specified' ref: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-862529273
15428
+ if (o && !o.disabled)
15429
+ cb(n, normalize(o.attributes.value && o.attributes.value.specified ? o.value : o.text))
15430
+ }
15431
+
15432
+ // don't serialize elements that are disabled or without a name
15433
+ if (el.disabled || !n) return;
15434
+
15435
+ switch (t) {
15436
+ case 'input':
15437
+ if (!/reset|button|image|file/i.test(el.type)) {
15438
+ var ch = /checkbox/i.test(el.type)
15439
+ , ra = /radio/i.test(el.type)
15440
+ , val = el.value;
15441
+ // WebKit gives us "" instead of "on" if a checkbox has no value, so correct it here
15442
+ (!(ch || ra) || el.checked) && cb(n, normalize(ch && val === '' ? 'on' : val))
15443
+ }
15444
+ break;
15445
+ case 'textarea':
15446
+ cb(n, normalize(el.value))
15447
+ break;
15448
+ case 'select':
15449
+ if (el.type.toLowerCase() === 'select-one') {
15450
+ optCb(el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null)
15451
+ } else {
15452
+ for (var i = 0; el.length && i < el.length; i++) {
15453
+ el.options[i].selected && optCb(el.options[i])
15454
+ }
15455
+ }
15456
+ break;
15457
+ }
15458
+ }
15459
+
15460
+ // collect up all form elements found from the passed argument elements all
15461
+ // the way down to child elements; pass a '<form>' or form fields.
15462
+ // called with 'this'=callback to use for serial() on each element
15463
+ function eachFormElement() {
15464
+ var cb = this
15465
+ , e, i, j
15466
+ , serializeSubtags = function(e, tags) {
15467
+ for (var i = 0; i < tags.length; i++) {
15468
+ var fa = e[byTag](tags[i])
15469
+ for (j = 0; j < fa.length; j++) serial(fa[j], cb)
15470
+ }
15471
+ }
15472
+
15473
+ for (i = 0; i < arguments.length; i++) {
15474
+ e = arguments[i]
15475
+ if (/input|select|textarea/i.test(e.tagName)) serial(e, cb)
15476
+ serializeSubtags(e, [ 'input', 'select', 'textarea' ])
15477
+ }
15478
+ }
15479
+
15480
+ // standard query string style serialization
15481
+ function serializeQueryString() {
15482
+ return reqwest.toQueryString(reqwest.serializeArray.apply(null, arguments))
15483
+ }
15484
+
15485
+ // { 'name': 'value', ... } style serialization
15486
+ function serializeHash() {
15487
+ var hash = {}
15488
+ eachFormElement.apply(function (name, value) {
15489
+ if (name in hash) {
15490
+ hash[name] && !isArray(hash[name]) && (hash[name] = [hash[name]])
15491
+ hash[name].push(value)
15492
+ } else hash[name] = value
15493
+ }, arguments)
15494
+ return hash
15495
+ }
15496
+
15497
+ // [ { name: 'name', value: 'value' }, ... ] style serialization
15498
+ reqwest.serializeArray = function () {
15499
+ var arr = []
15500
+ eachFormElement.apply(function(name, value) {
15501
+ arr.push({name: name, value: value})
15502
+ }, arguments)
15503
+ return arr
15504
+ }
15505
+
15506
+ reqwest.serialize = function () {
15507
+ if (arguments.length === 0) return ''
15508
+ var opt, fn
15509
+ , args = Array.prototype.slice.call(arguments, 0)
15510
+
15511
+ opt = args.pop()
15512
+ opt && opt.nodeType && args.push(opt) && (opt = null)
15513
+ opt && (opt = opt.type)
15514
+
15515
+ if (opt == 'map') fn = serializeHash
15516
+ else if (opt == 'array') fn = reqwest.serializeArray
15517
+ else fn = serializeQueryString
15518
+
15519
+ return fn.apply(null, args)
15520
+ }
15521
+
15522
+ reqwest.toQueryString = function (o) {
15523
+ var qs = '', i
15524
+ , enc = encodeURIComponent
15525
+ , push = function (k, v) {
15526
+ qs += enc(k) + '=' + enc(v) + '&'
15527
+ }
15528
+
15529
+ if (isArray(o)) {
15530
+ for (i = 0; o && i < o.length; i++) push(o[i].name, o[i].value)
15531
+ } else {
15532
+ for (var k in o) {
15533
+ if (!Object.hasOwnProperty.call(o, k)) continue;
15534
+ var v = o[k]
15535
+ if (isArray(v)) {
15536
+ for (i = 0; i < v.length; i++) push(k, v[i])
15537
+ } else push(k, o[k])
15538
+ }
15539
+ }
15540
+
15541
+ // spaces should be + according to spec
15542
+ return qs.replace(/&$/, '').replace(/%20/g,'+')
15543
+ }
15544
+
15545
+ // jQuery and Zepto compatibility, differences can be remapped here so you can call
15546
+ // .ajax.compat(options, callback)
15547
+ reqwest.compat = function (o, fn) {
15548
+ if (o) {
15549
+ o.type && (o.method = o.type) && delete o.type
15550
+ o.dataType && (o.type = o.dataType)
15551
+ o.jsonpCallback && (o.jsonpCallbackName = o.jsonpCallback) && delete o.jsonpCallback
15552
+ o.jsonp && (o.jsonpCallback = o.jsonp)
15553
+ }
15554
+ return new Reqwest(o, fn)
15555
+ }
15556
+
15557
+ return reqwest
15558
+ });
15559
+
15560
+ (function() {
15561
+ var SAFARI_CONTAINS_IS_BROKEN, version;
15562
+
15563
+ if (/Safari/.test(navigator.userAgent)) {
15564
+ version = /WebKit\/(\S+)/.exec(navigator.userAgent);
15565
+ if (version && parseFloat(version) < 540) {
15566
+ SAFARI_CONTAINS_IS_BROKEN = true;
15567
+ }
15568
+ }
15569
+
15570
+ (typeof window !== "undefined" && window !== null ? window : global).containsNode = function(parent, child) {
15571
+ if (parent === child) {
15572
+ return true;
15573
+ }
15574
+ if (parent.contains && !SAFARI_CONTAINS_IS_BROKEN) {
15575
+ return parent.contains(child);
15576
+ }
15577
+ if (parent.compareDocumentPosition) {
15578
+ return !!(parent.compareDocumentPosition(child) & 16);
15579
+ }
15580
+ while (child && parent !== child) {
15581
+ child = child.parentNode;
15582
+ }
15583
+ return child === parent;
15584
+ };
15585
+
15586
+ }).call(this);
15587
+
15588
+ (function() {
15589
+ Batman.extend(Batman.DOM, {
15590
+ querySelectorAll: function(node, selector) {
15591
+ return zest(selector, node);
15592
+ },
15593
+ querySelector: function(node, selector) {
15594
+ return zest(selector, node)[0];
15595
+ },
15596
+ setInnerHTML: function(node, html) {
15597
+ return node != null ? node.innerHTML = html : void 0;
15598
+ },
15599
+ containsNode: function(parent, child) {
15600
+ if (!child) {
15601
+ child = parent;
15602
+ parent = document.body;
15603
+ }
15604
+ return window.containsNode(parent, child);
15605
+ },
15606
+ textContent: function(node) {
15607
+ var _ref;
15608
+ return (_ref = node.textContent) != null ? _ref : node.innerText;
15609
+ },
15610
+ destroyNode: function(node) {
15611
+ var _ref;
15612
+ Batman.DOM.cleanupNode(node);
15613
+ return node != null ? (_ref = node.parentNode) != null ? _ref.removeChild(node) : void 0 : void 0;
15614
+ }
15615
+ });
15616
+
15617
+ Batman.extend(Batman.Request.prototype, {
15618
+ _parseResponseHeaders: function(xhr) {
15619
+ var headers;
15620
+ return headers = xhr.getAllResponseHeaders().split('\n').reduce(function(acc, header) {
15621
+ var key, matches, value;
15622
+ if (matches = header.match(/([^:]*):\s*(.*)/)) {
15623
+ key = matches[1];
15624
+ value = matches[2];
15625
+ acc[key] = value;
15626
+ }
15627
+ return acc;
15628
+ }, {});
15629
+ },
15630
+ send: function(data) {
15631
+ var options, xhr, _ref,
15632
+ _this = this;
15633
+ if (data == null) {
15634
+ data = this.get('data');
15635
+ }
15636
+ this.fire('loading');
15637
+ options = {
15638
+ url: this.get('url'),
15639
+ method: this.get('method'),
15640
+ type: this.get('type'),
15641
+ headers: this.get('headers'),
15642
+ success: function(response) {
15643
+ _this.mixin({
15644
+ xhr: xhr,
15645
+ response: response,
15646
+ status: typeof xhr !== "undefined" && xhr !== null ? xhr.status : void 0,
15647
+ responseHeaders: _this._parseResponseHeaders(xhr)
15648
+ });
15649
+ return _this.fire('success', response);
15650
+ },
15651
+ error: function(xhr) {
15652
+ _this.mixin({
15653
+ xhr: xhr,
15654
+ response: xhr.responseText || xhr.content,
15655
+ status: xhr.status,
15656
+ responseHeaders: _this._parseResponseHeaders(xhr)
15657
+ });
15658
+ xhr.request = _this;
15659
+ return _this.fire('error', xhr);
15660
+ },
15661
+ complete: function() {
15662
+ return _this.fire('loaded');
15663
+ }
15664
+ };
15665
+ if ((_ref = options.method) === 'PUT' || _ref === 'POST') {
15666
+ if (this.hasFileUploads()) {
15667
+ options.data = this.constructor.objectToFormData(data);
15668
+ } else {
15669
+ options.contentType = this.get('contentType');
15670
+ options.data = Batman.URI.queryFromParams(data);
15671
+ }
15672
+ } else {
15673
+ options.data = data;
15674
+ }
15675
+ return xhr = (reqwest(options)).request;
15676
+ }
15677
+ });
15678
+
15679
+ }).call(this);
15680
+
15681
+ (function() {
15682
+
15683
+
13243
15684
  }).call(this);