summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Beauchamp <daniel.beauchamp@shopify.com>2012-10-30 05:16:35 -0400
committerDaniel Beauchamp <daniel.beauchamp@shopify.com>2012-10-30 05:16:35 -0400
commita4a4564f760bb1bcd541366186cd46488d5a569b (patch)
tree3737ae6d233464482482880bd858665866686f10
parent3a77d19282f2055559c63468c574b824138464ac (diff)
Added new widgets, and made them more flexible. Ready for 0.1.3!
-rw-r--r--README.md191
-rw-r--r--dashing.gemspec10
-rw-r--r--javascripts/batman.jquery.js52
-rw-r--r--javascripts/batman.js4970
-rw-r--r--javascripts/dashing.coffee2
-rw-r--r--templates/dashboard/%name%.erb.tt1
-rw-r--r--templates/project/Gemfile2
-rw-r--r--templates/project/README.md1
-rw-r--r--templates/project/assets/images/logo.pngbin0 -> 68147 bytes
-rw-r--r--templates/project/assets/javascripts/application.coffee12
-rw-r--r--templates/project/assets/stylesheets/application.scss40
-rw-r--r--templates/project/dashboards/sample.erb6
-rw-r--r--templates/project/dashboards/sampletv.erb56
-rw-r--r--templates/project/jobs/buzzwords.rb2
-rw-r--r--templates/project/jobs/convergence.rb1
-rw-r--r--templates/project/jobs/sample.rb4
-rw-r--r--templates/project/jobs/twitter.rb17
-rw-r--r--templates/project/public/.empty_directory1
-rw-r--r--templates/project/widgets/clock/clock.coffee18
-rw-r--r--templates/project/widgets/clock/clock.html2
-rw-r--r--templates/project/widgets/clock/clock.scss13
-rw-r--r--templates/project/widgets/comments/comments.coffee24
-rw-r--r--templates/project/widgets/comments/comments.html7
-rw-r--r--templates/project/widgets/comments/comments.scss33
-rw-r--r--templates/project/widgets/graph/graph.coffee14
-rw-r--r--templates/project/widgets/graph/graph.html4
-rw-r--r--templates/project/widgets/graph/graph.scss36
-rw-r--r--templates/project/widgets/iframe/iframe.coffee9
-rw-r--r--templates/project/widgets/iframe/iframe.html1
-rw-r--r--templates/project/widgets/iframe/iframe.scss8
-rw-r--r--templates/project/widgets/image/image.coffee9
-rw-r--r--templates/project/widgets/image/image.html1
-rw-r--r--templates/project/widgets/image/image.scss13
-rw-r--r--templates/project/widgets/list/list.coffee15
-rw-r--r--templates/project/widgets/list/list.html5
-rw-r--r--templates/project/widgets/list/list.scss14
-rw-r--r--templates/project/widgets/meter/meter.html4
-rw-r--r--templates/project/widgets/meter/meter.scss20
-rw-r--r--templates/project/widgets/number/number.coffee8
-rw-r--r--templates/project/widgets/number/number.html10
-rw-r--r--templates/project/widgets/number/number.scss14
-rw-r--r--templates/project/widgets/text/text.html6
-rw-r--r--templates/project/widgets/text/text.scss17
43 files changed, 3186 insertions, 2487 deletions
diff --git a/README.md b/README.md
index 13b00c7..cdcc722 100644
--- a/README.md
+++ b/README.md
@@ -1,190 +1 @@
-# Dashing!
-
-A handsome dashboard framework solution
-
-## Introduction
-
-Dashing is a framework for building web-based dashboards.
-
-Features:
-
- - Custom widgets! Built using whatever HTML/Coffeescript wizardry you posses
- - Multiple dashboards! You can have many different views all hosted in the same location
- - Shared widgets! It's easy to have have the same widget show up on different dashboards
- - Push or pull data, you decide!
- - Responsive grid layout! Your dashboard will look good on any sized screen
-
-## Installation and Setup
-
- 1. Install the gem from the command line:
-
- ```gem install dashing```
-
- 2. Generate a new project:
-
- ```dashing new sweet_dashboard_project```
-
- 3. Change your directory to ```sweet_dashboard_project``` and start Dashing
-
- ```dashing start```
-
- 4. Point your browser at [localhost:3000](http://localhost:3000)
-
-## Building a dashboard
-
-```main.erb``` contains the layout for the default dashboard which is accessible at ```/```.
-You can add additional dashboards with by running ```dashing COMMAND THINGY new_view``` which creates a ```new_view.erb``` file in ```dashboards/```.
-That new view will be accessible at ```localhost:3000/new_view```
-
-## Widgets
-
-Widgets are represented by a ```div``` element with ```data-id``` and ```data-view``` attributes. eg:
-
-```HTML
-<div data-id="sample" data-view="SweetWidget"></div>
-```
-
-The ```data-id``` attribute is used to set the **widget ID** which will be used when to push data to the widget. Two widgets can have the same widget id, allowing you to have the same widget in multiple dashboards.
-
-```data-view``` specifies the type of widget what will be used. This field is case sensitive and must match the coffeescript class of the widget. See making your own widget section for more details.
-
-This ```<div>``` can also be used to configure your widgets. For example, the pre-bundled widgets let you set a title with ```data-title="Widget Title"```.
-
-### Layout
-
-Getting the style and layout right when you have multiple widgets is hard, that's why we've done it for you. By default Dashing uses [masonry](http://masonry.desandro.com/) to produce a grid layout. If it can, your dashboard will fill the screen with 5 columns. If there isn't enough room though, widgets will be reorganized to fit into fewer columns until you are left with a single column
-
-Examples here?
-
-Masonry requires that your widgets be contained within a ```<ul>``` element as follows:
-
-```HTML
-<ul class="list-nostyle clearfix">
- <li class="widget-container">
- <div data-id="widget_id1" data-view="MyWidget"></div>
- </li>
- <li class="widget-container">
- <div data-id="widget_id2" data-view="MyWidget"></div>
- </li>
- <li class="widget-container">
- <div data-id="widget_id3" data-view="MyWidget"></div>
- </li>
-</ul>
-```
-
-### Making you own widget
-
-A widget consists of three parts:
-
- - an html file used for layout and bindings
- - a scss file for style
- - a coffeescript file which allows you to operate on the data
-
-To make your own run ```dashing generate sweet_widget``` which will create scaffolding files in the ```widget/``` folder or your project.
-
-#### sweet_widget.html
-
-Contains the HTML for you widget.
-We use [batman bindings](http://batmanjs.org/docs/batman.html#batman-view-bindings-how-to-use-bindings) in order to update the content of a widget.
-In the example below, updating the title attribute of the coffeescript object representing that widget will set the innerHTML of the ```<h1>``` element.
-Dashing provides a simple way to update your widgets attributes through a push interface and a pull interface. See the Getting Data into Dashing section.
-
-##### Example
-```html
-<h1 data-bind="title"></h1>
-
-<h3 data-bind="text"></h3>
-````
-
-#### sweet_widget.coffee
-
-This coffee script file allows you to perform any operations you wish on your widget. In the example below we can initialize things with the constructor method.
-We can also manipulate the data we recieve from data updates. Data will be the JSON object you pass in.
-
-##### Example
-```coffeescript
-class Dashing.SweetWidget extends Dashing.Widget
-
- constructor: ->
- super
- @set('attr', 'wooo')
-
- onData: (data) ->
- super
- @set('cool_thing', data.massage.split(',')[2]
-```
-
-#### sweet_widget.scss
-
-##### Example
-````scss
-$text_value-color: #fff;
-$text_title-color: lighten($widget-text-color, 30%);
-
-.widget-text {
- .title {
- color: $text_title-color;
- }
- .p {
- color: $text_value-color:
- }
-}
-```
-
-## Getting data into Dashing
-
-Providing data to widgets is easy. You specify which widget you want using a widget id. Dashing expects the data you send to be in JSON format.
-Upon getting data, dashing mixes the json into the widget object. So it's easy to update multiple attributes within the same object.
-
-### Jobs (poll)
-
-Dashing uses [rufus-scheduler](http://rufus.rubyforge.org/rufus-scheduler/) to schedule jobs.
-You can make a new job with ```dashing job super_job``` which will create a file in the jobs folder called ```super_job.rb```.
-Data is sent to a widget using the ```send_event(widget_id, json_formatted_data)``` method.
-
-#### Example
-
-```ruby
-# :first_in sets how long it takes before the job is first run. In this case, it is run immediately
-SCHEDULER.every '1m', :first_in => 0 do |job|
- send_event('widget_id', {text: "I am #{%w(happy sad hungry).sample}"})
-end
-```
-
-### Push
-
-You can also push data directly to your dashboard! Post the data you want in json to ```/widgets/widget_id```.
-For security, you will also have to include your auth_token (which can be found in ```config.ru```) as part of the json object.
-
-#### Example
-```bash
-curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "value": 100 }' http://localhost:3000/widgets/synergy
-```
-
-or
-
-```ruby
-HTTParty.post('http://ADDRESS/widgets/widget_id',
- :body => { auth_token: "YOUR_AUTH_TOKEN", text: "Weeeeee"}.to_json)
-```
-
-## Misc
-
-### Deploying to heroku
-
-### Using omni-auth
-
-## Dependencies
-
- - [Sinatra](http://www.sinatrarb.com/)
- - [batman.js](http://batmanjs.org/)
- - [rufus-scheduler](http://rufus.rubyforge.org/rufus-scheduler/)
- - [Thor](https://github.com/wycats/thor/)
- - [jQuery-knob](http://anthonyterrien.com/knob/)
- - [masonry](http://masonry.desandro.com/)
- - [thin](http://code.macournoyer.com/thin/)
- - [Sass](http://sass-lang.com/)
-
-## Licensing
-
-This code is released under the MIT license. See ```MIT-LICENSE``` file for more details \ No newline at end of file
+Check out the [Homepage](http://shopify.github.com/dashing) \ No newline at end of file
diff --git a/dashing.gemspec b/dashing.gemspec
index 5bacdfa..610de20 100644
--- a/dashing.gemspec
+++ b/dashing.gemspec
@@ -1,16 +1,16 @@
Gem::Specification.new do |s|
s.name = 'dashing'
- s.version = '0.1.0'
- s.date = '2012-07-24'
+ s.version = '0.1.3'
+ s.date = '2012-10-30'
s.executables << 'dashing'
- s.summary = "A simple & flexible framework for creating dashboards."
- s.description = "An elegant, simple, beautiful, & flexible framework for creating dashboards."
+ s.summary = "The exceptionally handsome dashboard framework."
+ s.description = "This framework lets you build & easily layout dashboards with your own custom widgets. Use it to make a status boards for your ops team, or use it to track signups, conversion rates, or whatever else metrics you'd like to see in one spot. Included with the framework are ready-made widgets for you to use or customize. All of this code was extracted out of a project at Shopify that displays dashboards on TVs around the office."
s.authors = ["Daniel Beauchamp"]
s.email = 'daniel.beauchamp@shopify.com'
s.files = ["lib/Dashing.rb"]
- s.homepage = 'http://Dashing.shopify.com'
+ s.homepage = 'http://shopify.github.com/dashing'
s.files = Dir['README.md', 'javascripts/**/*', 'templates/**/*','templates/**/.[a-z]*', 'lib/**/*']
diff --git a/javascripts/batman.jquery.js b/javascripts/batman.jquery.js
index 6407098..0716b0e 100644
--- a/javascripts/batman.jquery.js
+++ b/javascripts/batman.jquery.js
@@ -1,5 +1,57 @@
(function() {
+ Batman.extend(Batman.DOM, {
+ querySelectorAll: function(node, selector) {
+ return jQuery(selector, node);
+ },
+ querySelector: function(node, selector) {
+ return jQuery(selector, node)[0];
+ },
+ setInnerHTML: function(node, html) {
+ var child, childNodes, result, _i, _j, _len, _len1;
+ childNodes = (function() {
+ var _i, _len, _ref, _results;
+ _ref = node.childNodes;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ child = _ref[_i];
+ _results.push(child);
+ }
+ return _results;
+ })();
+ for (_i = 0, _len = childNodes.length; _i < _len; _i++) {
+ child = childNodes[_i];
+ Batman.DOM.willRemoveNode(child);
+ }
+ result = jQuery(node).html(html);
+ for (_j = 0, _len1 = childNodes.length; _j < _len1; _j++) {
+ child = childNodes[_j];
+ Batman.DOM.didRemoveNode(child);
+ }
+ return result;
+ },
+ removeNode: function(node) {
+ var _ref;
+ Batman.DOM.willRemoveNode(node);
+ if ((_ref = node.parentNode) != null) {
+ _ref.removeChild(node);
+ }
+ return Batman.DOM.didRemoveNode(node);
+ },
+ destroyNode: function(node) {
+ Batman.DOM.willDestroyNode(node);
+ Batman.DOM.willRemoveNode(node);
+ jQuery(node).remove();
+ Batman.DOM.didRemoveNode(node);
+ return Batman.DOM.didDestroyNode(node);
+ },
+ appendChild: function(parent, child) {
+ Batman.DOM.willInsertNode(child);
+ jQuery(parent).append(child);
+ return Batman.DOM.didInsertNode(child);
+ }
+ });
+
Batman.Request.prototype._parseResponseHeaders = function(xhr) {
var headers;
return headers = xhr.getAllResponseHeaders().split('\n').reduce(function(acc, header) {
diff --git a/javascripts/batman.js b/javascripts/batman.js
index 22c7b3a..c4e5f34 100644
--- a/javascripts/batman.js
+++ b/javascripts/batman.js
@@ -12,10 +12,12 @@
})(Batman.Object, mixins, function(){});
};
- Batman.version = '0.10.0';
+ Batman.version = '0.13.0';
Batman.config = {
pathPrefix: '/',
+ viewPrefix: 'views',
+ fetchRemoteViews: true,
usePushState: false,
minificationErrors: true
};
@@ -47,6 +49,325 @@
}).call(this);
(function() {
+ var _implementImmediates, _objectToString,
+ __slice = [].slice,
+ __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.typeOf = function(object) {
+ if (typeof object === 'undefined') {
+ return "Undefined";
+ }
+ return _objectToString.call(object).slice(8, -1);
+ };
+
+ _objectToString = Object.prototype.toString;
+
+ Batman.extend = function() {
+ var key, object, objects, to, value, _i, _len;
+ to = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ for (_i = 0, _len = objects.length; _i < _len; _i++) {
+ object = objects[_i];
+ for (key in object) {
+ value = object[key];
+ to[key] = value;
+ }
+ }
+ return to;
+ };
+
+ Batman.mixin = function() {
+ var hasSet, key, mixin, mixins, to, value, _i, _len;
+ to = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ hasSet = typeof to.set === 'function';
+ for (_i = 0, _len = mixins.length; _i < _len; _i++) {
+ mixin = mixins[_i];
+ if (Batman.typeOf(mixin) !== 'Object') {
+ continue;
+ }
+ for (key in mixin) {
+ if (!__hasProp.call(mixin, key)) continue;
+ value = mixin[key];
+ if (key === 'initialize' || key === 'uninitialize' || key === 'prototype') {
+ continue;
+ }
+ if (hasSet) {
+ to.set(key, value);
+ } else if (to.nodeName != null) {
+ Batman.data(to, key, value);
+ } else {
+ to[key] = value;
+ }
+ }
+ if (typeof mixin.initialize === 'function') {
+ mixin.initialize.call(to);
+ }
+ }
+ return to;
+ };
+
+ Batman.unmixin = function() {
+ var from, key, mixin, mixins, _i, _len;
+ from = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ for (_i = 0, _len = mixins.length; _i < _len; _i++) {
+ mixin = mixins[_i];
+ for (key in mixin) {
+ if (key === 'initialize' || key === 'uninitialize') {
+ continue;
+ }
+ delete from[key];
+ }
+ if (typeof mixin.uninitialize === 'function') {
+ mixin.uninitialize.call(from);
+ }
+ }
+ return from;
+ };
+
+ Batman._functionName = Batman.functionName = function(f) {
+ var _ref;
+ if (f.__name__) {
+ return f.__name__;
+ }
+ if (f.name) {
+ return f.name;
+ }
+ return (_ref = f.toString().match(/\W*function\s+([\w\$]+)\(/)) != null ? _ref[1] : void 0;
+ };
+
+ Batman._isChildOf = Batman.isChildOf = function(parentNode, childNode) {
+ var node;
+ node = childNode.parentNode;
+ while (node) {
+ if (node === parentNode) {
+ return true;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ };
+
+ Batman.setImmediate = Batman.clearImmediate = null;
+
+ _implementImmediates = function(container) {
+ var canUsePostMessage, count, functions, getHandle, handler, prefix, tasks;
+ canUsePostMessage = function() {
+ var async, oldMessage;
+ if (!container.postMessage) {
+ return false;
+ }
+ async = true;
+ oldMessage = container.onmessage;
+ container.onmessage = function() {
+ return async = false;
+ };
+ container.postMessage("", "*");
+ container.onmessage = oldMessage;
+ return async;
+ };
+ tasks = new Batman.SimpleHash;
+ count = 0;
+ getHandle = function() {
+ return "go" + (++count);
+ };
+ if (container.setImmediate) {
+ Batman.setImmediate = container.setImmediate;
+ Batman.clearImmediate = container.clearImmediate;
+ } else if (container.msSetImmediate) {
+ Batman.setImmediate = msSetImmediate;
+ Batman.clearImmediate = msClearImmediate;
+ } else if (canUsePostMessage()) {
+ prefix = 'com.batman.';
+ functions = new Batman.SimpleHash;
+ handler = function(e) {
+ var handle, _base;
+ if (!~e.data.search(prefix)) {
+ return;
+ }
+ handle = e.data.substring(prefix.length);
+ return typeof (_base = tasks.unset(handle)) === "function" ? _base() : void 0;
+ };
+ if (container.addEventListener) {
+ container.addEventListener('message', handler, false);
+ } else {
+ container.attachEvent('onmessage', handler);
+ }
+ Batman.setImmediate = function(f) {
+ var handle;
+ tasks.set(handle = getHandle(), f);
+ container.postMessage(prefix + handle, "*");
+ return handle;
+ };
+ Batman.clearImmediate = function(handle) {
+ return tasks.unset(handle);
+ };
+ } else if (typeof document !== 'undefined' && __indexOf.call(document.createElement("script"), "onreadystatechange") >= 0) {
+ Batman.setImmediate = function(f) {
+ var handle, script;
+ handle = getHandle();
+ script = document.createElement("script");
+ script.onreadystatechange = function() {
+ var _base;
+ if (typeof (_base = tasks.get(handle)) === "function") {
+ _base();
+ }
+ script.onreadystatechange = null;
+ script.parentNode.removeChild(script);
+ return script = null;
+ };
+ document.documentElement.appendChild(script);
+ return handle;
+ };
+ Batman.clearImmediate = function(handle) {
+ return tasks.unset(handle);
+ };
+ } else if (typeof process !== "undefined" && process !== null ? process.nextTick : void 0) {
+ functions = {};
+ Batman.setImmediate = function(f) {
+ var handle;
+ handle = getHandle();
+ functions[handle] = f;
+ process.nextTick(function() {
+ if (typeof functions[handle] === "function") {
+ functions[handle]();
+ }
+ return delete functions[handle];
+ });
+ return handle;
+ };
+ Batman.clearImmediate = function(handle) {
+ return delete functions[handle];
+ };
+ } else {
+ Batman.setImmediate = function(f) {
+ return setTimeout(f, 0);
+ };
+ Batman.clearImmediate = function(handle) {
+ return clearTimeout(handle);
+ };
+ }
+ Batman.setImmediate = Batman.setImmediate;
+ return Batman.clearImmediate = Batman.clearImmediate;
+ };
+
+ Batman.setImmediate = function() {
+ _implementImmediates(Batman.container);
+ return Batman.setImmediate.apply(this, arguments);
+ };
+
+ Batman.clearImmediate = function() {
+ _implementImmediates(Batman.container);
+ return Batman.clearImmediate.apply(this, arguments);
+ };
+
+ Batman.forEach = function(container, iterator, ctx) {
+ var e, i, k, v, _i, _len, _results, _results1;
+ if (container.forEach) {
+ return container.forEach(iterator, ctx);
+ } else if (container.indexOf) {
+ _results = [];
+ for (i = _i = 0, _len = container.length; _i < _len; i = ++_i) {
+ e = container[i];
+ _results.push(iterator.call(ctx, e, i, container));
+ }
+ return _results;
+ } else {
+ _results1 = [];
+ for (k in container) {
+ v = container[k];
+ _results1.push(iterator.call(ctx, k, v, container));
+ }
+ return _results1;
+ }
+ };
+
+ Batman.objectHasKey = function(object, key) {
+ if (typeof object.hasKey === 'function') {
+ return object.hasKey(key);
+ } else {
+ return key in object;
+ }
+ };
+
+ Batman.contains = function(container, item) {
+ if (container.indexOf) {
+ return __indexOf.call(container, item) >= 0;
+ } else if (typeof container.has === 'function') {
+ return container.has(item);
+ } else {
+ return Batman.objectHasKey(container, item);
+ }
+ };
+
+ Batman.get = function(base, key) {
+ if (typeof base.get === 'function') {
+ return base.get(key);
+ } else {
+ return Batman.Property.forBaseAndKey(base, key).getValue();
+ }
+ };
+
+ Batman.getPath = function(base, segments) {
+ var segment, _i, _len;
+ for (_i = 0, _len = segments.length; _i < _len; _i++) {
+ segment = segments[_i];
+ if (base != null) {
+ base = Batman.get(base, segment);
+ if (base == null) {
+ return base;
+ }
+ } else {
+ return void 0;
+ }
+ }
+ return base;
+ };
+
+ Batman.escapeHTML = (function() {
+ var replacements;
+ replacements = {
+ "&": "&amp;",
+ "<": "&lt;",
+ ">": "&gt;",
+ "\"": "&#34;",
+ "'": "&#39;"
+ };
+ return function(s) {
+ return ("" + s).replace(/[&<>'"]/g, function(c) {
+ return replacements[c];
+ });
+ };
+ })();
+
+ Batman.translate = function(x, values) {
+ if (values == null) {
+ values = {};
+ }
+ return Batman.helpers.interpolate(Batman.get(Batman.translate.messages, x), values);
+ };
+
+ Batman.translate.messages = {};
+
+ Batman.t = function() {
+ return Batman.translate.apply(Batman, arguments);
+ };
+
+ Batman.redirect = function(url) {
+ var _ref;
+ return (_ref = Batman.navigator) != null ? _ref.redirect(url) : void 0;
+ };
+
+ Batman.initializeObject = function(object) {
+ if (object._batman != null) {
+ return object._batman.check(object);
+ } else {
+ return object._batman = new Batman._Batman(object);
+ }
+ };
+
+}).call(this);
+
+(function() {
var __slice = [].slice,
__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; };
@@ -370,325 +691,6 @@
}).call(this);
(function() {
- var _implementImmediates, _objectToString,
- __slice = [].slice,
- __hasProp = {}.hasOwnProperty,
- __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; };
-
- Batman.typeOf = function(object) {
- if (typeof object === 'undefined') {
- return "Undefined";
- }
- return _objectToString.call(object).slice(8, -1);
- };
-
- _objectToString = Object.prototype.toString;
-
- Batman.extend = function() {
- var key, object, objects, to, value, _i, _len;
- to = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
- for (_i = 0, _len = objects.length; _i < _len; _i++) {
- object = objects[_i];
- for (key in object) {
- value = object[key];
- to[key] = value;
- }
- }
- return to;
- };
-
- Batman.mixin = function() {
- var hasSet, key, mixin, mixins, to, value, _i, _len;
- to = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
- hasSet = typeof to.set === 'function';
- for (_i = 0, _len = mixins.length; _i < _len; _i++) {
- mixin = mixins[_i];
- if (Batman.typeOf(mixin) !== 'Object') {
- continue;
- }
- for (key in mixin) {
- if (!__hasProp.call(mixin, key)) continue;
- value = mixin[key];
- if (key === 'initialize' || key === 'uninitialize' || key === 'prototype') {
- continue;
- }
- if (hasSet) {
- to.set(key, value);
- } else if (to.nodeName != null) {
- Batman.data(to, key, value);
- } else {
- to[key] = value;
- }
- }
- if (typeof mixin.initialize === 'function') {
- mixin.initialize.call(to);
- }
- }
- return to;
- };
-
- Batman.unmixin = function() {
- var from, key, mixin, mixins, _i, _len;
- from = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
- for (_i = 0, _len = mixins.length; _i < _len; _i++) {
- mixin = mixins[_i];
- for (key in mixin) {
- if (key === 'initialize' || key === 'uninitialize') {
- continue;
- }
- delete from[key];
- }
- if (typeof mixin.uninitialize === 'function') {
- mixin.uninitialize.call(from);
- }
- }
- return from;
- };
-
- Batman._functionName = Batman.functionName = function(f) {
- var _ref;
- if (f.__name__) {
- return f.__name__;
- }
- if (f.name) {
- return f.name;
- }
- return (_ref = f.toString().match(/\W*function\s+([\w\$]+)\(/)) != null ? _ref[1] : void 0;
- };
-
- Batman._isChildOf = Batman.isChildOf = function(parentNode, childNode) {
- var node;
- node = childNode.parentNode;
- while (node) {
- if (node === parentNode) {
- return true;
- }
- node = node.parentNode;
- }
- return false;
- };
-
- Batman.setImmediate = Batman.clearImmediate = null;
-
- _implementImmediates = function(container) {
- var canUsePostMessage, count, functions, getHandle, handler, prefix, tasks;
- canUsePostMessage = function() {
- var async, oldMessage;
- if (!container.postMessage) {
- return false;
- }
- async = true;
- oldMessage = container.onmessage;
- container.onmessage = function() {
- return async = false;
- };
- container.postMessage("", "*");
- container.onmessage = oldMessage;
- return async;
- };
- tasks = new Batman.SimpleHash;
- count = 0;
- getHandle = function() {
- return "go" + (++count);
- };
- if (container.setImmediate) {
- Batman.setImmediate = container.setImmediate;
- Batman.clearImmediate = container.clearImmediate;
- } else if (container.msSetImmediate) {
- Batman.setImmediate = msSetImmediate;
- Batman.clearImmediate = msClearImmediate;
- } else if (canUsePostMessage()) {
- prefix = 'com.batman.';
- functions = new Batman.SimpleHash;
- handler = function(e) {
- var handle, _base;
- if (!~e.data.search(prefix)) {
- return;
- }
- handle = e.data.substring(prefix.length);
- return typeof (_base = tasks.unset(handle)) === "function" ? _base() : void 0;
- };
- if (container.addEventListener) {
- container.addEventListener('message', handler, false);
- } else {
- container.attachEvent('onmessage', handler);
- }
- Batman.setImmediate = function(f) {
- var handle;
- tasks.set(handle = getHandle(), f);
- container.postMessage(prefix + handle, "*");
- return handle;
- };
- Batman.clearImmediate = function(handle) {
- return tasks.unset(handle);
- };
- } else if (typeof document !== 'undefined' && __indexOf.call(document.createElement("script"), "onreadystatechange") >= 0) {
- Batman.setImmediate = function(f) {
- var handle, script;
- handle = getHandle();
- script = document.createElement("script");
- script.onreadystatechange = function() {
- var _base;
- if (typeof (_base = tasks.get(handle)) === "function") {
- _base();
- }
- script.onreadystatechange = null;
- script.parentNode.removeChild(script);
- return script = null;
- };
- document.documentElement.appendChild(script);
- return handle;
- };
- Batman.clearImmediate = function(handle) {
- return tasks.unset(handle);
- };
- } else if (typeof process !== "undefined" && process !== null ? process.nextTick : void 0) {
- functions = {};
- Batman.setImmediate = function(f) {
- var handle;
- handle = getHandle();
- functions[handle] = f;
- process.nextTick(function() {
- if (typeof functions[handle] === "function") {
- functions[handle]();
- }
- return delete functions[handle];
- });
- return handle;
- };
- Batman.clearImmediate = function(handle) {
- return delete functions[handle];
- };
- } else {
- Batman.setImmediate = function(f) {
- return setTimeout(f, 0);
- };
- Batman.clearImmediate = function(handle) {
- return clearTimeout(handle);
- };
- }
- Batman.setImmediate = Batman.setImmediate;
- return Batman.clearImmediate = Batman.clearImmediate;
- };
-
- Batman.setImmediate = function() {
- _implementImmediates(Batman.container);
- return Batman.setImmediate.apply(this, arguments);
- };
-
- Batman.clearImmediate = function() {
- _implementImmediates(Batman.container);
- return Batman.clearImmediate.apply(this, arguments);
- };
-
- Batman.forEach = function(container, iterator, ctx) {
- var e, i, k, v, _i, _len, _results, _results1;
- if (container.forEach) {
- return container.forEach(iterator, ctx);
- } else if (container.indexOf) {
- _results = [];
- for (i = _i = 0, _len = container.length; _i < _len; i = ++_i) {
- e = container[i];
- _results.push(iterator.call(ctx, e, i, container));
- }
- return _results;
- } else {
- _results1 = [];
- for (k in container) {
- v = container[k];
- _results1.push(iterator.call(ctx, k, v, container));
- }
- return _results1;
- }
- };
-
- Batman.objectHasKey = function(object, key) {
- if (typeof object.hasKey === 'function') {
- return object.hasKey(key);
- } else {
- return key in object;
- }
- };
-
- Batman.contains = function(container, item) {
- if (container.indexOf) {
- return __indexOf.call(container, item) >= 0;
- } else if (typeof container.has === 'function') {
- return container.has(item);
- } else {
- return Batman.objectHasKey(container, item);
- }
- };
-
- Batman.get = function(base, key) {
- if (typeof base.get === 'function') {
- return base.get(key);
- } else {
- return Batman.Property.forBaseAndKey(base, key).getValue();
- }
- };
-
- Batman.getPath = function(base, segments) {
- var segment, _i, _len;
- for (_i = 0, _len = segments.length; _i < _len; _i++) {
- segment = segments[_i];
- if (base != null) {
- base = Batman.get(base, segment);
- if (base == null) {
- return base;
- }
- } else {
- return void 0;
- }
- }
- return base;
- };
-
- Batman.escapeHTML = (function() {
- var replacements;
- replacements = {
- "&": "&amp;",
- "<": "&lt;",
- ">": "&gt;",
- "\"": "&#34;",
- "'": "&#39;"
- };
- return function(s) {
- return ("" + s).replace(/[&<>'"]/g, function(c) {
- return replacements[c];
- });
- };
- })();
-
- Batman.translate = function(x, values) {
- if (values == null) {
- values = {};
- }
- return Batman.helpers.interpolate(Batman.get(Batman.translate.messages, x), values);
- };
-
- Batman.translate.messages = {};
-
- Batman.t = function() {
- return Batman.translate.apply(Batman, arguments);
- };
-
- Batman.redirect = function(url) {
- var _ref;
- return (_ref = Batman.navigator) != null ? _ref.redirect(url) : void 0;
- };
-
- Batman.initializeObject = function(object) {
- if (object._batman != null) {
- return object._batman.check(object);
- } else {
- return object._batman = new Batman._Batman(object);
- }
- };
-
-}).call(this);
-
-(function() {
var developer;
Batman.developer = {
@@ -1067,17 +1069,24 @@
return newEvent;
}
},
- on: function(key, handler) {
- return this.event(key).addHandler(handler);
+ on: function() {
+ var handler, key, keys, _i, _j, _len, _results;
+ keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), handler = arguments[_i++];
+ _results = [];
+ for (_j = 0, _len = keys.length; _j < _len; _j++) {
+ key = keys[_j];
+ _results.push(this.event(key).addHandler(handler));
+ }
+ return _results;
},
- once: function(key, originalHandler) {
- var event, handler;
+ once: function(key, handler) {
+ var event, handlerWrapper;
event = this.event(key);
- handler = function() {
- originalHandler.apply(this, arguments);
- return event.removeHandler(handler);
+ handlerWrapper = function() {
+ handler.apply(this, arguments);
+ return event.removeHandler(handlerWrapper);
};
- return event.addHandler(handler);
+ return event.addHandler(handlerWrapper);
},
registerAsMutableSource: function() {
return Batman.Property.registerSource(this);
@@ -2262,20 +2271,6 @@
var _ref;
return (_ref = document.getElementById(elementID)) != null ? typeof _ref.scrollIntoView === "function" ? _ref.scrollIntoView() : void 0 : void 0;
},
- querySelectorAll: (typeof window !== "undefined" && window !== null ? window.jQuery : void 0) != null ? function(node, selector) {
- return jQuery(selector, node);
- } : (typeof document !== "undefined" && document !== null ? document.querySelectorAll : void 0) != null ? function(node, selector) {
- return node.querySelectorAll(selector);
- } : function() {
- return Batman.developer.error("Please include either jQuery or a querySelectorAll polyfill, or set Batman.DOM.querySelectorAll to return an empty array.");
- },
- querySelector: (typeof window !== "undefined" && window !== null ? window.jQuery : void 0) != null ? function(node, selector) {
- return jQuery(selector, node)[0];
- } : (typeof document !== "undefined" && document !== null ? document.querySelector : void 0) != null ? function(node, selector) {
- return node.querySelector(selector);
- } : function() {
- return Batman.developer.error("Please include either jQuery or a querySelector polyfill, or set Batman.DOM.querySelector to an empty function.");
- },
partial: function(container, path, context, renderer) {
var view;
renderer.prevent('rendered');
@@ -2284,12 +2279,12 @@
context: context
});
return view.on('ready', function() {
- Batman.setInnerHTML(container, '');
- Batman.appendChild(container, view.get('node'));
+ Batman.DOM.setInnerHTML(container, '');
+ Batman.DOM.appendChild(container, view.get('node'));
return renderer.allowAndFire('rendered');
});
},
- propagateBindingEvent: Batman.propagateBindingEvent = function(binding, node) {
+ propagateBindingEvent: function(binding, node) {
var current, parentBinding, parentBindings, _i, _len;
while ((current = (current || node).parentNode)) {
parentBindings = Batman._data(current, 'bindings');
@@ -2303,21 +2298,21 @@
}
}
},
- propagateBindingEvents: Batman.propagateBindingEvents = function(newNode) {
+ propagateBindingEvents: function(newNode) {
var binding, bindings, child, _i, _j, _len, _len1, _ref;
_ref = newNode.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
- Batman.propagateBindingEvents(child);
+ Batman.DOM.propagateBindingEvents(child);
}
if (bindings = Batman._data(newNode, 'bindings')) {
for (_j = 0, _len1 = bindings.length; _j < _len1; _j++) {
binding = bindings[_j];
- Batman.propagateBindingEvent(binding, newNode);
+ Batman.DOM.propagateBindingEvent(binding, newNode);
}
}
},
- trackBinding: Batman.trackBinding = function(binding, node) {
+ trackBinding: function(binding, node) {
var bindings;
if (bindings = Batman._data(node, 'bindings')) {
bindings.push(binding);
@@ -2325,10 +2320,10 @@
Batman._data(node, 'bindings', [binding]);
}
Batman.DOM.fire('bindingAdded', binding);
- Batman.propagateBindingEvent(binding, node);
+ Batman.DOM.propagateBindingEvent(binding, node);
return true;
},
- onParseExit: Batman.onParseExit = function(node, callback) {
+ onParseExit: function(node, callback) {
var set;
set = Batman._data(node, 'onParseExit') || Batman._data(node, 'onParseExit', new Batman.SimpleSet);
if (callback != null) {
@@ -2336,7 +2331,7 @@
}
return set;
},
- forgetParseExit: Batman.forgetParseExit = function(node, callback) {
+ forgetParseExit: function(node, callback) {
return Batman.removeData(node, 'onParseExit', true);
},
defineView: function(name, node) {
@@ -2345,55 +2340,14 @@
Batman.View.store.set(Batman.Navigator.normalizePath(name), contents);
return contents;
},
- setInnerHTML: Batman.setInnerHTML = function(node, html) {
- var child, childNodes, result, _i, _j, _len, _len1;
- childNodes = (function() {
- var _i, _len, _ref, _results;
- _ref = node.childNodes;
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- child = _ref[_i];
- _results.push(child);
- }
- return _results;
- })();
- for (_i = 0, _len = childNodes.length; _i < _len; _i++) {
- child = childNodes[_i];
- Batman.DOM.willRemoveNode(child);
- }
- result = node.innerHTML = html;
- for (_j = 0, _len1 = childNodes.length; _j < _len1; _j++) {
- child = childNodes[_j];
- Batman.DOM.didRemoveNode(child);
- }
- return result;
- },
- setStyleProperty: Batman.setStyleProperty = function(node, property, value, importance) {
- if (node.style.setAttribute) {
- return node.style.setAttribute(property, value, importance);
- } else {
+ setStyleProperty: function(node, property, value, importance) {
+ if (node.style.setProperty) {
return node.style.setProperty(property, value, importance);
+ } else {
+ return node.style.setAttribute(property, value, importance);
}
},
- removeNode: Batman.removeNode = function(node) {
- var _ref;
- Batman.DOM.willRemoveNode(node);
- if ((_ref = node.parentNode) != null) {
- _ref.removeChild(node);
- }
- return Batman.DOM.didRemoveNode(node);
- },
- destroyNode: Batman.destroyNode = function(node) {
- Batman.DOM.willDestroyNode(node);
- Batman.removeNode(node);
- return Batman.DOM.didDestroyNode(node);
- },
- appendChild: Batman.appendChild = function(parent, child) {
- Batman.DOM.willInsertNode(child);
- parent.appendChild(child);
- return Batman.DOM.didInsertNode(child);
- },
- removeOrDestroyNode: Batman.removeOrDestroyNode = function(node) {
+ removeOrDestroyNode: function(node) {
var view;
view = Batman._data(node, 'view');
view || (view = Batman._data(node, 'yielder'));
@@ -2403,12 +2357,12 @@
return Batman.DOM.destroyNode(node);
}
},
- insertBefore: Batman.insertBefore = function(parentNode, newNode, referenceNode) {
+ insertBefore: function(parentNode, newNode, referenceNode) {
if (referenceNode == null) {
referenceNode = null;
}
if (!referenceNode || parentNode.childNodes.length <= 0) {
- return Batman.appendChild(parentNode, newNode);
+ return Batman.DOM.appendChild(parentNode, newNode);
} else {
Batman.DOM.willInsertNode(newNode);
parentNode.insertBefore(newNode, referenceNode);
@@ -2440,7 +2394,7 @@
break;
default:
if (isSetting) {
- return Batman.setInnerHTML(node, escapeValue ? Batman.escapeHTML(value) : value);
+ return Batman.DOM.setInnerHTML(node, escapeValue ? Batman.escapeHTML(value) : value);
} else {
return node.innerHTML;
}
@@ -2450,7 +2404,7 @@
var _ref;
return (_ref = node.nodeName.toUpperCase()) === 'INPUT' || _ref === 'TEXTAREA' || _ref === 'SELECT';
},
- addEventListener: Batman.addEventListener = function(node, eventName, callback) {
+ addEventListener: function(node, eventName, callback) {
var listeners;
if (!(listeners = Batman._data(node, 'listeners'))) {
listeners = Batman._data(node, 'listeners', {});
@@ -2459,13 +2413,13 @@
listeners[eventName] = [];
}
listeners[eventName].push(callback);
- if (Batman.hasAddEventListener) {
+ if (Batman.DOM.hasAddEventListener) {
return node.addEventListener(eventName, callback, false);
} else {
return node.attachEvent("on" + eventName, callback);
}
},
- removeEventListener: Batman.removeEventListener = function(node, eventName, callback) {
+ removeEventListener: function(node, eventName, callback) {
var eventListeners, index, listeners;
if (listeners = Batman._data(node, 'listeners')) {
if (eventListeners = listeners[eventName]) {
@@ -2475,21 +2429,21 @@
}
}
}
- if (Batman.hasAddEventListener) {
+ if (Batman.DOM.hasAddEventListener) {
return node.removeEventListener(eventName, callback, false);
} else {
return node.detachEvent('on' + eventName, callback);
}
},
- hasAddEventListener: Batman.hasAddEventListener = !!(typeof window !== "undefined" && window !== null ? window.addEventListener : void 0),
- preventDefault: Batman.preventDefault = function(e) {
+ hasAddEventListener: !!(typeof window !== "undefined" && window !== null ? window.addEventListener : void 0),
+ preventDefault: function(e) {
if (typeof e.preventDefault === "function") {
return e.preventDefault();
} else {
return e.returnValue = false;
}
},
- stopPropagation: Batman.stopPropagation = function(e) {
+ stopPropagation: function(e) {
if (e.stopPropagation) {
return e.stopPropagation();
} else {
@@ -2602,7 +2556,7 @@
for (eventName in listeners) {
eventListeners = listeners[eventName];
eventListeners.forEach(function(listener) {
- return Batman.removeEventListener(node, eventName, listener);
+ return Batman.DOM.removeEventListener(node, eventName, listener);
});
}
}
@@ -2706,7 +2660,7 @@
return true;
},
defineview: function(node, name, context, renderer) {
- Batman.onParseExit(node, function() {
+ Batman.DOM.onParseExit(node, function() {
var _ref;
return (_ref = node.parentNode) != null ? _ref.removeChild(node) : void 0;
});
@@ -2718,7 +2672,7 @@
return false;
},
"yield": function(node, key) {
- Batman.onParseExit(node, function() {
+ Batman.DOM.onParseExit(node, function() {
return Batman.DOM.Yield.withName(key).set('containerNode', node);
});
return true;
@@ -2727,7 +2681,7 @@
if (action == null) {
action = 'append';
}
- Batman.onParseExit(node, function() {
+ Batman.DOM.onParseExit(node, function() {
var _ref;
if ((_ref = node.parentNode) != null) {
_ref.removeChild(node);
@@ -2753,11 +2707,17 @@
if (eventName == null) {
eventName = 'click';
}
- Batman.addEventListener(node, eventName, function() {
- var args;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- callback.apply(null, [node].concat(__slice.call(args), [context]));
- return Batman.preventDefault(args[0]);
+ Batman.DOM.addEventListener(node, eventName, function() {
+ var args, event;
+ event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ if (event.metaKey || event.ctrlKey) {
+ return;
+ }
+ Batman.DOM.preventDefault(event);
+ if (!Batman.DOM.eventIsAllowed(eventName, event)) {
+ return;
+ }
+ return callback.apply(null, [node, event].concat(__slice.call(args), [context]));
});
if (node.nodeName.toUpperCase() === 'A' && !node.href) {
node.href = '#';
@@ -2795,7 +2755,7 @@
_results = [];
for (_i = 0, _len = eventNames.length; _i < _len; _i++) {
eventName = eventNames[_i];
- _results.push(Batman.addEventListener(node, eventName, function() {
+ _results.push(Batman.DOM.addEventListener(node, eventName, function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
return callback.apply(null, [node].concat(__slice.call(args), [context]));
@@ -2809,36 +2769,36 @@
},
submit: function(node, callback, context) {
if (Batman.DOM.nodeIsEditable(node)) {
- Batman.addEventListener(node, 'keydown', function() {
+ Batman.DOM.addEventListener(node, 'keydown', function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
if (Batman.DOM.events.isEnter(args[0])) {
return Batman.DOM._keyCapturingNode = node;
}
});
- Batman.addEventListener(node, 'keyup', function() {
+ Batman.DOM.addEventListener(node, 'keyup', function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
if (Batman.DOM.events.isEnter(args[0])) {
if (Batman.DOM._keyCapturingNode === node) {
- Batman.preventDefault(args[0]);
+ Batman.DOM.preventDefault(args[0]);
callback.apply(null, [node].concat(__slice.call(args), [context]));
}
return Batman.DOM._keyCapturingNode = null;
}
});
} else {
- Batman.addEventListener(node, 'submit', function() {
+ Batman.DOM.addEventListener(node, 'submit', function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- Batman.preventDefault(args[0]);
+ Batman.DOM.preventDefault(args[0]);
return callback.apply(null, [node].concat(__slice.call(args), [context]));
});
}
return node;
},
other: function(node, eventName, callback, context) {
- return Batman.addEventListener(node, eventName, function() {
+ return Batman.DOM.addEventListener(node, eventName, function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
return callback.apply(null, [node].concat(__slice.call(args), [context]));
@@ -2846,6 +2806,16 @@
}
};
+ Batman.DOM.eventIsAllowed = function(eventName, event) {
+ var delegate, _ref, _ref1;
+ if (delegate = (_ref = Batman.currentApp) != null ? (_ref1 = _ref.shouldAllowEvent) != null ? _ref1[eventName] : void 0 : void 0) {
+ if (delegate(event) === false) {
+ return false;
+ }
+ }
+ return true;
+ };
+
}).call(this);
(function() {
@@ -2930,123 +2900,83 @@
}).call(this);
(function() {
- var BatmanObject,
+ var BatmanObject, ObjectFunctions, getAccessorObject, promiseWrapper, wrapSingleAccessor,
+ __slice = [].slice,
__hasProp = {}.hasOwnProperty,
- __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; },
- __slice = [].slice;
-
- BatmanObject = (function(_super) {
- var counter, getAccessorObject, promiseWrapper, wrapSingleAccessor;
-
- __extends(BatmanObject, _super);
-
- Batman.initializeObject(BatmanObject);
-
- Batman.initializeObject(BatmanObject.prototype);
-
- BatmanObject.classMixin = function() {
- return Batman.mixin.apply(Batman, [this].concat(__slice.call(arguments)));
- };
-
- BatmanObject.mixin = function() {
- return this.classMixin.apply(this.prototype, arguments);
- };
-
- BatmanObject.prototype.mixin = BatmanObject.classMixin;
-
- counter = 0;
+ __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; };
- BatmanObject.prototype._batmanID = function() {
- var c;
- this._batmanID = function() {
- return c;
+ getAccessorObject = function(base, accessor) {
+ var deprecated, _i, _len, _ref;
+ if (typeof accessor === 'function') {
+ accessor = {
+ get: accessor
};
- return c = counter++;
- };
-
- BatmanObject.prototype.hashKey = function() {
- var key;
- if (typeof this.isEqual === 'function') {
- return;
- }
- this.hashKey = function() {
- return key;
- };
- return key = "<Batman.Object " + (this._batmanID()) + ">";
- };
-
- BatmanObject.prototype.toJSON = function() {
- var key, obj, value;
- obj = {};
- for (key in this) {
- if (!__hasProp.call(this, key)) continue;
- value = this[key];
- if (key !== "_batman" && key !== "hashKey" && key !== "_batmanID") {
- obj[key] = (value != null ? value.toJSON : void 0) ? value.toJSON() : value;
+ }
+ _ref = ['cachable', 'cacheable'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ deprecated = _ref[_i];
+ if (deprecated in accessor) {
+ Batman.developer.warn("Property accessor option \"" + deprecated + "\" is deprecated. Use \"cache\" instead.");
+ if (!('cache' in accessor)) {
+ accessor.cache = accessor[deprecated];
}
}
- return obj;
- };
+ }
+ return accessor;
+ };
- getAccessorObject = function(base, accessor) {
- var deprecated, _i, _len, _ref;
- if (typeof accessor === 'function') {
- accessor = {
- get: accessor
- };
- }
- _ref = ['cachable', 'cacheable'];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- deprecated = _ref[_i];
- if (deprecated in accessor) {
- Batman.developer.warn("Property accessor option \"" + deprecated + "\" is deprecated. Use \"cache\" instead.");
- if (!('cache' in accessor)) {
- accessor.cache = accessor[deprecated];
+ promiseWrapper = function(fetcher) {
+ return function(defaultAccessor) {
+ return {
+ get: function(key) {
+ var asyncDeliver, existingValue, newValue, _base, _base1, _ref, _ref1,
+ _this = this;
+ if ((existingValue = defaultAccessor.get.apply(this, arguments)) != null) {
+ return existingValue;
}
- }
- }
- return accessor;
- };
-
- promiseWrapper = function(fetcher) {
- return function(core) {
- return {
- get: function(key) {
- var deliver, returned, val,
- _this = this;
- val = core.get.apply(this, arguments);
- if (typeof val !== 'undefined') {
- return val;
- }
- returned = false;
- deliver = function(err, result) {
- if (returned) {
- _this.set(key, result);
+ asyncDeliver = false;
+ newValue = void 0;
+ if ((_ref = (_base = this._batman).promises) == null) {
+ _base.promises = {};
+ }
+ if ((_ref1 = (_base1 = this._batman.promises)[key]) == null) {
+ _base1[key] = (function() {
+ var deliver, returnValue;
+ deliver = function(err, result) {
+ if (asyncDeliver) {
+ _this.set(key, result);
+ }
+ return newValue = result;
+ };
+ returnValue = fetcher.call(_this, deliver, key);
+ if (newValue == null) {
+ newValue = returnValue;
}
- return val = result;
- };
- fetcher.call(this, deliver, key);
- returned = true;
- return val;
- },
- cache: true
- };
+ return true;
+ })();
+ }
+ asyncDeliver = true;
+ return newValue;
+ },
+ cache: true
};
};
+ };
- wrapSingleAccessor = function(core, wrapper) {
- var k, v;
- wrapper = (typeof wrapper === "function" ? wrapper(core) : void 0) || wrapper;
- for (k in core) {
- v = core[k];
- if (!(k in wrapper)) {
- wrapper[k] = v;
- }
+ wrapSingleAccessor = function(core, wrapper) {
+ var k, v;
+ wrapper = (typeof wrapper === "function" ? wrapper(core) : void 0) || wrapper;
+ for (k in core) {
+ v = core[k];
+ if (!(k in wrapper)) {
+ wrapper[k] = v;
}
- return wrapper;
- };
+ }
+ return wrapper;
+ };
- BatmanObject._defineAccessor = function() {
+ ObjectFunctions = {
+ _defineAccessor: function() {
var accessor, key, keys, _base, _i, _j, _len, _ref, _results;
keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), accessor = arguments[_i++];
if (!(accessor != null)) {
@@ -3068,11 +2998,8 @@
}
return _results;
}
- };
-
- BatmanObject.prototype._defineAccessor = BatmanObject._defineAccessor;
-
- BatmanObject._defineWrapAccessor = function() {
+ },
+ _defineWrapAccessor: function() {
var key, keys, wrapper, _i, _j, _len, _results;
keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), wrapper = arguments[_i++];
Batman.initializeObject(this);
@@ -3086,9 +3013,45 @@
}
return _results;
}
+ },
+ _resetPromises: function() {
+ var key;
+ if (this._batman.promises == null) {
+ return;
+ }
+ for (key in this._batman.promises) {
+ this._resetPromise(key);
+ }
+ },
+ _resetPromise: function(key) {
+ this.unset(key);
+ this.property(key).cached = false;
+ delete this._batman.promises[key];
+ }
+ };
+
+ BatmanObject = (function(_super) {
+ var counter;
+
+ __extends(BatmanObject, _super);
+
+ Batman.initializeObject(BatmanObject);
+
+ Batman.initializeObject(BatmanObject.prototype);
+
+ Batman.mixin(BatmanObject.prototype, ObjectFunctions, Batman.EventEmitter, Batman.Observable);
+
+ Batman.mixin(BatmanObject, ObjectFunctions, Batman.EventEmitter, Batman.Observable);
+
+ BatmanObject.classMixin = function() {
+ return Batman.mixin.apply(Batman, [this].concat(__slice.call(arguments)));
+ };
+
+ BatmanObject.mixin = function() {
+ return this.classMixin.apply(this.prototype, arguments);
};
- BatmanObject.prototype._defineWrapAccessor = BatmanObject._defineWrapAccessor;
+ BatmanObject.prototype.mixin = BatmanObject.classMixin;
BatmanObject.classAccessor = BatmanObject._defineAccessor;
@@ -3108,17 +3071,6 @@
BatmanObject.prototype.wrapAccessor = BatmanObject._defineWrapAccessor;
- function BatmanObject() {
- var mixins;
- mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- this._batman = new Batman._Batman(this);
- this.mixin.apply(this, mixins);
- }
-
- BatmanObject.classMixin(Batman.EventEmitter, Batman.Observable);
-
- BatmanObject.mixin(Batman.EventEmitter, Batman.Observable);
-
BatmanObject.observeAll = function() {
return this.prototype.observe.apply(this.prototype, arguments);
};
@@ -3139,6 +3091,48 @@
return this._batmanID();
});
+ function BatmanObject() {
+ var mixins;
+ mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ this._batman = new Batman._Batman(this);
+ this.mixin.apply(this, mixins);
+ }
+
+ counter = 0;
+
+ BatmanObject.prototype._batmanID = function() {
+ var _base, _ref;
+ this._batman.check(this);
+ if ((_ref = (_base = this._batman).id) == null) {
+ _base.id = counter++;
+ }
+ return this._batman.id;
+ };
+
+ BatmanObject.prototype.hashKey = function() {
+ var key;
+ if (typeof this.isEqual === 'function') {
+ return;
+ }
+ this.hashKey = function() {
+ return key;
+ };
+ return key = "<Batman.Object " + (this._batmanID()) + ">";
+ };
+
+ BatmanObject.prototype.toJSON = function() {
+ var key, obj, value;
+ obj = {};
+ for (key in this) {
+ if (!__hasProp.call(this, key)) continue;
+ value = this[key];
+ if (key !== "_batman" && key !== "hashKey" && key !== "_batmanID") {
+ obj[key] = (value != null ? value.toJSON : void 0) ? value.toJSON() : value;
+ }
+ }
+ return obj;
+ };
+
return BatmanObject;
})(Object);
@@ -3176,6 +3170,8 @@
Renderer.prototype.start = function() {
this.startTime = new Date;
+ this.prevent('parsed');
+ this.prevent('rendered');
return this.parseNode(this.node);
};
@@ -3187,8 +3183,8 @@
Renderer.prototype.finish = function() {
this.startTime = null;
this.prevent('stopped');
- this.fire('parsed');
- return this.fire('rendered');
+ this.allowAndFire('parsed');
+ return this.allowAndFire('rendered');
};
Renderer.prototype.stop = function() {
@@ -3265,7 +3261,7 @@
} else if (result instanceof Batman.RenderContext) {
oldContext = this.context;
this.context = result;
- Batman.onParseExit(node, function() {
+ Batman.DOM.onParseExit(node, function() {
return _this.context = oldContext;
});
}
@@ -3287,12 +3283,12 @@
}
}
sibling = node.nextSibling;
- if ((_ref1 = Batman.onParseExit(node)) != null) {
+ if ((_ref1 = Batman.DOM.onParseExit(node)) != null) {
_ref1.forEach(function(callback) {
return callback();
});
}
- Batman.forgetParseExit(node);
+ Batman.DOM.forgetParseExit(node);
if (this.node === node) {
return;
}
@@ -3302,12 +3298,12 @@
nextParent = node;
while (nextParent = nextParent.parentNode) {
parentSibling = nextParent.nextSibling;
- if ((_ref2 = Batman.onParseExit(nextParent)) != null) {
+ if ((_ref2 = Batman.DOM.onParseExit(nextParent)) != null) {
_ref2.forEach(function(callback) {
return callback();
});
}
- Batman.forgetParseExit(nextParent);
+ Batman.DOM.forgetParseExit(nextParent);
if (this.node === nextParent) {
return;
}
@@ -3636,7 +3632,7 @@
if (typeof (hide = Batman.data(this.node, 'hide')) === 'function') {
hide.call(this.node);
} else {
- Batman.setStyleProperty(this.node, 'display', 'none', 'important');
+ Batman.DOM.setStyleProperty(this.node, 'display', 'none', 'important');
}
return view != null ? view.fire('disappear', this.node) : void 0;
}
@@ -3659,7 +3655,7 @@
SelectBinding.prototype.isInputBinding = true;
- SelectBinding.prototype.firstBind = true;
+ SelectBinding.prototype.canSetImplicitly = true;
function SelectBinding() {
this.updateOptionBindings = __bind(this.updateOptionBindings, this);
@@ -3700,9 +3696,16 @@
return this._fireDataChange(this.get('filteredValue'));
};
+ SelectBinding.prototype.lastKeyContext = null;
+
SelectBinding.prototype.dataChange = function(newValue) {
var child, matches, valueToChild, _i, _len, _name, _ref,
_this = this;
+ this.lastKeyContext || (this.lastKeyContext = this.get('keyContext'));
+ if (this.lastKeyContext !== this.get('keyContext')) {
+ this.canSetImplicitly = true;
+ this.lastKeyContext = this.get('keyContext');
+ }
if (newValue != null ? newValue.forEach : void 0) {
valueToChild = {};
_ref = this.node.children;
@@ -3724,12 +3727,15 @@
}
});
} else {
- if (typeof newValue === 'undefined' && this.firstBind) {
- this.set('unfilteredValue', this.node.value);
+ if (!(newValue != null) && this.canSetImplicitly) {
+ if (this.node.value) {
+ this.canSetImplicitly = false;
+ this.set('unfilteredValue', this.node.value);
+ }
} else {
+ this.canSetImplicitly = false;
Batman.DOM.valueForNode(this.node, newValue, this.escapeValue);
}
- this.firstBind = false;
}
this.updateOptionBindings();
};
@@ -4047,7 +4053,7 @@
this.contextName = contextName;
delete this.attributeName;
Batman.DOM.events.submit(this.get('node'), function(node, e) {
- return Batman.preventDefault(e);
+ return Batman.DOM.preventDefault(e);
});
this.setupErrorsList();
}
@@ -4065,7 +4071,7 @@
FormBinding.prototype.setupErrorsList = function() {
if (this.errorsListNode = Batman.DOM.querySelector(this.get('node'), this.get('errorsListSelector'))) {
- Batman.setInnerHTML(this.errorsListNode, this.errorsListHTML());
+ Batman.DOM.setInnerHTML(this.errorsListNode, this.errorsListHTML());
if (!this.errorsListNode.getAttribute('data-showif')) {
return this.errorsListNode.setAttribute('data-showif', "" + this.contextName + ".errors.length");
}
@@ -4093,15 +4099,11 @@
EventBinding.prototype.bindImmediately = false;
function EventBinding(node, eventName, key, context) {
- var attacher, callback, confirmText,
+ var attacher, callback,
_this = this;
EventBinding.__super__.constructor.apply(this, arguments);
- confirmText = this.node.getAttribute('data-confirm');
callback = function() {
var _ref;
- if (confirmText && !confirm(confirmText)) {
- return;
- }
return (_ref = _this.get('filteredValue')) != null ? _ref.apply(_this.get('callbackContext'), arguments) : void 0;
};
if (attacher = Batman.DOM.events[this.attributeName]) {
@@ -4132,8 +4134,8 @@
if (keys.length > 1) {
functionKey = keys.pop();
keyContext = Batman.getPath(this, ['keyContext'].concat(keys));
+ keyContext = Batman.RenderContext.deProxy(keyContext);
if (keyContext != null) {
- keyContext = Batman.RenderContext.deProxy(keyContext);
return keyContext[functionKey];
}
}
@@ -4506,11 +4508,11 @@
if (Batman.canDeleteExpando) {
delete sourceNode[Batman.expando];
}
- Batman.insertBefore(sourceNode.parentNode, this.startNode, previousSiblingNode);
- Batman.insertBefore(sourceNode.parentNode, this.endNode, previousSiblingNode);
+ Batman.DOM.insertBefore(sourceNode.parentNode, this.startNode, previousSiblingNode);
+ Batman.DOM.insertBefore(sourceNode.parentNode, this.endNode, previousSiblingNode);
this.parentRenderer.prevent('rendered');
Batman.DOM.onParseExit(sourceNode.parentNode, function() {
- Batman.destroyNode(sourceNode);
+ Batman.DOM.destroyNode(sourceNode);
_this.bind();
return _this.parentRenderer.allowAndFire('rendered');
});
@@ -4546,15 +4548,17 @@
parentNode = this.parentNode();
startIndex = this._getStartNodeIndex() + 1;
unseenNodeMap = this.nodeMap.merge();
- for (index = _i = 0, _len = newItems.length; _i < _len; index = ++_i) {
- newItem = newItems[index];
- nodeAtIndex = parentNode.childNodes[startIndex + index];
- if ((nodeAtIndex != null) && this._itemForNode(nodeAtIndex) === newItem) {
- unseenNodeMap.unset(newItem);
- continue;
- } else {
- node = (existingNode = this.nodeMap.get(newItem)) ? (unseenNodeMap.unset(newItem), existingNode) : this._newNodeForItem(newItem);
- Batman.insertBefore(this.parentNode(), node, nodeAtIndex);
+ if (newItems != null) {
+ for (index = _i = 0, _len = newItems.length; _i < _len; index = ++_i) {
+ newItem = newItems[index];
+ nodeAtIndex = parentNode.childNodes[startIndex + index];
+ if ((nodeAtIndex != null) && this._itemForNode(nodeAtIndex) === newItem) {
+ unseenNodeMap.unset(newItem);
+ continue;
+ } else {
+ node = (existingNode = this.nodeMap.get(newItem)) ? (unseenNodeMap.unset(newItem), existingNode) : this._newNodeForItem(newItem);
+ Batman.DOM.insertBefore(this.parentNode(), node, nodeAtIndex);
+ }
}
}
unseenNodeMap.forEach(function(item, node) {
@@ -4575,7 +4579,7 @@
this.parentRenderer.prevent('rendered');
renderer = new Batman.Renderer(newNode, this.renderContext.descend(newItem, this.iteratorName), this.parentRenderer.view);
renderer.on('rendered', function() {
- Batman.propagateBindingEvents(newNode);
+ Batman.DOM.propagateBindingEvents(newNode);
_this.fire('nodeAdded', newNode, newItem);
return _this.parentRenderer.allowAndFire('rendered');
});
@@ -4597,7 +4601,7 @@
IteratorBinding.prototype._removeItem = function(item) {
var node;
node = this.nodeMap.unset(item);
- Batman.destroyNode(node);
+ Batman.DOM.destroyNode(node);
return this.fire('nodeRemoved', node, item);
};
@@ -4746,6 +4750,76 @@
})(StorageAdapter.StorageError);
+ StorageAdapter.NotAllowedError = (function(_super1) {
+
+ __extends(NotAllowedError, _super1);
+
+ NotAllowedError.prototype.name = "NotAllowedError";
+
+ function NotAllowedError(message) {
+ NotAllowedError.__super__.constructor.call(this, message || "Storage operation denied access to the operation!");
+ }
+
+ return NotAllowedError;
+
+ })(StorageAdapter.StorageError);
+
+ StorageAdapter.NotAcceptableError = (function(_super1) {
+
+ __extends(NotAcceptableError, _super1);
+
+ NotAcceptableError.prototype.name = "NotAcceptableError";
+
+ function NotAcceptableError(message) {
+ NotAcceptableError.__super__.constructor.call(this, message || "Storage operation permitted but the request was malformed!");
+ }
+
+ return NotAcceptableError;
+
+ })(StorageAdapter.StorageError);
+
+ StorageAdapter.UnprocessableRecordError = (function(_super1) {
+
+ __extends(UnprocessableRecordError, _super1);
+
+ UnprocessableRecordError.prototype.name = "UnprocessableRecordError";
+
+ function UnprocessableRecordError(message) {
+ UnprocessableRecordError.__super__.constructor.call(this, message || "Storage adapter could not process the record!");
+ }
+
+ return UnprocessableRecordError;
+
+ })(StorageAdapter.StorageError);
+
+ StorageAdapter.InternalStorageError = (function(_super1) {
+
+ __extends(InternalStorageError, _super1);
+
+ InternalStorageError.prototype.name = "InternalStorageError";
+
+ function InternalStorageError(message) {
+ InternalStorageError.__super__.constructor.call(this, message || "An error occured during the storage operation!");
+ }
+
+ return InternalStorageError;
+
+ })(StorageAdapter.StorageError);
+
+ StorageAdapter.NotImplementedError = (function(_super1) {
+
+ __extends(NotImplementedError, _super1);
+
+ NotImplementedError.prototype.name = "NotImplementedError";
+
+ function NotImplementedError(message) {
+ NotImplementedError.__super__.constructor.call(this, message || "This operation is not implemented by the storage adpater!");
+ }
+
+ return NotImplementedError;
+
+ })(StorageAdapter.StorageError);
+
function StorageAdapter(model) {
var constructor;
StorageAdapter.__super__.constructor.call(this, {
@@ -4891,9 +4965,10 @@
}
return _this.runAfterFilter(key, env, callback);
};
- return this.runBeforeFilter(key, env, function(env) {
+ this.runBeforeFilter(key, env, function(env) {
return this[key](env, next);
});
+ return void 0;
};
return StorageAdapter;
@@ -4914,6 +4989,20 @@
__extends(RestStorage, _super);
+ RestStorage.CommunicationError = (function(_super1) {
+
+ __extends(CommunicationError, _super1);
+
+ CommunicationError.prototype.name = 'CommunicationError';
+
+ function CommunicationError(message) {
+ CommunicationError.__super__.constructor.call(this, message || "A communication error has occurred!");
+ }
+
+ return CommunicationError;
+
+ })(RestStorage.StorageError);
+
RestStorage.JSONContentType = 'application/json';
RestStorage.PostBodyContentType = 'application/x-www-form-urlencoded';
@@ -4998,16 +5087,19 @@
return Batman.helpers.pluralize(this.storageKey(constructor.prototype));
};
- RestStorage.prototype._execWithOptions = function(object, key, options) {
+ RestStorage.prototype._execWithOptions = function(object, key, options, context) {
+ if (context == null) {
+ context = object;
+ }
if (typeof object[key] === 'function') {
- return object[key](options);
+ return object[key].call(context, options);
} else {
return object[key];
}
};
RestStorage.prototype._defaultCollectionUrl = function(model) {
- return "/" + (this.storageKey(model.prototype));
+ return "" + (this.storageKey(model.prototype));
};
RestStorage.prototype._addParams = function(url, options) {
@@ -5018,9 +5110,32 @@
return url;
};
+ RestStorage.prototype._addUrlAffixes = function(url, subject, env) {
+ var prefix, segments;
+ segments = [url, this.urlSuffix(subject, env)];
+ if (url.charAt(0) !== '/') {
+ prefix = this.urlPrefix(subject, env);
+ if (prefix.charAt(prefix.length - 1) !== '/') {
+ segments.unshift('/');
+ }
+ segments.unshift(prefix);
+ }
+ return segments.join('');
+ };
+
+ RestStorage.prototype.urlPrefix = function(object, env) {
+ return this._execWithOptions(object, 'urlPrefix', env.options) || '';
+ };
+
+ RestStorage.prototype.urlSuffix = function(object, env) {
+ return this._execWithOptions(object, 'urlSuffix', env.options) || '';
+ };
+
RestStorage.prototype.urlForRecord = function(record, env) {
- var id, url;
- if (record.url) {
+ var id, url, _ref;
+ if ((_ref = env.options) != null ? _ref.recordUrl : void 0) {
+ url = this._execWithOptions(env.options, 'recordUrl', env.options, record);
+ } else if (record.url) {
url = this._execWithOptions(record, 'url', env.options);
} else {
url = record.constructor.url ? this._execWithOptions(record.constructor, 'url', env.options) : this._defaultCollectionUrl(record.constructor);
@@ -5032,23 +5147,13 @@
}
}
}
- url = this._addParams(url, env.options);
- return this.urlPrefix(record, env) + url + this.urlSuffix(record, env);
+ return this._addUrlAffixes(this._addParams(url, env.options), record, env);
};
RestStorage.prototype.urlForCollection = function(model, env) {
- var url;
- url = model.url ? this._execWithOptions(model, 'url', env.options) : this._defaultCollectionUrl(model, env.options);
- url = this._addParams(url, env.options);
- return this.urlPrefix(model, env) + url + this.urlSuffix(model, env);
- };
-
- RestStorage.prototype.urlPrefix = function(object, env) {
- return this._execWithOptions(object, 'urlPrefix', env.options) || '';
- };
-
- RestStorage.prototype.urlSuffix = function(object, env) {
- return this._execWithOptions(object, 'urlSuffix', env.options) || '';
+ var url, _ref;
+ url = ((_ref = env.options) != null ? _ref.collectionUrl : void 0) ? this._execWithOptions(env.options, 'collectionUrl', env.options, env.options.urlContext) : model.url ? this._execWithOptions(model, 'url', env.options) : this._defaultCollectionUrl(model, env.options);
+ return this._addUrlAffixes(this._addParams(url, env.options), model, env);
};
RestStorage.prototype.request = function(env, next) {
@@ -5212,6 +5317,37 @@
_fn(key);
}
+ RestStorage.prototype.after('all', function(env, next) {
+ if (env.error) {
+ env.error = this._errorFor(env.error, env);
+ }
+ return next();
+ });
+
+ RestStorage._statusCodeErrors = {
+ '0': RestStorage.CommunicationError,
+ '403': RestStorage.NotAllowedError,
+ '404': RestStorage.NotFoundError,
+ '406': RestStorage.NotAcceptableError,
+ '422': RestStorage.UnprocessableRecordError,
+ '500': RestStorage.InternalStorageError,
+ '501': RestStorage.NotImplementedError
+ };
+
+ RestStorage.prototype._errorFor = function(error, env) {
+ var errorClass, request;
+ if (error instanceof Error || !(error.request != null)) {
+ return error;
+ }
+ if (errorClass = this.constructor._statusCodeErrors[error.request.status]) {
+ request = error.request;
+ error = new errorClass;
+ error.request = request;
+ error.env = env;
+ }
+ return error;
+ };
+
return RestStorage;
}).call(this, Batman.StorageAdapter);
@@ -5427,1126 +5563,6 @@
var __hasProp = {}.hasOwnProperty,
__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; };
- Batman.AssociationProxy = (function(_super) {
-
- __extends(AssociationProxy, _super);
-
- AssociationProxy.prototype.isProxy = true;
-
- function AssociationProxy(association, model) {
- this.association = association;
- this.model = model;
- }
-
- AssociationProxy.prototype.loaded = false;
-
- AssociationProxy.prototype.toJSON = function() {
- var target;
- target = this.get('target');
- if (target != null) {
- return this.get('target').toJSON();
- }
- };
-
- AssociationProxy.prototype.load = function(callback) {
- var _this = this;
- this.fetch(function(err, proxiedRecord) {
- if (!err) {
- _this.set('loaded', true);
- _this.set('target', proxiedRecord);
- }
- return typeof callback === "function" ? callback(err, proxiedRecord) : void 0;
- });
- return this.get('target');
- };
-
- AssociationProxy.prototype.fetch = function(callback) {
- var record;
- if ((this.get('foreignValue') || this.get('primaryValue')) == null) {
- return callback(void 0, void 0);
- }
- record = this.fetchFromLocal();
- if (record) {
- return callback(void 0, record);
- } else {
- return this.fetchFromRemote(callback);
- }
- };
-
- AssociationProxy.accessor('loaded', Batman.Property.defaultAccessor);
-
- AssociationProxy.accessor('target', {
- get: function() {
- return this.fetchFromLocal();
- },
- set: function(_, v) {
- return v;
- }
- });
-
- AssociationProxy.accessor({
- get: function(k) {
- var _ref;
- return (_ref = this.get('target')) != null ? _ref.get(k) : void 0;
- },
- set: function(k, v) {
- var _ref;
- return (_ref = this.get('target')) != null ? _ref.set(k, v) : void 0;
- }
- });
-
- return AssociationProxy;
-
- })(Batman.Object);
-
-}).call(this);
-
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __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; };
-
- Batman.HasOneProxy = (function(_super) {
-
- __extends(HasOneProxy, _super);
-
- function HasOneProxy() {
- return HasOneProxy.__super__.constructor.apply(this, arguments);
- }
-
- HasOneProxy.accessor('primaryValue', function() {
- return this.model.get(this.association.primaryKey);
- });
-
- HasOneProxy.prototype.fetchFromLocal = function() {
- return this.association.setIndex().get(this.get('primaryValue'));
- };
-
- HasOneProxy.prototype.fetchFromRemote = function(callback) {
- var loadOptions,
- _this = this;
- loadOptions = {};
- loadOptions[this.association.foreignKey] = this.get('primaryValue');
- return this.association.getRelatedModel().load(loadOptions, function(error, loadedRecords) {
- if (error) {
- throw error;
- }
- if (!loadedRecords || loadedRecords.length <= 0) {
- return callback(new Error("Couldn't find related record!"), void 0);
- } else {
- return callback(void 0, loadedRecords[0]);
- }
- });
- };
-
- return HasOneProxy;
-
- })(Batman.AssociationProxy);
-
-}).call(this);
-
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __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; };
-
- Batman.BelongsToProxy = (function(_super) {
-
- __extends(BelongsToProxy, _super);
-
- function BelongsToProxy() {
- return BelongsToProxy.__super__.constructor.apply(this, arguments);
- }
-
- BelongsToProxy.accessor('foreignValue', function() {
- return this.model.get(this.association.foreignKey);
- });
-
- BelongsToProxy.prototype.fetchFromLocal = function() {
- return this.association.setIndex().get(this.get('foreignValue'));
- };
-
- BelongsToProxy.prototype.fetchFromRemote = function(callback) {
- var _this = this;
- return this.association.getRelatedModel().find(this.get('foreignValue'), function(error, loadedRecord) {
- if (error) {
- throw error;
- }
- return callback(void 0, loadedRecord);
- });
- };
-
- return BelongsToProxy;
-
- })(Batman.AssociationProxy);
-
-}).call(this);
-
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __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; };
-
- Batman.PolymorphicBelongsToProxy = (function(_super) {
-
- __extends(PolymorphicBelongsToProxy, _super);
-
- function PolymorphicBelongsToProxy() {
- return PolymorphicBelongsToProxy.__super__.constructor.apply(this, arguments);
- }
-
- PolymorphicBelongsToProxy.accessor('foreignTypeValue', function() {
- return this.model.get(this.association.foreignTypeKey);
- });
-
- PolymorphicBelongsToProxy.prototype.fetchFromLocal = function() {
- return this.association.setIndexForType(this.get('foreignTypeValue')).get(this.get('foreignValue'));
- };
-
- PolymorphicBelongsToProxy.prototype.fetchFromRemote = function(callback) {
- var _this = this;
- return this.association.getRelatedModelForType(this.get('foreignTypeValue')).find(this.get('foreignValue'), function(error, loadedRecord) {
- if (error) {
- throw error;
- }
- return callback(void 0, loadedRecord);
- });
- };
-
- return PolymorphicBelongsToProxy;
-
- })(Batman.BelongsToProxy);
-
-}).call(this);
-
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __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; },
- __slice = [].slice;
-
- Batman.StateMachine = (function(_super) {
-
- __extends(StateMachine, _super);
-
- StateMachine.InvalidTransitionError = function(message) {
- this.message = message != null ? message : "";
- };
-
- StateMachine.InvalidTransitionError.prototype = new Error;
-
- StateMachine.transitions = function(table) {
- var definePredicate, fromState, k, object, predicateKeys, toState, transitions, v, _fn, _ref,
- _this = this;
- for (k in table) {
- v = table[k];
- if (!(v.from && v.to)) {
- continue;
- }
- object = {};
- if (v.from.forEach) {
- v.from.forEach(function(fromKey) {
- return object[fromKey] = v.to;
- });
- } else {
- object[v.from] = v.to;
- }
- table[k] = object;
- }
- this.prototype.transitionTable = Batman.extend({}, this.prototype.transitionTable, table);
- predicateKeys = [];
- definePredicate = function(state) {
- var key;
- key = "is" + (Batman.helpers.capitalize(state));
- if (_this.prototype[key] != null) {
- return;
- }
- predicateKeys.push(key);
- return _this.prototype[key] = function() {
- return this.get('state') === state;
- };
- };
- _ref = this.prototype.transitionTable;
- _fn = function(k) {
- return _this.prototype[k] = function() {
- return this.startTransition(k);
- };
- };
- for (k in _ref) {
- transitions = _ref[k];
- if (!(!this.prototype[k])) {
- continue;
- }
- _fn(k);
- for (fromState in transitions) {
- toState = transitions[fromState];
- definePredicate(fromState);
- definePredicate(toState);
- }
- }
- if (predicateKeys.length) {
- this.accessor.apply(this, __slice.call(predicateKeys).concat([function(key) {
- return this[key]();
- }]));
- }
- return this;
- };
-
- function StateMachine(startState) {
- this.nextEvents = [];
- this.set('_state', startState);
- }
-
- StateMachine.accessor('state', function() {
- return this.get('_state');
- });
-
- StateMachine.prototype.isTransitioning = false;
-
- StateMachine.prototype.transitionTable = {};
-
- StateMachine.prototype.onTransition = function(from, into, callback) {
- return this.on("" + from + "->" + into, callback);
- };
-
- StateMachine.prototype.onEnter = function(into, callback) {
- return this.on("enter " + into, callback);
- };
-
- StateMachine.prototype.onExit = function(from, callback) {
- return this.on("exit " + from, callback);
- };
-
- StateMachine.prototype.startTransition = Batman.Property.wrapTrackingPrevention(function(event) {
- var nextState, previousState;
- if (this.isTransitioning) {
- this.nextEvents.push(event);
- return;
- }
- previousState = this.get('state');
- nextState = this.nextStateForEvent(event);
- if (!nextState) {
- return false;
- }
- this.isTransitioning = true;
- this.fire("exit " + previousState);
- this.set('_state', nextState);
- this.fire("" + previousState + "->" + nextState);
- this.fire("enter " + nextState);
- this.fire(event);
- this.isTransitioning = false;
- if (this.nextEvents.length > 0) {
- this.startTransition(this.nextEvents.shift());
- }
- return true;
- });
-
- StateMachine.prototype.canStartTransition = function(event, fromState) {
- if (fromState == null) {
- fromState = this.get('state');
- }
- return !!this.nextStateForEvent(event, fromState);
- };
-
- StateMachine.prototype.nextStateForEvent = function(event, fromState) {
- var _ref;
- if (fromState == null) {
- fromState = this.get('state');
- }
- return (_ref = this.transitionTable[event]) != null ? _ref[fromState] : void 0;
- };
-
- return StateMachine;
-
- })(Batman.Object);
-
- Batman.DelegatingStateMachine = (function(_super) {
-
- __extends(DelegatingStateMachine, _super);
-
- function DelegatingStateMachine(startState, base) {
- this.base = base;
- DelegatingStateMachine.__super__.constructor.call(this, startState);
- }
-
- DelegatingStateMachine.prototype.fire = function() {
- var result, _ref;
- result = DelegatingStateMachine.__super__.fire.apply(this, arguments);
- (_ref = this.base).fire.apply(_ref, arguments);
- return result;
- };
-
- return DelegatingStateMachine;
-
- })(Batman.StateMachine);
-
-}).call(this);
-
-(function() {
- var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __hasProp = {}.hasOwnProperty,
- __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; },
- __slice = [].slice;
-
- Batman.Model = (function(_super) {
- var functionName, _i, _j, _len, _len1, _ref, _ref1;
-
- __extends(Model, _super);
-
- Model.primaryKey = 'id';
-
- Model.storageKey = null;
-
- Model.persist = function(mechanism, options) {
- Batman.initializeObject(this.prototype);
- mechanism = mechanism.isStorageAdapter ? mechanism : new mechanism(this);
- if (options) {
- Batman.mixin(mechanism, options);
- }
- this.prototype._batman.storage = mechanism;
- return mechanism;
- };
-
- Model.storageAdapter = function() {
- Batman.initializeObject(this.prototype);
- return this.prototype._batman.storage;
- };
-
- Model.encode = function() {
- var encoder, encoderForKey, encoderOrLastKey, key, keys, _base, _i, _j, _len;
- keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), encoderOrLastKey = arguments[_i++];
- Batman.initializeObject(this.prototype);
- (_base = this.prototype._batman).encoders || (_base.encoders = new Batman.SimpleHash);
- encoder = {};
- switch (Batman.typeOf(encoderOrLastKey)) {
- case 'String':
- keys.push(encoderOrLastKey);
- break;
- case 'Function':
- encoder.encode = encoderOrLastKey;
- break;
- default:
- encoder = encoderOrLastKey;
- }
- for (_j = 0, _len = keys.length; _j < _len; _j++) {
- key = keys[_j];
- encoderForKey = Batman.extend({
- as: key
- }, this.defaultEncoder, encoder);
- this.prototype._batman.encoders.set(key, encoderForKey);
- }
- };
-
- Model.defaultEncoder = {
- encode: function(x) {
- return x;
- },
- decode: function(x) {
- return x;
- }
- };
-
- Model.observeAndFire('primaryKey', function(newPrimaryKey, oldPrimaryKey) {
- this.encode(oldPrimaryKey, {
- encode: false,
- decode: false
- });
- return this.encode(newPrimaryKey, {
- encode: false,
- decode: this.defaultEncoder.decode
- });
- });
-
- Model.validate = function() {
- var keys, match, matches, options, optionsOrFunction, validator, validators, _base, _i, _j, _k, _len, _len1, _ref, _results;
- keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), optionsOrFunction = arguments[_i++];
- Batman.initializeObject(this.prototype);
- validators = (_base = this.prototype._batman).validators || (_base.validators = []);
- if (typeof optionsOrFunction === 'function') {
- return validators.push({
- keys: keys,
- callback: optionsOrFunction
- });
- } else {
- options = optionsOrFunction;
- _ref = Batman.Validators;
- _results = [];
- for (_j = 0, _len = _ref.length; _j < _len; _j++) {
- validator = _ref[_j];
- if ((matches = validator.matches(options))) {
- for (_k = 0, _len1 = matches.length; _k < _len1; _k++) {
- match = matches[_k];
- delete options[match];
- }
- _results.push(validators.push({
- keys: keys,
- validator: new validator(matches)
- }));
- } else {
- _results.push(void 0);
- }
- }
- return _results;
- }
- };
-
- Model.LifecycleStateMachine = (function(_super1) {
-
- __extends(LifecycleStateMachine, _super1);
-
- function LifecycleStateMachine() {
- return LifecycleStateMachine.__super__.constructor.apply(this, arguments);
- }
-
- LifecycleStateMachine.transitions({
- load: {
- empty: 'loading',
- loaded: 'loading',
- loading: 'loading'
- },
- loaded: {
- loading: 'loaded'
- },
- error: {
- loading: 'error'
- }
- });
-
- return LifecycleStateMachine;
-
- })(Batman.DelegatingStateMachine);
-
- Model.classAccessor('lifecycle', function() {
- var _base;
- this._batman.check(this);
- return (_base = this._batman).lifecycle || (_base.lifecycle = new this.LifecycleStateMachine('empty', this));
- });
-
- Model.classAccessor('resourceName', {
- get: function() {
- if (this.resourceName != null) {
- return this.resourceName;
- } else {
- if (Batman.config.minificationErrors) {
- Batman.developer.error("Please define " + (Batman.functionName(this)) + ".resourceName in order for your model to be minification safe.");
- }
- return Batman.helpers.underscore(Batman.functionName(this));
- }
- }
- });
-
- Model.classAccessor('all', {
- get: function() {
- this._batman.check(this);
- if (this.prototype.hasStorage() && !this._batman.allLoadTriggered) {
- this.load();
- this._batman.allLoadTriggered = true;
- }
- return this.get('loaded');
- },
- set: function(k, v) {
- return this.set('loaded', v);
- }
- });
-
- Model.classAccessor('loaded', {
- get: function() {
- return this._loaded || (this._loaded = new Batman.Set);
- },
- set: function(k, v) {
- return this._loaded = v;
- }
- });
-
- Model.classAccessor('first', function() {
- return this.get('all').toArray()[0];
- });
-
- Model.classAccessor('last', function() {
- var x;
- x = this.get('all').toArray();
- return x[x.length - 1];
- });
-
- Model.clear = function() {
- var result, _ref;
- Batman.initializeObject(this);
- result = this.get('loaded').clear();
- if ((_ref = this._batman.get('associations')) != null) {
- _ref.reset();
- }
- return result;
- };
-
- Model.find = function(id, callback) {
- var record;
- Batman.developer.assert(callback, "Must call find with a callback!");
- record = new this();
- record._withoutDirtyTracking(function() {
- return this.set('id', id);
- });
- record.load(callback);
- return record;
- };
-
- Model.load = function(options, callback) {
- var lifecycle, _ref,
- _this = this;
- if ((_ref = typeof options) === 'function' || _ref === 'undefined') {
- callback = options;
- options = {};
- }
- lifecycle = this.get('lifecycle');
- if (lifecycle.load()) {
- return this._doStorageOperation('readAll', {
- data: options
- }, function(err, records, env) {
- var mappedRecords, record;
- if (err != null) {
- lifecycle.error();
- return typeof callback === "function" ? callback(err, []) : void 0;
- } else {
- mappedRecords = (function() {
- var _i, _len, _results;
- _results = [];
- for (_i = 0, _len = records.length; _i < _len; _i++) {
- record = records[_i];
- _results.push(this._mapIdentity(record));
- }
- return _results;
- }).call(_this);
- lifecycle.loaded();
- return typeof callback === "function" ? callback(err, mappedRecords, env) : void 0;
- }
- });
- } else {
- return callback(new Batman.StateMachine.InvalidTransitionError("Can't load while in state " + (lifecycle.get('state'))));
- }
- };
-
- Model.create = function(attrs, callback) {
- var obj, _ref;
- if (!callback) {
- _ref = [{}, attrs], attrs = _ref[0], callback = _ref[1];
- }
- obj = new this(attrs);
- obj.save(callback);
- return obj;
- };
-
- Model.findOrCreate = function(attrs, callback) {
- var foundRecord, record;
- record = new this(attrs);
- if (record.isNew()) {
- record.save(callback);
- } else {
- foundRecord = this._mapIdentity(record);
- callback(void 0, foundRecord);
- }
- return record;
- };
-
- Model._mapIdentity = function(record) {
- var existing, id, _ref;
- if (typeof (id = record.get('id')) === 'undefined' || id === '') {
- return record;
- } else {
- existing = (_ref = this.get("loaded.indexedBy.id").get(id)) != null ? _ref.toArray()[0] : void 0;
- if (existing) {
- existing._withoutDirtyTracking(function() {
- var _ref1;
- return this.updateAttributes(((_ref1 = record.get('attributes')) != null ? _ref1.toObject() : void 0) || {});
- });
- return existing;
- } else {
- this.get('loaded').add(record);
- return record;
- }
- }
- };
-
- Model._doStorageOperation = function(operation, options, callback) {
- var adapter;
- Batman.developer.assert(this.prototype.hasStorage(), "Can't " + operation + " model " + (Batman.functionName(this.constructor)) + " without any storage adapters!");
- adapter = this.prototype._batman.get('storage');
- return adapter.perform(operation, this, options, callback);
- };
-
- _ref = ['find', 'load', 'create'];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- functionName = _ref[_i];
- Model[functionName] = Batman.Property.wrapTrackingPrevention(Model[functionName]);
- }
-
- Model.InstanceLifecycleStateMachine = (function(_super1) {
-
- __extends(InstanceLifecycleStateMachine, _super1);
-
- function InstanceLifecycleStateMachine() {
- return InstanceLifecycleStateMachine.__super__.constructor.apply(this, arguments);
- }
-
- InstanceLifecycleStateMachine.transitions({
- load: {
- from: ['dirty', 'clean'],
- to: 'loading'
- },
- create: {
- from: ['dirty', 'clean'],
- to: 'creating'
- },
- save: {
- from: ['dirty', 'clean'],
- to: 'saving'
- },
- destroy: {
- from: ['dirty', 'clean'],
- to: 'destroying'
- },
- failedValidation: {
- from: ['saving', 'creating'],
- to: 'dirty'
- },
- loaded: {
- loading: 'clean'
- },
- created: {
- creating: 'clean'
- },
- saved: {
- saving: 'clean'
- },
- destroyed: {
- destroying: 'destroyed'
- },
- set: {
- from: ['dirty', 'clean'],
- to: 'dirty'
- },
- error: {
- from: ['saving', 'creating', 'loading', 'destroying'],
- to: 'error'
- }
- });
-
- return InstanceLifecycleStateMachine;
-
- })(Batman.DelegatingStateMachine);
-
- function Model(idOrAttributes) {
- if (idOrAttributes == null) {
- idOrAttributes = {};
- }
- this.destroy = __bind(this.destroy, this);
-
- this.save = __bind(this.save, this);
-
- this.load = __bind(this.load, this);
-
- Batman.developer.assert(this instanceof Batman.Object, "constructors must be called with new");
- if (Batman.typeOf(idOrAttributes) === 'Object') {
- Model.__super__.constructor.call(this, idOrAttributes);
- } else {
- Model.__super__.constructor.call(this);
- this.set('id', idOrAttributes);
- }
- }
-
- Model.accessor('lifecycle', function() {
- return this.lifecycle || (this.lifecycle = new Batman.Model.InstanceLifecycleStateMachine('clean', this));
- });
-
- Model.accessor('attributes', function() {
- return this.attributes || (this.attributes = new Batman.Hash);
- });
-
- Model.accessor('dirtyKeys', function() {
- return this.dirtyKeys || (this.dirtyKeys = new Batman.Hash);
- });
-
- Model.accessor('_dirtiedKeys', function() {
- return this._dirtiedKeys || (this._dirtiedKeys = new Batman.SimpleSet);
- });
-
- Model.accessor('errors', function() {
- return this.errors || (this.errors = new Batman.ErrorsSet);
- });
-
- Model.accessor('isNew', function() {
- return this.isNew();
- });
-
- Model.accessor(Model.defaultAccessor = {
- get: function(k) {
- return Batman.getPath(this, ['attributes', k]);
- },
- set: function(k, v) {
- if (this._willSet(k)) {
- return this.get('attributes').set(k, v);
- } else {
- return this.get(k);
- }
- },
- unset: function(k) {
- return this.get('attributes').unset(k);
- }
- });
-
- Model.wrapAccessor('id', function(core) {
- return {
- get: function() {
- var primaryKey;
- primaryKey = this.constructor.primaryKey;
- if (primaryKey === 'id') {
- return core.get.apply(this, arguments);
- } else {
- return this.get(primaryKey);
- }
- },
- set: function(key, value) {
- var parsedValue, primaryKey;
- if ((typeof value === "string") && (value.match(/[^0-9]/) === null) && (("" + (parsedValue = parseInt(value, 10))) === value)) {
- value = parsedValue;
- }
- primaryKey = this.constructor.primaryKey;
- if (primaryKey === 'id') {
- this._willSet(key);
- return core.set.apply(this, arguments);
- } else {
- return this.set(primaryKey, value);
- }
- }
- };
- });
-
- Model.prototype.isNew = function() {
- return typeof this.get('id') === 'undefined';
- };
-
- Model.prototype.updateAttributes = function(attrs) {
- this.mixin(attrs);
- return this;
- };
-
- Model.prototype.toString = function() {
- return "" + (this.constructor.get('resourceName')) + ": " + (this.get('id'));
- };
-
- Model.prototype.toParam = function() {
- return this.get('id');
- };
-
- Model.prototype.toJSON = function() {
- var encoders, obj,
- _this = this;
- obj = {};
- encoders = this._batman.get('encoders');
- if (!(!encoders || encoders.isEmpty())) {
- encoders.forEach(function(key, encoder) {
- var encodedVal, val;
- if (encoder.encode) {
- val = _this.get(key);
- if (typeof val !== 'undefined') {
- encodedVal = encoder.encode(val, key, obj, _this);
- if (typeof encodedVal !== 'undefined') {
- return obj[encoder.as] = encodedVal;
- }
- }
- }
- });
- }
- return obj;
- };
-
- Model.prototype.fromJSON = function(data) {
- var encoders, key, obj, value,
- _this = this;
- obj = {};
- encoders = this._batman.get('encoders');
- if (!encoders || encoders.isEmpty() || !encoders.some(function(key, encoder) {
- return encoder.decode != null;
- })) {
- for (key in data) {
- value = data[key];
- obj[key] = value;
- }
- } else {
- encoders.forEach(function(key, encoder) {
- if (encoder.decode && typeof data[encoder.as] !== 'undefined') {
- return obj[key] = encoder.decode(data[encoder.as], encoder.as, data, obj, _this);
- }
- });
- }
- if (this.constructor.primaryKey !== 'id') {
- obj.id = data[this.constructor.primaryKey];
- }
- Batman.developer["do"](function() {
- if ((!encoders) || encoders.length <= 1) {
- return Batman.developer.warn("Warning: Model " + (Batman.functionName(_this.constructor)) + " has suspiciously few decoders!");
- }
- });
- return this.mixin(obj);
- };
-
- Model.prototype.hasStorage = function() {
- return this._batman.get('storage') != null;
- };
-
- Model.prototype.load = function(options, callback) {
- var callbackQueue, hasOptions, _ref1, _ref2,
- _this = this;
- if (!callback) {
- _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1];
- }
- hasOptions = Object.keys(options).length !== 0;
- if ((_ref2 = this.get('lifecycle.state')) === 'destroying' || _ref2 === 'destroyed') {
- if (typeof callback === "function") {
- callback(new Error("Can't load a destroyed record!"));
- }
- return;
- }
- if (this.get('lifecycle').load()) {
- callbackQueue = [];
- if (callback != null) {
- callbackQueue.push(callback);
- }
- if (!hasOptions) {
- this._currentLoad = callbackQueue;
- }
- return this._doStorageOperation('read', {
- data: options
- }, function(err, record, env) {
- var _j, _len1;
- if (!err) {
- _this.get('lifecycle').loaded();
- record = _this.constructor._mapIdentity(record);
- } else {
- _this.get('lifecycle').error();
- }
- if (!hasOptions) {
- _this._currentLoad = null;
- }
- for (_j = 0, _len1 = callbackQueue.length; _j < _len1; _j++) {
- callback = callbackQueue[_j];
- callback(err, record, env);
- }
- });
- } else {
- if (this.get('lifecycle.state') === 'loading' && !hasOptions) {
- if (callback != null) {
- return this._currentLoad.push(callback);
- }
- } else {
- return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't load while in state " + (this.get('lifecycle.state')))) : void 0;
- }
- }
- };
-
- Model.prototype.save = function(options, callback) {
- var endState, isNew, startState, storageOperation, _ref1, _ref2, _ref3,
- _this = this;
- if (!callback) {
- _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1];
- }
- if ((_ref2 = this.get('lifecycle').get('state')) === 'destroying' || _ref2 === 'destroyed') {
- if (typeof callback === "function") {
- callback(new Error("Can't save a destroyed record!"));
- }
- return;
- }
- isNew = this.isNew();
- _ref3 = isNew ? ['create', 'create', 'created'] : ['save', 'update', 'saved'], startState = _ref3[0], storageOperation = _ref3[1], endState = _ref3[2];
- return this.validate(function(error, errors) {
- var associations, creating;
- if (error || errors.length) {
- _this.get('lifecycle').failedValidation();
- if (typeof callback === "function") {
- callback(error || errors, _this);
- }
- return;
- }
- creating = _this.isNew();
- if (_this.get('lifecycle').startTransition(startState)) {
- associations = _this.constructor._batman.get('associations');
- _this._withoutDirtyTracking(function() {
- var _ref4,
- _this = this;
- return associations != null ? (_ref4 = associations.getByType('belongsTo')) != null ? _ref4.forEach(function(association, label) {
- return association.apply(_this);
- }) : void 0 : void 0;
- });
- return _this._doStorageOperation(storageOperation, {
- data: options
- }, function(err, record, env) {
- if (!err) {
- _this.get('dirtyKeys').clear();
- _this.get('_dirtiedKeys').clear();
- if (associations) {
- record._withoutDirtyTracking(function() {
- var _ref4, _ref5;
- if ((_ref4 = associations.getByType('hasOne')) != null) {
- _ref4.forEach(function(association, label) {
- return association.apply(err, record);
- });
- }
- return (_ref5 = associations.getByType('hasMany')) != null ? _ref5.forEach(function(association, label) {
- return association.apply(err, record);
- }) : void 0;
- });
- }
- record = _this.constructor._mapIdentity(record);
- _this.get('lifecycle').startTransition(endState);
- } else {
- if (err instanceof Batman.ErrorsSet) {
- _this.get('lifecycle').failedValidation();
- } else {
- _this.get('lifecycle').error();
- }
- }
- return typeof callback === "function" ? callback(err, record || _this, env) : void 0;
- });
- } else {
- return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't save while in state " + (_this.get('lifecycle.state')))) : void 0;
- }
- });
- };
-
- Model.prototype.destroy = function(options, callback) {
- var _ref1,
- _this = this;
- if (!callback) {
- _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1];
- }
- if (this.get('lifecycle').destroy()) {
- return this._doStorageOperation('destroy', {
- data: options
- }, function(err, record, env) {
- if (!err) {
- _this.constructor.get('loaded').remove(_this);
- _this.get('lifecycle').destroyed();
- } else {
- _this.get('lifecycle').error();
- }
- return typeof callback === "function" ? callback(err, record, env) : void 0;
- });
- } else {
- return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't destroy while in state " + (this.get('lifecycle.state')))) : void 0;
- }
- };
-
- Model.prototype.validate = function(callback) {
- var args, count, errors, finishedValidation, key, validator, validators, _j, _k, _len1, _len2, _ref1;
- errors = this.get('errors');
- errors.clear();
- validators = this._batman.get('validators') || [];
- if (!validators || validators.length === 0) {
- if (typeof callback === "function") {
- callback(void 0, errors);
- }
- return true;
- }
- count = validators.reduce((function(acc, validator) {
- return acc + validator.keys.length;
- }), 0);
- finishedValidation = function() {
- if (--count === 0) {
- return typeof callback === "function" ? callback(void 0, errors) : void 0;
- }
- };
- for (_j = 0, _len1 = validators.length; _j < _len1; _j++) {
- validator = validators[_j];
- _ref1 = validator.keys;
- for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
- key = _ref1[_k];
- args = [errors, this, key, finishedValidation];
- try {
- if (validator.validator) {
- validator.validator.validateEach.apply(validator.validator, args);
- } else {
- validator.callback.apply(validator, args);
- }
- } catch (e) {
- if (typeof callback === "function") {
- callback(e, errors);
- }
- }
- }
- }
- };
-
- Model.prototype.associationProxy = function(association) {
- var proxies, _base, _name;
- Batman.initializeObject(this);
- proxies = (_base = this._batman).associationProxies || (_base.associationProxies = {});
- proxies[_name = association.label] || (proxies[_name] = new association.proxyClass(association, this));
- return proxies[association.label];
- };
-
- Model.prototype._willSet = function(key) {
- if (this._pauseDirtyTracking) {
- return true;
- }
- if (this.get('lifecycle').startTransition('set')) {
- if (!this.get('_dirtiedKeys').has(key)) {
- this.set("dirtyKeys." + key, this.get(key));
- this.get('_dirtiedKeys').add(key);
- }
- return true;
- } else {
- return false;
- }
- };
-
- Model.prototype._doStorageOperation = function(operation, options, callback) {
- var adapter,
- _this = this;
- Batman.developer.assert(this.hasStorage(), "Can't " + operation + " model " + (Batman.functionName(this.constructor)) + " without any storage adapters!");
- adapter = this._batman.get('storage');
- return adapter.perform(operation, this, options, function() {
- return callback.apply(null, arguments);
- });
- };
-
- Model.prototype._withoutDirtyTracking = function(block) {
- var result;
- this._pauseDirtyTracking = true;
- result = block.call(this);
- this._pauseDirtyTracking = false;
- return result;
- };
-
- _ref1 = ['load', 'save', 'validate', 'destroy'];
- for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
- functionName = _ref1[_j];
- Model.prototype[functionName] = Batman.Property.wrapTrackingPrevention(Model.prototype[functionName]);
- }
-
- return Model;
-
- }).call(this, Batman.Object);
-
-}).call(this);
-
-(function() {
- var k, _fn, _i, _len, _ref,
- _this = this;
-
- _ref = Batman.AssociationCurator.availableAssociations;
- _fn = function(k) {
- return Batman.Model[k] = function(label, scope) {
- var collection, _base;
- Batman.initializeObject(this);
- collection = (_base = this._batman).associations || (_base.associations = new Batman.AssociationCurator(this));
- return collection.add(new Batman["" + (Batman.helpers.capitalize(k)) + "Association"](this, label, scope));
- };
- };
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- k = _ref[_i];
- _fn(k);
- }
-
-}).call(this);
-
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __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; };
-
Batman.ParamsReplacer = (function(_super) {
__extends(ParamsReplacer, _super);
@@ -6863,7 +5879,7 @@
};
Dispatcher.prototype.dispatch = function(params) {
- var inferredParams, path, route, _ref;
+ var error, inferredParams, path, route, _ref, _ref1;
inferredParams = this.constructor.paramsFromArgument(params);
route = this.routeForParams(inferredParams);
if (route) {
@@ -6878,6 +5894,19 @@
} else {
this.get('app.currentParams').clear();
}
+ error = {
+ type: '404',
+ isPrevented: false,
+ preventDefault: function() {
+ return this.isPrevented = true;
+ }
+ };
+ if ((_ref1 = Batman.currentApp) != null) {
+ _ref1.fire('error', error);
+ }
+ if (error.isPrevented) {
+ return params;
+ }
if (params !== '/404') {
return Batman.redirect('/404');
}
@@ -6924,6 +5953,7 @@
}
pattern = templatePath.replace(regexps.escapeRegExp, '\\$&');
regexp = RegExp("^" + (pattern.replace(regexps.openOptParam, '(?:').replace(regexps.closeOptParam, ')?').replace(regexps.namedParam, '([^\/]+)').replace(regexps.splatParam, '(.*?)')) + regexps.queryParam + "$");
+ regexps.namedOrSplat.lastIndex = 0;
namedArguments = ((function() {
var _results;
_results = [];
@@ -7401,6 +6431,7 @@
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__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; },
+ __slice = [].slice,
__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; };
Batman.Controller = (function(_super) {
@@ -7455,16 +6486,16 @@
var filters, options, _base;
Batman.initializeObject(this);
options = _optionsFromFilterArguments.apply(null, arguments);
- filters = (_base = this._batman).beforeFilters || (_base.beforeFilters = new Batman.SimpleHash);
- return filters.set(options.block, options);
+ filters = (_base = this._batman).beforeFilters || (_base.beforeFilters = []);
+ return filters.push(options);
};
Controller.afterFilter = function() {
var filters, options, _base;
Batman.initializeObject(this);
options = _optionsFromFilterArguments.apply(null, arguments);
- filters = (_base = this._batman).afterFilters || (_base.afterFilters = new Batman.SimpleHash);
- return filters.set(options.block, options);
+ filters = (_base = this._batman).afterFilters || (_base.afterFilters = []);
+ return filters.push(options);
};
Controller.afterFilter(function(params) {
@@ -7473,8 +6504,71 @@
}
});
+ Controller.catchError = function() {
+ var currentHandlers, error, errors, handlers, options, _base, _i, _j, _len, _results;
+ errors = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), options = arguments[_i++];
+ Batman.initializeObject(this);
+ (_base = this._batman).errorHandlers || (_base.errorHandlers = new Batman.SimpleHash);
+ handlers = Batman.typeOf(options["with"]) === 'Array' ? options["with"] : [options["with"]];
+ _results = [];
+ for (_j = 0, _len = errors.length; _j < _len; _j++) {
+ error = errors[_j];
+ currentHandlers = this._batman.errorHandlers.get(error) || [];
+ _results.push(this._batman.errorHandlers.set(error, currentHandlers.concat(handlers)));
+ }
+ return _results;
+ };
+
+ Controller.prototype.errorHandler = function(callback) {
+ var errorFrame, _ref,
+ _this = this;
+ errorFrame = (_ref = this._actionFrames) != null ? _ref[this._actionFrames.length - 1] : void 0;
+ return function(err, result, env) {
+ if (err) {
+ if (errorFrame != null ? errorFrame.error : void 0) {
+ return;
+ }
+ if (errorFrame != null) {
+ errorFrame.error = err;
+ }
+ if (!_this.handleError(err)) {
+ throw err;
+ }
+ } else {
+ return typeof callback === "function" ? callback(result, env) : void 0;
+ }
+ };
+ };
+
+ Controller.prototype.handleError = function(error) {
+ var handled, _ref,
+ _this = this;
+ handled = false;
+ if ((_ref = this.constructor._batman.getAll('errorHandlers')) != null) {
+ _ref.forEach(function(hash) {
+ return hash.forEach(function(key, value) {
+ var handler, _i, _len, _results;
+ if (error instanceof key) {
+ handled = true;
+ _results = [];
+ for (_i = 0, _len = value.length; _i < _len; _i++) {
+ handler = value[_i];
+ _results.push(handler.call(_this, error));
+ }
+ return _results;
+ }
+ });
+ });
+ }
+ return handled;
+ };
+
function Controller() {
this.redirect = __bind(this.redirect, this);
+
+ this.handleError = __bind(this.handleError, this);
+
+ this.errorHandler = __bind(this.errorHandler, this);
Controller.__super__.constructor.apply(this, arguments);
this._resetActionFrames();
}
@@ -7519,7 +6613,9 @@
action: action
}, function() {
var _ref;
- _this._runFilters(action, params, 'afterFilters');
+ if (!_this._afterFilterRedirect) {
+ _this._runFilters(action, params, 'afterFilters');
+ }
_this._resetActionFrames();
return (_ref = Batman.navigator) != null ? _ref.redirect = oldRedirect : void 0;
});
@@ -7532,7 +6628,9 @@
_ref1.redirect = this.redirect;
}
this._runFilters(action, params, 'beforeFilters');
- result = this[action](params);
+ if (!this._afterFilterRedirect) {
+ result = this[action](params);
+ }
if (!frame.operationOccurred) {
this.render();
}
@@ -7623,24 +6721,28 @@
};
Controller.prototype._runFilters = function(action, params, filters) {
- var _ref,
- _this = this;
+ var block, options, _i, _len, _ref;
if (filters = (_ref = this.constructor._batman) != null ? _ref.get(filters) : void 0) {
- return filters.forEach(function(_, options) {
- var block;
+ for (_i = 0, _len = filters.length; _i < _len; _i++) {
+ options = filters[_i];
if (options.only && __indexOf.call(options.only, action) < 0) {
- return;
+ continue;
}
if (options.except && __indexOf.call(options.except, action) >= 0) {
+ continue;
+ }
+ if (this._afterFilterRedirect) {
return;
}
block = options.block;
if (typeof block === 'function') {
- return block.call(_this, params);
+ block.call(this, params);
} else {
- return typeof _this[block] === "function" ? _this[block](params) : void 0;
+ if (typeof this[block] === "function") {
+ this[block](params);
+ }
}
- });
+ }
}
};
@@ -7824,6 +6926,8 @@
return result;
};
+ Batman.Set._applySetAccessors(SetProxy);
+
_ref = ['add', 'remove', 'find', 'clear', 'has', 'merge', 'toArray', 'isEmpty', 'indexedBy', 'indexedByUnique', 'sortedBy'];
_fn = function(k) {
return SetProxy.prototype[k] = function() {
@@ -7836,8 +6940,6 @@
_fn(k);
}
- Batman.Set._applySetAccessors(SetProxy);
-
SetProxy.accessor('length', {
get: function() {
this.registerAsMutableSource();
@@ -8083,8 +7185,1177 @@
}).call(this);
(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; },
+ __slice = [].slice;
- Batman.mixins = new Batman.Object;
+ Batman.StateMachine = (function(_super) {
+
+ __extends(StateMachine, _super);
+
+ StateMachine.InvalidTransitionError = function(message) {
+ this.message = message != null ? message : "";
+ };
+
+ StateMachine.InvalidTransitionError.prototype = new Error;
+
+ StateMachine.transitions = function(table) {
+ var definePredicate, fromState, k, object, predicateKeys, toState, transitions, v, _fn, _ref,
+ _this = this;
+ for (k in table) {
+ v = table[k];
+ if (!(v.from && v.to)) {
+ continue;
+ }
+ object = {};
+ if (v.from.forEach) {
+ v.from.forEach(function(fromKey) {
+ return object[fromKey] = v.to;
+ });
+ } else {
+ object[v.from] = v.to;
+ }
+ table[k] = object;
+ }
+ this.prototype.transitionTable = Batman.extend({}, this.prototype.transitionTable, table);
+ predicateKeys = [];
+ definePredicate = function(state) {
+ var key;
+ key = "is" + (Batman.helpers.capitalize(state));
+ if (_this.prototype[key] != null) {
+ return;
+ }
+ predicateKeys.push(key);
+ return _this.prototype[key] = function() {
+ return this.get('state') === state;
+ };
+ };
+ _ref = this.prototype.transitionTable;
+ _fn = function(k) {
+ return _this.prototype[k] = function() {
+ return this.startTransition(k);
+ };
+ };
+ for (k in _ref) {
+ transitions = _ref[k];
+ if (!(!this.prototype[k])) {
+ continue;
+ }
+ _fn(k);
+ for (fromState in transitions) {
+ toState = transitions[fromState];
+ definePredicate(fromState);
+ definePredicate(toState);
+ }
+ }
+ if (predicateKeys.length) {
+ this.accessor.apply(this, __slice.call(predicateKeys).concat([function(key) {
+ return this[key]();
+ }]));
+ }
+ return this;
+ };
+
+ function StateMachine(startState) {
+ this.nextEvents = [];
+ this.set('_state', startState);
+ }
+
+ StateMachine.accessor('state', function() {
+ return this.get('_state');
+ });
+
+ StateMachine.prototype.isTransitioning = false;
+
+ StateMachine.prototype.transitionTable = {};
+
+ StateMachine.prototype.onTransition = function(from, into, callback) {
+ return this.on("" + from + "->" + into, callback);
+ };
+
+ StateMachine.prototype.onEnter = function(into, callback) {
+ return this.on("enter " + into, callback);
+ };
+
+ StateMachine.prototype.onExit = function(from, callback) {
+ return this.on("exit " + from, callback);
+ };
+
+ StateMachine.prototype.startTransition = Batman.Property.wrapTrackingPrevention(function(event) {
+ var nextState, previousState;
+ if (this.isTransitioning) {
+ this.nextEvents.push(event);
+ return;
+ }
+ previousState = this.get('state');
+ nextState = this.nextStateForEvent(event);
+ if (!nextState) {
+ return false;
+ }
+ this.isTransitioning = true;
+ this.fire("exit " + previousState);
+ this.set('_state', nextState);
+ this.fire("" + previousState + "->" + nextState);
+ this.fire("enter " + nextState);
+ this.fire(event);
+ this.isTransitioning = false;
+ if (this.nextEvents.length > 0) {
+ this.startTransition(this.nextEvents.shift());
+ }
+ return true;
+ });
+
+ StateMachine.prototype.canStartTransition = function(event, fromState) {
+ if (fromState == null) {
+ fromState = this.get('state');
+ }
+ return !!this.nextStateForEvent(event, fromState);
+ };
+
+ StateMachine.prototype.nextStateForEvent = function(event, fromState) {
+ var _ref;
+ if (fromState == null) {
+ fromState = this.get('state');
+ }
+ return (_ref = this.transitionTable[event]) != null ? _ref[fromState] : void 0;
+ };
+
+ return StateMachine;
+
+ })(Batman.Object);
+
+ Batman.DelegatingStateMachine = (function(_super) {
+
+ __extends(DelegatingStateMachine, _super);
+
+ function DelegatingStateMachine(startState, base) {
+ this.base = base;
+ DelegatingStateMachine.__super__.constructor.call(this, startState);
+ }
+
+ DelegatingStateMachine.prototype.fire = function() {
+ var result, _ref;
+ result = DelegatingStateMachine.__super__.fire.apply(this, arguments);
+ (_ref = this.base).fire.apply(_ref, arguments);
+ return result;
+ };
+
+ return DelegatingStateMachine;
+
+ })(Batman.StateMachine);
+
+}).call(this);
+
+(function() {
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+ __hasProp = {}.hasOwnProperty,
+ __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; },
+ __slice = [].slice;
+
+ Batman.Model = (function(_super) {
+ var functionName, _i, _j, _len, _len1, _ref, _ref1;
+
+ __extends(Model, _super);
+
+ Model.storageKey = null;
+
+ Model.primaryKey = 'id';
+
+ Model.persist = function() {
+ var mechanism, options;
+ mechanism = arguments[0], options = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ Batman.initializeObject(this.prototype);
+ mechanism = mechanism.isStorageAdapter ? mechanism : new mechanism(this);
+ if (options.length > 0) {
+ Batman.mixin.apply(Batman, [mechanism].concat(__slice.call(options)));
+ }
+ this.prototype._batman.storage = mechanism;
+ return mechanism;
+ };
+
+ Model.storageAdapter = function() {
+ Batman.initializeObject(this.prototype);
+ return this.prototype._batman.storage;
+ };
+
+ Model.encode = function() {
+ var encoder, encoderForKey, encoderOrLastKey, key, keys, _base, _i, _j, _len;
+ keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), encoderOrLastKey = arguments[_i++];
+ Batman.initializeObject(this.prototype);
+ (_base = this.prototype._batman).encoders || (_base.encoders = new Batman.SimpleHash);
+ encoder = {};
+ switch (Batman.typeOf(encoderOrLastKey)) {
+ case 'String':
+ keys.push(encoderOrLastKey);
+ break;
+ case 'Function':
+ encoder.encode = encoderOrLastKey;
+ break;
+ default:
+ encoder = encoderOrLastKey;
+ }
+ for (_j = 0, _len = keys.length; _j < _len; _j++) {
+ key = keys[_j];
+ encoderForKey = Batman.extend({
+ as: key
+ }, this.defaultEncoder, encoder);
+ this.prototype._batman.encoders.set(key, encoderForKey);
+ }
+ };
+
+ Model.defaultEncoder = {
+ encode: function(x) {
+ return x;
+ },
+ decode: function(x) {
+ return x;
+ }
+ };
+
+ Model.observeAndFire('primaryKey', function(newPrimaryKey, oldPrimaryKey) {
+ this.encode(oldPrimaryKey, {
+ encode: false,
+ decode: false
+ });
+ return this.encode(newPrimaryKey, {
+ encode: false,
+ decode: this.defaultEncoder.decode
+ });
+ });
+
+ Model.validate = function() {
+ var keys, matches, optionsOrFunction, validatorClass, validators, _base, _i, _j, _len, _ref, _results;
+ keys = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), optionsOrFunction = arguments[_i++];
+ Batman.initializeObject(this.prototype);
+ validators = (_base = this.prototype._batman).validators || (_base.validators = []);
+ if (typeof optionsOrFunction === 'function') {
+ return validators.push({
+ keys: keys,
+ callback: optionsOrFunction
+ });
+ } else {
+ _ref = Batman.Validators;
+ _results = [];
+ for (_j = 0, _len = _ref.length; _j < _len; _j++) {
+ validatorClass = _ref[_j];
+ if ((matches = validatorClass.matches(optionsOrFunction))) {
+ _results.push(validators.push({
+ keys: keys,
+ validator: new validatorClass(matches)
+ }));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ }
+ };
+
+ Model.classAccessor('resourceName', {
+ get: function() {
+ if (this.resourceName != null) {
+ return this.resourceName;
+ } else {
+ if (Batman.config.minificationErrors) {
+ Batman.developer.error("Please define " + (Batman.functionName(this)) + ".resourceName in order for your model to be minification safe.");
+ }
+ return Batman.helpers.underscore(Batman.functionName(this));
+ }
+ }
+ });
+
+ Model.classAccessor('all', {
+ get: function() {
+ this._batman.check(this);
+ if (this.prototype.hasStorage() && !this._batman.allLoadTriggered) {
+ this.load();
+ this._batman.allLoadTriggered = true;
+ }
+ return this.get('loaded');
+ },
+ set: function(k, v) {
+ return this.set('loaded', v);
+ }
+ });
+
+ Model.classAccessor('loaded', {
+ get: function() {
+ return this._loaded || (this._loaded = new Batman.Set);
+ },
+ set: function(k, v) {
+ return this._loaded = v;
+ }
+ });
+
+ Model.classAccessor('first', function() {
+ return this.get('all').toArray()[0];
+ });
+
+ Model.classAccessor('last', function() {
+ var x;
+ x = this.get('all').toArray();
+ return x[x.length - 1];
+ });
+
+ Model.clear = function() {
+ var result, _ref;
+ Batman.initializeObject(this);
+ result = this.get('loaded').clear();
+ if ((_ref = this._batman.get('associations')) != null) {
+ _ref.reset();
+ }
+ this._resetPromises();
+ return result;
+ };
+
+ Model.find = function(id, callback) {
+ return this.findWithOptions(id, void 0, callback);
+ };
+
+ Model.findWithOptions = function(id, options, callback) {
+ var record;
+ if (options == null) {
+ options = {};
+ }
+ Batman.developer.assert(callback, "Must call find with a callback!");
+ record = new this();
+ record._withoutDirtyTracking(function() {
+ return this.set('id', id);
+ });
+ record.loadWithOptions(options, callback);
+ return record;
+ };
+
+ Model.load = function(options, callback) {
+ var _ref;
+ if ((_ref = typeof options) === 'function' || _ref === 'undefined') {
+ callback = options;
+ options = {};
+ } else {
+ options = {
+ data: options
+ };
+ }
+ return this.loadWithOptions(options, callback);
+ };
+
+ Model.loadWithOptions = function(options, callback) {
+ var _this = this;
+ this.fire('loading', options);
+ return this._doStorageOperation('readAll', options, function(err, records, env) {
+ var mappedRecords, record;
+ if (err != null) {
+ _this.fire('error', err);
+ return typeof callback === "function" ? callback(err, []) : void 0;
+ } else {
+ mappedRecords = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = records.length; _i < _len; _i++) {
+ record = records[_i];
+ _results.push(this._mapIdentity(record));
+ }
+ return _results;
+ }).call(_this);
+ _this.fire('loaded', mappedRecords, env);
+ return typeof callback === "function" ? callback(err, mappedRecords, env) : void 0;
+ }
+ });
+ };
+
+ Model.create = function(attrs, callback) {
+ var obj, _ref;
+ if (!callback) {
+ _ref = [{}, attrs], attrs = _ref[0], callback = _ref[1];
+ }
+ obj = new this(attrs);
+ obj.save(callback);
+ return obj;
+ };
+
+ Model.findOrCreate = function(attrs, callback) {
+ var foundRecord, record;
+ record = new this(attrs);
+ if (record.isNew()) {
+ record.save(callback);
+ } else {
+ foundRecord = this._mapIdentity(record);
+ callback(void 0, foundRecord);
+ }
+ return record;
+ };
+
+ Model.createFromJSON = function(json) {
+ var record;
+ record = new this;
+ record._withoutDirtyTracking(function() {
+ return this.fromJSON(json);
+ });
+ return this._mapIdentity(record);
+ };
+
+ Model._mapIdentity = function(record) {
+ var existing, id, _ref;
+ if (typeof (id = record.get('id')) === 'undefined' || id === '') {
+ return record;
+ } else {
+ existing = (_ref = this.get("loaded.indexedBy.id").get(id)) != null ? _ref.toArray()[0] : void 0;
+ if (existing) {
+ existing._withoutDirtyTracking(function() {
+ var _ref1;
+ return this.updateAttributes(((_ref1 = record.get('attributes')) != null ? _ref1.toObject() : void 0) || {});
+ });
+ return existing;
+ } else {
+ this.get('loaded').add(record);
+ return record;
+ }
+ }
+ };
+
+ Model._doStorageOperation = function(operation, options, callback) {
+ var adapter;
+ Batman.developer.assert(this.prototype.hasStorage(), "Can't " + operation + " model " + (Batman.functionName(this.constructor)) + " without any storage adapters!");
+ adapter = this.prototype._batman.get('storage');
+ return adapter.perform(operation, this, options, callback);
+ };
+
+ _ref = ['find', 'load', 'create'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ functionName = _ref[_i];
+ Model[functionName] = Batman.Property.wrapTrackingPrevention(Model[functionName]);
+ }
+
+ Model.InstanceLifecycleStateMachine = (function(_super1) {
+
+ __extends(InstanceLifecycleStateMachine, _super1);
+
+ function InstanceLifecycleStateMachine() {
+ return InstanceLifecycleStateMachine.__super__.constructor.apply(this, arguments);
+ }
+
+ InstanceLifecycleStateMachine.transitions({
+ load: {
+ from: ['dirty', 'clean'],
+ to: 'loading'
+ },
+ create: {
+ from: ['dirty', 'clean'],
+ to: 'creating'
+ },
+ save: {
+ from: ['dirty', 'clean'],
+ to: 'saving'
+ },
+ destroy: {
+ from: ['dirty', 'clean'],
+ to: 'destroying'
+ },
+ failedValidation: {
+ from: ['saving', 'creating'],
+ to: 'dirty'
+ },
+ loaded: {
+ loading: 'clean'
+ },
+ created: {
+ creating: 'clean'
+ },
+ saved: {
+ saving: 'clean'
+ },
+ destroyed: {
+ destroying: 'destroyed'
+ },
+ set: {
+ from: ['dirty', 'clean'],
+ to: 'dirty'
+ },
+ error: {
+ from: ['saving', 'creating', 'loading', 'destroying'],
+ to: 'error'
+ }
+ });
+
+ return InstanceLifecycleStateMachine;
+
+ })(Batman.DelegatingStateMachine);
+
+ function Model(idOrAttributes) {
+ if (idOrAttributes == null) {
+ idOrAttributes = {};
+ }
+ this.destroy = __bind(this.destroy, this);
+
+ this.save = __bind(this.save, this);
+
+ this.loadWithOptions = __bind(this.loadWithOptions, this);
+
+ this.load = __bind(this.load, this);
+
+ Batman.developer.assert(this instanceof Batman.Object, "constructors must be called with new");
+ if (Batman.typeOf(idOrAttributes) === 'Object') {
+ Model.__super__.constructor.call(this, idOrAttributes);
+ } else {
+ Model.__super__.constructor.call(this);
+ this.set('id', idOrAttributes);
+ }
+ }
+
+ Model.accessor('lifecycle', function() {
+ return this.lifecycle || (this.lifecycle = new Batman.Model.InstanceLifecycleStateMachine('clean', this));
+ });
+
+ Model.accessor('attributes', function() {
+ return this.attributes || (this.attributes = new Batman.Hash);
+ });
+
+ Model.accessor('dirtyKeys', function() {
+ return this.dirtyKeys || (this.dirtyKeys = new Batman.Hash);
+ });
+
+ Model.accessor('_dirtiedKeys', function() {
+ return this._dirtiedKeys || (this._dirtiedKeys = new Batman.SimpleSet);
+ });
+
+ Model.accessor('errors', function() {
+ return this.errors || (this.errors = new Batman.ErrorsSet);
+ });
+
+ Model.accessor('isNew', function() {
+ return this.isNew();
+ });
+
+ Model.accessor('isDirty', function() {
+ return this.isDirty();
+ });
+
+ Model.accessor(Model.defaultAccessor = {
+ get: function(k) {
+ return Batman.getPath(this, ['attributes', k]);
+ },
+ set: function(k, v) {
+ if (this._willSet(k)) {
+ return this.get('attributes').set(k, v);
+ } else {
+ return this.get(k);
+ }
+ },
+ unset: function(k) {
+ return this.get('attributes').unset(k);
+ }
+ });
+
+ Model.wrapAccessor('id', function(core) {
+ return {
+ get: function() {
+ var primaryKey;
+ primaryKey = this.constructor.primaryKey;
+ if (primaryKey === 'id') {
+ return core.get.apply(this, arguments);
+ } else {
+ return this.get(primaryKey);
+ }
+ },
+ set: function(key, value) {
+ var parsedValue, primaryKey;
+ if ((typeof value === "string") && (value.match(/[^0-9]/) === null) && (("" + (parsedValue = parseInt(value, 10))) === value)) {
+ value = parsedValue;
+ }
+ primaryKey = this.constructor.primaryKey;
+ if (primaryKey === 'id') {
+ this._willSet(key);
+ return core.set.apply(this, arguments);
+ } else {
+ return this.set(primaryKey, value);
+ }
+ }
+ };
+ });
+
+ Model.prototype.isNew = function() {
+ return typeof this.get('id') === 'undefined';
+ };
+
+ Model.prototype.isDirty = function() {
+ return this.get('lifecycle.state') === 'dirty';
+ };
+
+ Model.prototype.updateAttributes = function(attrs) {
+ this.mixin(attrs);
+ return this;
+ };
+
+ Model.prototype.toString = function() {
+ return "" + (this.constructor.get('resourceName')) + ": " + (this.get('id'));
+ };
+
+ Model.prototype.toParam = function() {
+ return this.get('id');
+ };
+
+ Model.prototype.toJSON = function() {
+ var encoders, obj,
+ _this = this;
+ obj = {};
+ encoders = this._batman.get('encoders');
+ if (!(!encoders || encoders.isEmpty())) {
+ encoders.forEach(function(key, encoder) {
+ var encodedVal, val;
+ if (encoder.encode) {
+ val = _this.get(key);
+ if (typeof val !== 'undefined') {
+ encodedVal = encoder.encode(val, key, obj, _this);
+ if (typeof encodedVal !== 'undefined') {
+ return obj[encoder.as] = encodedVal;
+ }
+ }
+ }
+ });
+ }
+ return obj;
+ };
+
+ Model.prototype.fromJSON = function(data) {
+ var encoders, key, obj, value,
+ _this = this;
+ obj = {};
+ encoders = this._batman.get('encoders');
+ if (!encoders || encoders.isEmpty() || !encoders.some(function(key, encoder) {
+ return encoder.decode != null;
+ })) {
+ for (key in data) {
+ value = data[key];
+ obj[key] = value;
+ }
+ } else {
+ encoders.forEach(function(key, encoder) {
+ if (encoder.decode && typeof data[encoder.as] !== 'undefined') {
+ return obj[key] = encoder.decode(data[encoder.as], encoder.as, data, obj, _this);
+ }
+ });
+ }
+ if (this.constructor.primaryKey !== 'id') {
+ obj.id = data[this.constructor.primaryKey];
+ }
+ Batman.developer["do"](function() {
+ if ((!encoders) || encoders.length <= 1) {
+ return Batman.developer.warn("Warning: Model " + (Batman.functionName(_this.constructor)) + " has suspiciously few decoders!");
+ }
+ });
+ return this.mixin(obj);
+ };
+
+ Model.prototype.hasStorage = function() {
+ return this._batman.get('storage') != null;
+ };
+
+ Model.prototype.load = function(options, callback) {
+ var _ref1;
+ if (!callback) {
+ _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1];
+ } else {
+ options = {
+ data: options
+ };
+ }
+ return this.loadWithOptions(options, callback);
+ };
+
+ Model.prototype.loadWithOptions = function(options, callback) {
+ var callbackQueue, hasOptions, _ref1,
+ _this = this;
+ hasOptions = Object.keys(options).length !== 0;
+ if ((_ref1 = this.get('lifecycle.state')) === 'destroying' || _ref1 === 'destroyed') {
+ if (typeof callback === "function") {
+ callback(new Error("Can't load a destroyed record!"));
+ }
+ return;
+ }
+ if (this.get('lifecycle').load()) {
+ callbackQueue = [];
+ if (callback != null) {
+ callbackQueue.push(callback);
+ }
+ if (!hasOptions) {
+ this._currentLoad = callbackQueue;
+ }
+ return this._doStorageOperation('read', options, function(err, record, env) {
+ var _j, _len1;
+ if (!err) {
+ _this.get('lifecycle').loaded();
+ record = _this.constructor._mapIdentity(record);
+ } else {
+ _this.get('lifecycle').error();
+ }
+ if (!hasOptions) {
+ _this._currentLoad = null;
+ }
+ for (_j = 0, _len1 = callbackQueue.length; _j < _len1; _j++) {
+ callback = callbackQueue[_j];
+ callback(err, record, env);
+ }
+ });
+ } else {
+ if (this.get('lifecycle.state') === 'loading' && !hasOptions) {
+ if (callback != null) {
+ return this._currentLoad.push(callback);
+ }
+ } else {
+ return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't load while in state " + (this.get('lifecycle.state')))) : void 0;
+ }
+ }
+ };
+
+ Model.prototype.save = function(options, callback) {
+ var endState, isNew, startState, storageOperation, _ref1, _ref2,
+ _this = this;
+ if (!callback) {
+ _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1];
+ }
+ isNew = this.isNew();
+ _ref2 = isNew ? ['create', 'create', 'created'] : ['save', 'update', 'saved'], startState = _ref2[0], storageOperation = _ref2[1], endState = _ref2[2];
+ if (this.get('lifecycle').startTransition(startState)) {
+ return this.validate(function(error, errors) {
+ var associations;
+ if (error || errors.length) {
+ _this.get('lifecycle').failedValidation();
+ return typeof callback === "function" ? callback(error || errors, _this) : void 0;
+ }
+ associations = _this.constructor._batman.get('associations');
+ _this._withoutDirtyTracking(function() {
+ var _ref3,
+ _this = this;
+ return associations != null ? (_ref3 = associations.getByType('belongsTo')) != null ? _ref3.forEach(function(association, label) {
+ return association.apply(_this);
+ }) : void 0 : void 0;
+ });
+ return _this._doStorageOperation(storageOperation, {
+ data: options
+ }, function(err, record, env) {
+ if (!err) {
+ _this.get('dirtyKeys').clear();
+ _this.get('_dirtiedKeys').clear();
+ if (associations) {
+ record._withoutDirtyTracking(function() {
+ var _ref3, _ref4;
+ if ((_ref3 = associations.getByType('hasOne')) != null) {
+ _ref3.forEach(function(association, label) {
+ return association.apply(err, record);
+ });
+ }
+ return (_ref4 = associations.getByType('hasMany')) != null ? _ref4.forEach(function(association, label) {
+ return association.apply(err, record);
+ }) : void 0;
+ });
+ }
+ record = _this.constructor._mapIdentity(record);
+ _this.get('lifecycle').startTransition(endState);
+ } else {
+ if (err instanceof Batman.ErrorsSet) {
+ _this.get('lifecycle').failedValidation();
+ } else {
+ _this.get('lifecycle').error();
+ }
+ }
+ return typeof callback === "function" ? callback(err, record || _this, env) : void 0;
+ });
+ });
+ } else {
+ return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't save while in state " + (this.get('lifecycle.state')))) : void 0;
+ }
+ };
+
+ Model.prototype.destroy = function(options, callback) {
+ var _ref1,
+ _this = this;
+ if (!callback) {
+ _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1];
+ }
+ if (this.get('lifecycle').destroy()) {
+ return this._doStorageOperation('destroy', {
+ data: options
+ }, function(err, record, env) {
+ if (!err) {
+ _this.constructor.get('loaded').remove(_this);
+ _this.get('lifecycle').destroyed();
+ } else {
+ _this.get('lifecycle').error();
+ }
+ return typeof callback === "function" ? callback(err, record, env) : void 0;
+ });
+ } else {
+ return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't destroy while in state " + (this.get('lifecycle.state')))) : void 0;
+ }
+ };
+
+ Model.prototype.validate = function(callback) {
+ var args, count, errors, finishedValidation, key, validator, validators, _j, _k, _len1, _len2, _ref1;
+ errors = this.get('errors');
+ errors.clear();
+ validators = this._batman.get('validators') || [];
+ if (!validators || validators.length === 0) {
+ if (typeof callback === "function") {
+ callback(void 0, errors);
+ }
+ return true;
+ }
+ count = validators.reduce((function(acc, validator) {
+ return acc + validator.keys.length;
+ }), 0);
+ finishedValidation = function() {
+ if (--count === 0) {
+ return typeof callback === "function" ? callback(void 0, errors) : void 0;
+ }
+ };
+ for (_j = 0, _len1 = validators.length; _j < _len1; _j++) {
+ validator = validators[_j];
+ _ref1 = validator.keys;
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ key = _ref1[_k];
+ args = [errors, this, key, finishedValidation];
+ try {
+ if (validator.validator) {
+ validator.validator.validateEach.apply(validator.validator, args);
+ } else {
+ validator.callback.apply(validator, args);
+ }
+ } catch (e) {
+ if (typeof callback === "function") {
+ callback(e, errors);
+ }
+ }
+ }
+ }
+ };
+
+ Model.prototype.associationProxy = function(association) {
+ var proxies, _base, _name;
+ Batman.initializeObject(this);
+ proxies = (_base = this._batman).associationProxies || (_base.associationProxies = {});
+ proxies[_name = association.label] || (proxies[_name] = new association.proxyClass(association, this));
+ return proxies[association.label];
+ };
+
+ Model.prototype._willSet = function(key) {
+ if (this._pauseDirtyTracking) {
+ return true;
+ }
+ if (this.get('lifecycle').startTransition('set')) {
+ if (!this.get('_dirtiedKeys').has(key)) {
+ this.set("dirtyKeys." + key, this.get(key));
+ this.get('_dirtiedKeys').add(key);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ Model.prototype._doStorageOperation = function(operation, options, callback) {
+ var adapter,
+ _this = this;
+ Batman.developer.assert(this.hasStorage(), "Can't " + operation + " model " + (Batman.functionName(this.constructor)) + " without any storage adapters!");
+ adapter = this._batman.get('storage');
+ return adapter.perform(operation, this, options, function() {
+ return callback.apply(null, arguments);
+ });
+ };
+
+ Model.prototype._withoutDirtyTracking = function(block) {
+ var result;
+ this._pauseDirtyTracking = true;
+ result = block.call(this);
+ this._pauseDirtyTracking = false;
+ return result;
+ };
+
+ _ref1 = ['load', 'save', 'validate', 'destroy'];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ functionName = _ref1[_j];
+ Model.prototype[functionName] = Batman.Property.wrapTrackingPrevention(Model.prototype[functionName]);
+ }
+
+ return Model;
+
+ }).call(this, Batman.Object);
+
+}).call(this);
+
+(function() {
+ var k, _fn, _i, _len, _ref,
+ _this = this;
+
+ _ref = Batman.AssociationCurator.availableAssociations;
+ _fn = function(k) {
+ return Batman.Model[k] = function(label, scope) {
+ var collection, _base;
+ Batman.initializeObject(this);
+ collection = (_base = this._batman).associations || (_base.associations = new Batman.AssociationCurator(this));
+ return collection.add(new Batman["" + (Batman.helpers.capitalize(k)) + "Association"](this, label, scope));
+ };
+ };
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ k = _ref[_i];
+ _fn(k);
+ }
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.Proxy = (function(_super) {
+
+ __extends(Proxy, _super);
+
+ Proxy.prototype.isProxy = true;
+
+ function Proxy(target) {
+ Proxy.__super__.constructor.call(this);
+ if (target != null) {
+ this.set('target', target);
+ }
+ }
+
+ Proxy.accessor('target', Batman.Property.defaultAccessor);
+
+ Proxy.accessor({
+ get: function(key) {
+ var _ref;
+ return (_ref = this.get('target')) != null ? _ref.get(key) : void 0;
+ },
+ set: function(key, value) {
+ var _ref;
+ return (_ref = this.get('target')) != null ? _ref.set(key, value) : void 0;
+ },
+ unset: function(key) {
+ var _ref;
+ return (_ref = this.get('target')) != null ? _ref.unset(key) : void 0;
+ }
+ });
+
+ return Proxy;
+
+ })(Batman.Object);
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.AssociationProxy = (function(_super) {
+
+ __extends(AssociationProxy, _super);
+
+ AssociationProxy.prototype.loaded = false;
+
+ function AssociationProxy(association, model) {
+ this.association = association;
+ this.model = model;
+ AssociationProxy.__super__.constructor.call(this);
+ }
+
+ AssociationProxy.prototype.toJSON = function() {
+ var target;
+ target = this.get('target');
+ if (target != null) {
+ return this.get('target').toJSON();
+ }
+ };
+
+ AssociationProxy.prototype.load = function(callback) {
+ var _this = this;
+ this.fetch(function(err, proxiedRecord) {
+ if (!err) {
+ _this._setTarget(proxiedRecord);
+ }
+ return typeof callback === "function" ? callback(err, proxiedRecord) : void 0;
+ });
+ return this.get('target');
+ };
+
+ AssociationProxy.prototype.loadFromLocal = function() {
+ var target;
+ if (!this._canLoad()) {
+ return;
+ }
+ if (target = this.fetchFromLocal()) {
+ this._setTarget(target);
+ }
+ return target;
+ };
+
+ AssociationProxy.prototype.fetch = function(callback) {
+ var record;
+ if (!this._canLoad()) {
+ return callback(void 0, void 0);
+ }
+ record = this.fetchFromLocal();
+ if (record) {
+ return callback(void 0, record);
+ } else {
+ return this.fetchFromRemote(callback);
+ }
+ };
+
+ AssociationProxy.accessor('loaded', Batman.Property.defaultAccessor);
+
+ AssociationProxy.accessor('target', {
+ get: function() {
+ return this.fetchFromLocal();
+ },
+ set: function(_, v) {
+ return v;
+ }
+ });
+
+ AssociationProxy.prototype._canLoad = function() {
+ return (this.get('foreignValue') || this.get('primaryValue')) != null;
+ };
+
+ AssociationProxy.prototype._setTarget = function(target) {
+ this.set('target', target);
+ this.set('loaded', true);
+ return this.fire('loaded', target);
+ };
+
+ return AssociationProxy;
+
+ })(Batman.Proxy);
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.HasOneProxy = (function(_super) {
+
+ __extends(HasOneProxy, _super);
+
+ function HasOneProxy() {
+ return HasOneProxy.__super__.constructor.apply(this, arguments);
+ }
+
+ HasOneProxy.accessor('primaryValue', function() {
+ return this.model.get(this.association.primaryKey);
+ });
+
+ HasOneProxy.prototype.fetchFromLocal = function() {
+ return this.association.setIndex().get(this.get('primaryValue'));
+ };
+
+ HasOneProxy.prototype.fetchFromRemote = function(callback) {
+ var loadOptions,
+ _this = this;
+ loadOptions = {
+ data: {}
+ };
+ loadOptions.data[this.association.foreignKey] = this.get('primaryValue');
+ if (this.association.options.url) {
+ loadOptions.collectionUrl = this.association.options.url;
+ loadOptions.urlContext = this.model;
+ }
+ return this.association.getRelatedModel().loadWithOptions(loadOptions, function(error, loadedRecords) {
+ if (error) {
+ throw error;
+ }
+ if (!loadedRecords || loadedRecords.length <= 0) {
+ return callback(new Error("Couldn't find related record!"), void 0);
+ } else {
+ return callback(void 0, loadedRecords[0]);
+ }
+ });
+ };
+
+ return HasOneProxy;
+
+ })(Batman.AssociationProxy);
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.BelongsToProxy = (function(_super) {
+
+ __extends(BelongsToProxy, _super);
+
+ function BelongsToProxy() {
+ return BelongsToProxy.__super__.constructor.apply(this, arguments);
+ }
+
+ BelongsToProxy.accessor('foreignValue', function() {
+ return this.model.get(this.association.foreignKey);
+ });
+
+ BelongsToProxy.prototype.fetchFromLocal = function() {
+ return this.association.setIndex().get(this.get('foreignValue'));
+ };
+
+ BelongsToProxy.prototype.fetchFromRemote = function(callback) {
+ var loadOptions,
+ _this = this;
+ loadOptions = {};
+ if (this.association.options.url) {
+ loadOptions.recordUrl = this.association.options.url;
+ }
+ return this.association.getRelatedModel().findWithOptions(this.get('foreignValue'), loadOptions, function(error, loadedRecord) {
+ if (error) {
+ throw error;
+ }
+ return callback(void 0, loadedRecord);
+ });
+ };
+
+ return BelongsToProxy;
+
+ })(Batman.AssociationProxy);
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.PolymorphicBelongsToProxy = (function(_super) {
+
+ __extends(PolymorphicBelongsToProxy, _super);
+
+ function PolymorphicBelongsToProxy() {
+ return PolymorphicBelongsToProxy.__super__.constructor.apply(this, arguments);
+ }
+
+ PolymorphicBelongsToProxy.accessor('foreignTypeValue', function() {
+ return this.model.get(this.association.foreignTypeKey);
+ });
+
+ PolymorphicBelongsToProxy.prototype.fetchFromLocal = function() {
+ return this.association.setIndexForType(this.get('foreignTypeValue')).get(this.get('foreignValue'));
+ };
+
+ PolymorphicBelongsToProxy.prototype.fetchFromRemote = function(callback) {
+ var loadOptions,
+ _this = this;
+ loadOptions = {};
+ if (this.association.options.url) {
+ loadOptions.recordUrl = this.association.options.url;
+ }
+ return this.association.getRelatedModelForType(this.get('foreignTypeValue')).findWithOptions(this.get('foreignValue'), loadOptions, function(error, loadedRecord) {
+ if (error) {
+ throw error;
+ }
+ return callback(void 0, loadedRecord);
+ });
+ };
+
+ return PolymorphicBelongsToProxy;
+
+ })(Batman.BelongsToProxy);
}).call(this);
@@ -8121,6 +8392,358 @@
}).call(this);
(function() {
+
+ Batman.mixins = new Batman.Object;
+
+}).call(this);
+
+(function() {
+
+ Batman.URI = (function() {
+ /*
+ # URI parsing
+ */
+
+ var attributes, childKeyMatchers, decodeQueryComponent, encodeComponent, encodeQueryComponent, keyVal, nameParser, normalizeParams, plus, queryFromParams, r20, strictParser;
+
+ strictParser = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/;
+
+ attributes = ["source", "protocol", "authority", "userInfo", "user", "password", "hostname", "port", "relative", "path", "directory", "file", "query", "hash"];
+
+ function URI(str) {
+ var i, matches;
+ matches = strictParser.exec(str);
+ i = 14;
+ while (i--) {
+ this[attributes[i]] = matches[i] || '';
+ }
+ this.queryParams = this.constructor.paramsFromQuery(this.query);
+ delete this.authority;
+ delete this.userInfo;
+ delete this.relative;
+ delete this.directory;
+ delete this.file;
+ delete this.query;
+ }
+
+ URI.prototype.queryString = function() {
+ return this.constructor.queryFromParams(this.queryParams);
+ };
+
+ URI.prototype.toString = function() {
+ return [this.protocol ? "" + this.protocol + ":" : void 0, this.authority() ? "//" : void 0, this.authority(), this.relative()].join("");
+ };
+
+ URI.prototype.userInfo = function() {
+ return [this.user, this.password ? ":" + this.password : void 0].join("");
+ };
+
+ URI.prototype.authority = function() {
+ return [this.userInfo(), this.user || this.password ? "@" : void 0, this.hostname, this.port ? ":" + this.port : void 0].join("");
+ };
+
+ URI.prototype.relative = function() {
+ var query;
+ query = this.queryString();
+ return [this.path, query ? "?" + query : void 0, this.hash ? "#" + this.hash : void 0].join("");
+ };
+
+ URI.prototype.directory = function() {
+ var splitPath;
+ splitPath = this.path.split('/');
+ if (splitPath.length > 1) {
+ return splitPath.slice(0, splitPath.length - 1).join('/') + "/";
+ } else {
+ return "";
+ }
+ };
+
+ URI.prototype.file = function() {
+ var splitPath;
+ splitPath = this.path.split("/");
+ return splitPath[splitPath.length - 1];
+ };
+
+ /*
+ # query parsing
+ */
+
+
+ URI.paramsFromQuery = function(query) {
+ var matches, params, segment, _i, _len, _ref;
+ params = {};
+ _ref = query.split('&');
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ segment = _ref[_i];
+ if (matches = segment.match(keyVal)) {
+ normalizeParams(params, decodeQueryComponent(matches[1]), decodeQueryComponent(matches[2]));
+ } else {
+ normalizeParams(params, decodeQueryComponent(segment), null);
+ }
+ }
+ return params;
+ };
+
+ URI.decodeQueryComponent = decodeQueryComponent = function(str) {
+ return decodeURIComponent(str.replace(plus, '%20'));
+ };
+
+ nameParser = /^[\[\]]*([^\[\]]+)\]*(.*)/;
+
+ childKeyMatchers = [/^\[\]\[([^\[\]]+)\]$/, /^\[\](.+)$/];
+
+ plus = /\+/g;
+
+ r20 = /%20/g;
+
+ keyVal = /^([^=]*)=(.*)/;
+
+ normalizeParams = function(params, name, v) {
+ var after, childKey, k, last, matches, _ref, _ref1, _ref2;
+ if (matches = name.match(nameParser)) {
+ k = matches[1];
+ after = matches[2];
+ } else {
+ return;
+ }
+ if (after === '') {
+ params[k] = v;
+ } else if (after === '[]') {
+ if ((_ref = params[k]) == null) {
+ params[k] = [];
+ }
+ if (Batman.typeOf(params[k]) !== 'Array') {
+ throw new Error("expected Array (got " + (Batman.typeOf(params[k])) + ") for param \"" + k + "\"");
+ }
+ params[k].push(v);
+ } else if (matches = after.match(childKeyMatchers[0]) || after.match(childKeyMatchers[1])) {
+ childKey = matches[1];
+ if ((_ref1 = params[k]) == null) {
+ params[k] = [];
+ }
+ if (Batman.typeOf(params[k]) !== 'Array') {
+ throw new Error("expected Array (got " + (Batman.typeOf(params[k])) + ") for param \"" + k + "\"");
+ }
+ last = params[k][params[k].length - 1];
+ if (Batman.typeOf(last) === 'Object' && !(childKey in last)) {
+ normalizeParams(last, childKey, v);
+ } else {
+ params[k].push(normalizeParams({}, childKey, v));
+ }
+ } else {
+ if ((_ref2 = params[k]) == null) {
+ params[k] = {};
+ }
+ if (Batman.typeOf(params[k]) !== 'Object') {
+ throw new Error("expected Object (got " + (Batman.typeOf(params[k])) + ") for param \"" + k + "\"");
+ }
+ params[k] = normalizeParams(params[k], after, v);
+ }
+ return params;
+ };
+
+ /*
+ # query building
+ */
+
+
+ URI.queryFromParams = queryFromParams = function(value, prefix) {
+ var arrayResults, k, v, valueType;
+ if (value == null) {
+ return prefix;
+ }
+ valueType = Batman.typeOf(value);
+ if (!((prefix != null) || valueType === 'Object')) {
+ throw new Error("value must be an Object");
+ }
+ switch (valueType) {
+ case 'Array':
+ return ((function() {
+ var _i, _len;
+ arrayResults = [];
+ if (value.length === 0) {
+ arrayResults.push(queryFromParams(null, "" + prefix + "[]"));
+ } else {
+ for (_i = 0, _len = value.length; _i < _len; _i++) {
+ v = value[_i];
+ arrayResults.push(queryFromParams(v, "" + prefix + "[]"));
+ }
+ }
+ return arrayResults;
+ })()).join("&");
+ case 'Object':
+ return ((function() {
+ var _results;
+ _results = [];
+ for (k in value) {
+ v = value[k];
+ _results.push(queryFromParams(v, prefix ? "" + prefix + "[" + (encodeQueryComponent(k)) + "]" : encodeQueryComponent(k)));
+ }
+ return _results;
+ })()).join("&");
+ default:
+ if (prefix != null) {
+ return "" + prefix + "=" + (encodeQueryComponent(value));
+ } else {
+ return encodeQueryComponent(value);
+ }
+ }
+ };
+
+ URI.encodeComponent = encodeComponent = function(str) {
+ if (str != null) {
+ return encodeURIComponent(str);
+ } else {
+ return '';
+ }
+ };
+
+ URI.encodeQueryComponent = encodeQueryComponent = function(str) {
+ return encodeComponent(str).replace(r20, '+');
+ };
+
+ return URI;
+
+ })();
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
+ Batman.Request = (function(_super) {
+ var dataHasFileUploads;
+
+ __extends(Request, _super);
+
+ Request.objectToFormData = function(data) {
+ var formData, key, pairForList, val, _i, _len, _ref, _ref1;
+ pairForList = function(key, object, first) {
+ var k, list, v;
+ if (first == null) {
+ first = false;
+ }
+ if (object instanceof Batman.container.File) {
+ return [[key, object]];
+ }
+ return list = (function() {
+ switch (Batman.typeOf(object)) {
+ case 'Object':
+ list = (function() {
+ var _results;
+ _results = [];
+ for (k in object) {
+ v = object[k];
+ _results.push(pairForList((first ? k : "" + key + "[" + k + "]"), v));
+ }
+ return _results;
+ })();
+ return list.reduce(function(acc, list) {
+ return acc.concat(list);
+ }, []);
+ case 'Array':
+ return object.reduce(function(acc, element) {
+ return acc.concat(pairForList("" + key + "[]", element));
+ }, []);
+ default:
+ return [[key, object != null ? object : ""]];
+ }
+ })();
+ };
+ formData = new Batman.container.FormData();
+ _ref = pairForList("", data, true);
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ _ref1 = _ref[_i], key = _ref1[0], val = _ref1[1];
+ formData.append(key, val);
+ }
+ return formData;
+ };
+
+ Request.dataHasFileUploads = dataHasFileUploads = function(data) {
+ var k, type, v, _i, _len;
+ if ((typeof File !== "undefined" && File !== null) && data instanceof File) {
+ return true;
+ }
+ type = Batman.typeOf(data);
+ switch (type) {
+ case 'Object':
+ for (k in data) {
+ v = data[k];
+ if (dataHasFileUploads(v)) {
+ return true;
+ }
+ }
+ break;
+ case 'Array':
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ v = data[_i];
+ if (dataHasFileUploads(v)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ Request.wrapAccessor('method', function(core) {
+ return {
+ set: function(k, val) {
+ return core.set.call(this, k, val != null ? typeof val.toUpperCase === "function" ? val.toUpperCase() : void 0 : void 0);
+ }
+ };
+ });
+
+ Request.prototype.method = 'GET';
+
+ Request.prototype.hasFileUploads = function() {
+ return dataHasFileUploads(this.data);
+ };
+
+ Request.prototype.contentType = 'application/x-www-form-urlencoded';
+
+ Request.prototype.autosend = true;
+
+ function Request(options) {
+ var handler, handlers, k, _ref;
+ handlers = {};
+ for (k in options) {
+ handler = options[k];
+ if (!(k === 'success' || k === 'error' || k === 'loading' || k === 'loaded')) {
+ continue;
+ }
+ handlers[k] = handler;
+ delete options[k];
+ }
+ Request.__super__.constructor.call(this, options);
+ for (k in handlers) {
+ handler = handlers[k];
+ this.on(k, handler);
+ }
+ if (((_ref = this.get('url')) != null ? _ref.length : void 0) > 0) {
+ if (this.autosend) {
+ this.send();
+ }
+ } else {
+ this.observe('url', function(url) {
+ if (url != null) {
+ return this.send();
+ }
+ });
+ }
+ }
+
+ Request.prototype.send = function() {
+ return Batman.developer.error("Please source a dependency file for a request implementation");
+ };
+
+ return Request;
+
+ })(Batman.Object);
+
+}).call(this);
+
+(function() {
var __hasProp = {}.hasOwnProperty,
__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; },
__slice = [].slice;
@@ -8284,6 +8907,27 @@
return _results;
};
+ SetSort.prototype.find = function(block) {
+ var item, _i, _len, _ref;
+ this.base.registerAsMutableSource();
+ _ref = this.get('_storage');
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ item = _ref[_i];
+ if (block(item)) {
+ return item;
+ }
+ }
+ };
+
+ SetSort.prototype.merge = function(other) {
+ this.base.registerAsMutableSource();
+ return (function(func, args, ctor) {
+ ctor.prototype = func.prototype;
+ var child = new ctor, result = func.apply(child, args), t = typeof result;
+ return t == "object" || t == "function" ? result || child : child;
+ })(Batman.Set, this._storage, function(){}).merge(other).sortedBy(this.key, this.order);
+ };
+
SetSort.prototype.compare = function(a, b) {
if (a === b) {
return 0;
@@ -8388,7 +9032,7 @@
if (this.foreignKeyValue == null) {
return callback(void 0, this);
}
- return this.association.getRelatedModel().load(this._getLoadOptions(), function(err, records) {
+ return this.association.getRelatedModel().loadWithOptions(this._getLoadOptions(), function(err, records) {
if (!err) {
_this.markAsLoaded();
}
@@ -8398,8 +9042,14 @@
AssociationSet.prototype._getLoadOptions = function() {
var loadOptions;
- loadOptions = {};
- loadOptions[this.association.foreignKey] = this.foreignKeyValue;
+ loadOptions = {
+ data: {}
+ };
+ loadOptions.data[this.association.foreignKey] = this.foreignKeyValue;
+ if (this.association.options.url) {
+ loadOptions.collectionUrl = this.association.options.url;
+ loadOptions.urlContext = this.association.parentSetIndex().get(this.foreignKeyValue);
+ }
return loadOptions;
};
@@ -8433,9 +9083,15 @@
PolymorphicAssociationSet.prototype._getLoadOptions = function() {
var loadOptions;
- loadOptions = {};
- loadOptions[this.association.foreignKey] = this.foreignKeyValue;
- loadOptions[this.association.foreignTypeKey] = this.foreignTypeKeyValue;
+ loadOptions = {
+ data: {}
+ };
+ loadOptions.data[this.association.foreignKey] = this.foreignKeyValue;
+ loadOptions.data[this.association.foreignTypeKey] = this.foreignTypeKeyValue;
+ if (this.association.options.url) {
+ loadOptions.collectionUrl = this.association.options.url;
+ loadOptions.urlContext = this.association.parentSetIndex().get(this.foreignKeyValue);
+ }
return loadOptions;
};
@@ -8612,14 +9268,27 @@
}
AssociationSetIndex.prototype._resultSetForKey = function(key) {
+ return this.association.setForKey(key);
+ };
+
+ AssociationSetIndex.prototype.forEach = function(iterator, ctx) {
var _this = this;
- return this._storage.getOrSet(key, function() {
- return new _this.association.proxyClass(key, _this.association);
+ return this.association.proxies.forEach(function(record, set) {
+ var key;
+ key = _this.association.indexValueForRecord(record);
+ if (set.get('length') > 0) {
+ return iterator.call(ctx, key, set, _this);
+ }
});
};
- AssociationSetIndex.prototype._setResultSet = function(key, set) {
- return this._storage.set(key, set);
+ AssociationSetIndex.prototype.toArray = function() {
+ var results;
+ results = [];
+ this.forEach(function(key) {
+ return results.push(key);
+ });
+ return results;
};
return AssociationSetIndex;
@@ -8709,349 +9378,6 @@
}).call(this);
(function() {
-
- Batman.URI = (function() {
- /*
- # URI parsing
- */
-
- var attributes, childKeyMatchers, decodeQueryComponent, encodeComponent, encodeQueryComponent, keyVal, nameParser, normalizeParams, plus, queryFromParams, r20, strictParser;
-
- strictParser = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/;
-
- attributes = ["source", "protocol", "authority", "userInfo", "user", "password", "hostname", "port", "relative", "path", "directory", "file", "query", "hash"];
-
- function URI(str) {
- var i, matches;
- matches = strictParser.exec(str);
- i = 14;
- while (i--) {
- this[attributes[i]] = matches[i] || '';
- }
- this.queryParams = this.constructor.paramsFromQuery(this.query);
- delete this.authority;
- delete this.userInfo;
- delete this.relative;
- delete this.directory;
- delete this.file;
- delete this.query;
- }
-
- URI.prototype.queryString = function() {
- return this.constructor.queryFromParams(this.queryParams);
- };
-
- URI.prototype.toString = function() {
- return [this.protocol ? "" + this.protocol + ":" : void 0, this.authority() ? "//" : void 0, this.authority(), this.relative()].join("");
- };
-
- URI.prototype.userInfo = function() {
- return [this.user, this.password ? ":" + this.password : void 0].join("");
- };
-
- URI.prototype.authority = function() {
- return [this.userInfo(), this.user || this.password ? "@" : void 0, this.hostname, this.port ? ":" + this.port : void 0].join("");
- };
-
- URI.prototype.relative = function() {
- var query;
- query = this.queryString();
- return [this.path, query ? "?" + query : void 0, this.hash ? "#" + this.hash : void 0].join("");
- };
-
- URI.prototype.directory = function() {
- var splitPath;
- splitPath = this.path.split('/');
- if (splitPath.length > 1) {
- return splitPath.slice(0, splitPath.length - 1).join('/') + "/";
- } else {
- return "";
- }
- };
-
- URI.prototype.file = function() {
- var splitPath;
- splitPath = this.path.split("/");
- return splitPath[splitPath.length - 1];
- };
-
- /*
- # query parsing
- */
-
-
- URI.paramsFromQuery = function(query) {
- var matches, params, segment, _i, _len, _ref;
- params = {};
- _ref = query.split('&');
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- segment = _ref[_i];
- if (matches = segment.match(keyVal)) {
- normalizeParams(params, decodeQueryComponent(matches[1]), decodeQueryComponent(matches[2]));
- } else {
- normalizeParams(params, decodeQueryComponent(segment), null);
- }
- }
- return params;
- };
-
- URI.decodeQueryComponent = decodeQueryComponent = function(str) {
- return decodeURIComponent(str.replace(plus, '%20'));
- };
-
- nameParser = /^[\[\]]*([^\[\]]+)\]*(.*)/;
-
- childKeyMatchers = [/^\[\]\[([^\[\]]+)\]$/, /^\[\](.+)$/];
-
- plus = /\+/g;
-
- r20 = /%20/g;
-
- keyVal = /^([^=]*)=(.*)/;
-
- normalizeParams = function(params, name, v) {
- var after, childKey, k, last, matches, _ref, _ref1, _ref2;
- if (matches = name.match(nameParser)) {
- k = matches[1];
- after = matches[2];
- } else {
- return;
- }
- if (after === '') {
- params[k] = v;
- } else if (after === '[]') {
- if ((_ref = params[k]) == null) {
- params[k] = [];
- }
- if (Batman.typeOf(params[k]) !== 'Array') {
- throw new Error("expected Array (got " + (Batman.typeOf(params[k])) + ") for param \"" + k + "\"");
- }
- params[k].push(v);
- } else if (matches = after.match(childKeyMatchers[0]) || after.match(childKeyMatchers[1])) {
- childKey = matches[1];
- if ((_ref1 = params[k]) == null) {
- params[k] = [];
- }
- if (Batman.typeOf(params[k]) !== 'Array') {
- throw new Error("expected Array (got " + (Batman.typeOf(params[k])) + ") for param \"" + k + "\"");
- }
- last = params[k][params[k].length - 1];
- if (Batman.typeOf(last) === 'Object' && !(childKey in last)) {
- normalizeParams(last, childKey, v);
- } else {
- params[k].push(normalizeParams({}, childKey, v));
- }
- } else {
- if ((_ref2 = params[k]) == null) {
- params[k] = {};
- }
- if (Batman.typeOf(params[k]) !== 'Object') {
- throw new Error("expected Object (got " + (Batman.typeOf(params[k])) + ") for param \"" + k + "\"");
- }
- params[k] = normalizeParams(params[k], after, v);
- }
- return params;
- };
-
- /*
- # query building
- */
-
-
- URI.queryFromParams = queryFromParams = function(value, prefix) {
- var arrayResults, k, v, valueType;
- if (value == null) {
- return prefix;
- }
- valueType = Batman.typeOf(value);
- if (!((prefix != null) || valueType === 'Object')) {
- throw new Error("value must be an Object");
- }
- switch (valueType) {
- case 'Array':
- return ((function() {
- var _i, _len;
- arrayResults = [];
- if (value.length === 0) {
- arrayResults.push(queryFromParams(null, "" + prefix + "[]"));
- } else {
- for (_i = 0, _len = value.length; _i < _len; _i++) {
- v = value[_i];
- arrayResults.push(queryFromParams(v, "" + prefix + "[]"));
- }
- }
- return arrayResults;
- })()).join("&");
- case 'Object':
- return ((function() {
- var _results;
- _results = [];
- for (k in value) {
- v = value[k];
- _results.push(queryFromParams(v, prefix ? "" + prefix + "[" + (encodeQueryComponent(k)) + "]" : encodeQueryComponent(k)));
- }
- return _results;
- })()).join("&");
- default:
- if (prefix != null) {
- return "" + prefix + "=" + (encodeQueryComponent(value));
- } else {
- return encodeQueryComponent(value);
- }
- }
- };
-
- URI.encodeComponent = encodeComponent = function(str) {
- if (str != null) {
- return encodeURIComponent(str);
- } else {
- return '';
- }
- };
-
- URI.encodeQueryComponent = encodeQueryComponent = function(str) {
- return encodeComponent(str).replace(r20, '+');
- };
-
- return URI;
-
- })();
-
-}).call(this);
-
-(function() {
- var __hasProp = {}.hasOwnProperty,
- __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; };
-
- Batman.Request = (function(_super) {
- var dataHasFileUploads;
-
- __extends(Request, _super);
-
- Request.objectToFormData = function(data) {
- var formData, key, pairForList, val, _i, _len, _ref, _ref1;
- pairForList = function(key, object, first) {
- var k, list, v;
- if (first == null) {
- first = false;
- }
- return list = (function() {
- switch (Batman.typeOf(object)) {
- case 'Object':
- list = (function() {
- var _results;
- _results = [];
- for (k in object) {
- v = object[k];
- _results.push(pairForList((first ? k : "" + key + "[" + k + "]"), v));
- }
- return _results;
- })();
- return list.reduce(function(acc, list) {
- return acc.concat(list);
- }, []);
- case 'Array':
- return object.reduce(function(acc, element) {
- return acc.concat(pairForList("" + key + "[]", element));
- }, []);
- default:
- return [[key, object != null ? object : ""]];
- }
- })();
- };
- formData = new Batman.container.FormData();
- _ref = pairForList("", data, true);
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- _ref1 = _ref[_i], key = _ref1[0], val = _ref1[1];
- formData.append(key, val);
- }
- return formData;
- };
-
- Request.dataHasFileUploads = dataHasFileUploads = function(data) {
- var k, type, v, _i, _len;
- if ((typeof File !== "undefined" && File !== null) && data instanceof File) {
- return true;
- }
- type = Batman.typeOf(data);
- switch (type) {
- case 'Object':
- for (k in data) {
- v = data[k];
- if (dataHasFileUploads(v)) {
- return true;
- }
- }
- break;
- case 'Array':
- for (_i = 0, _len = data.length; _i < _len; _i++) {
- v = data[_i];
- if (dataHasFileUploads(v)) {
- return true;
- }
- }
- }
- return false;
- };
-
- Request.wrapAccessor('method', function(core) {
- return {
- set: function(k, val) {
- return core.set.call(this, k, val != null ? typeof val.toUpperCase === "function" ? val.toUpperCase() : void 0 : void 0);
- }
- };
- });
-
- Request.prototype.method = 'GET';
-
- Request.prototype.hasFileUploads = function() {
- return dataHasFileUploads(this.data);
- };
-
- Request.prototype.contentType = 'application/x-www-form-urlencoded';
-
- Request.prototype.autosend = true;
-
- function Request(options) {
- var handler, handlers, k, _ref;
- handlers = {};
- for (k in options) {
- handler = options[k];
- if (!(k === 'success' || k === 'error' || k === 'loading' || k === 'loaded')) {
- continue;
- }
- handlers[k] = handler;
- delete options[k];
- }
- Request.__super__.constructor.call(this, options);
- for (k in handlers) {
- handler = handlers[k];
- this.on(k, handler);
- }
- if (((_ref = this.get('url')) != null ? _ref.length : void 0) > 0) {
- if (this.autosend) {
- this.send();
- }
- } else {
- this.observe('url', function(url) {
- if (url != null) {
- return this.send();
- }
- });
- }
- }
-
- Request.prototype.send = function() {
- return Batman.developer.error("Please source a dependency file for a request implementation");
- };
-
- return Request;
-
- })(Batman.Object);
-
-}).call(this);
-
-(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__slice = [].slice;
@@ -9113,20 +9439,36 @@
};
Navigator.prototype.dispatch = function(params) {
- return this.cachedPath = this.app.get('dispatcher').dispatch(params);
+ this.cachedPath = this.app.get('dispatcher').dispatch(params);
+ if (this._lastRedirect) {
+ this.cachedPath = this._lastRedirect;
+ }
+ return this.cachedPath;
};
Navigator.prototype.push = function(params) {
- var path;
+ var path, pathFromParams, _base;
+ pathFromParams = typeof (_base = this.app.get('dispatcher')).pathFromParams === "function" ? _base.pathFromParams(params) : void 0;
+ if (pathFromParams) {
+ this._lastRedirect = pathFromParams;
+ }
path = this.dispatch(params);
- this.pushState(null, '', path);
+ if (!this._lastRedirect || this._lastRedirect === path) {
+ this.pushState(null, '', path);
+ }
return path;
};
Navigator.prototype.replace = function(params) {
- var path;
+ var path, pathFromParams, _base;
+ pathFromParams = typeof (_base = this.app.get('dispatcher')).pathFromParams === "function" ? _base.pathFromParams(params) : void 0;
+ if (pathFromParams) {
+ this._lastRedirect = pathFromParams;
+ }
path = this.dispatch(params);
- this.replaceState(null, '', path);
+ if (!this._lastRedirect || this._lastRedirect === path) {
+ this.replaceState(null, '', path);
+ }
return path;
};
@@ -9173,19 +9515,23 @@
};
PushStateNavigator.prototype.startWatching = function() {
- return Batman.addEventListener(window, 'popstate', this.handleCurrentLocation);
+ return Batman.DOM.addEventListener(window, 'popstate', this.handleCurrentLocation);
};
PushStateNavigator.prototype.stopWatching = function() {
- return Batman.removeEventListener(window, 'popstate', this.handleCurrentLocation);
+ return Batman.DOM.removeEventListener(window, 'popstate', this.handleCurrentLocation);
};
PushStateNavigator.prototype.pushState = function(stateObject, title, path) {
- return window.history.pushState(stateObject, title, this.linkTo(path));
+ if (path !== this.pathFromLocation(window.location)) {
+ return window.history.pushState(stateObject, title, this.linkTo(path));
+ }
};
PushStateNavigator.prototype.replaceState = function(stateObject, title, path) {
- return window.history.replaceState(stateObject, title, this.linkTo(path));
+ if (path !== this.pathFromLocation(window.location)) {
+ return window.history.replaceState(stateObject, title, this.linkTo(path));
+ }
};
PushStateNavigator.prototype.linkTo = function(url) {
@@ -9231,10 +9577,10 @@
if ((typeof window !== "undefined" && window !== null) && 'onhashchange' in window) {
HashbangNavigator.prototype.startWatching = function() {
- return Batman.addEventListener(window, 'hashchange', this.handleCurrentLocation);
+ return Batman.DOM.addEventListener(window, 'hashchange', this.handleCurrentLocation);
};
HashbangNavigator.prototype.stopWatching = function() {
- return Batman.removeEventListener(window, 'hashchange', this.handleCurrentLocation);
+ return Batman.DOM.removeEventListener(window, 'hashchange', this.handleCurrentLocation);
};
} else {
HashbangNavigator.prototype.startWatching = function() {
@@ -9558,7 +9904,7 @@
options = Batman.extend({}, this.baseOptions, options);
options[cardinality] = true;
routeTemplate = this.constructor.ROUTES[cardinality];
- resourceRoot = options.controller;
+ resourceRoot = Batman.helpers.underscore(options.controller);
for (_j = 0, _len = names.length; _j < _len; _j++) {
name = names[_j];
routeOptions = Batman.extend({
@@ -9744,6 +10090,8 @@
App.layout = void 0;
+ App.shouldAllowEvent = {};
+
_ref = Batman.RouteMapBuilder.BUILDER_FUNCTIONS;
_fn = function(name) {
return App[name] = function() {
@@ -9948,21 +10296,62 @@
__extends(PluralAssociation, _super);
- function PluralAssociation() {
- return PluralAssociation.__super__.constructor.apply(this, arguments);
- }
-
PluralAssociation.prototype.proxyClass = Batman.AssociationSet;
PluralAssociation.prototype.isSingular = false;
- PluralAssociation.prototype.setForRecord = Batman.Property.wrapTrackingPrevention(function(record) {
- var id;
- if (id = record.get(this.primaryKey)) {
- return this.setIndex().get(id);
+ function PluralAssociation() {
+ PluralAssociation.__super__.constructor.apply(this, arguments);
+ this._resetSetHashes();
+ }
+
+ PluralAssociation.prototype.setForRecord = function(record) {
+ var childModelSetIndex, indexValue,
+ _this = this;
+ indexValue = this.indexValueForRecord(record);
+ childModelSetIndex = this.setIndex();
+ Batman.Property.withoutTracking(function() {
+ return _this._setsByRecord.getOrSet(record, function() {
+ var existingValueSet, newSet;
+ if (indexValue != null) {
+ existingValueSet = _this._setsByValue.get(indexValue);
+ if (existingValueSet != null) {
+ return existingValueSet;
+ }
+ }
+ newSet = new _this.proxyClass(indexValue, _this);
+ if (indexValue != null) {
+ _this._setsByValue.set(indexValue, newSet);
+ }
+ return newSet;
+ });
+ });
+ if (indexValue != null) {
+ return childModelSetIndex.get(indexValue);
} else {
- return new this.proxyClass(void 0, this);
+ return this._setsByRecord.get(record);
+ }
+ };
+
+ PluralAssociation.prototype.setForKey = Batman.Property.wrapTrackingPrevention(function(indexValue) {
+ var foundSet,
+ _this = this;
+ foundSet = void 0;
+ this._setsByRecord.forEach(function(record, set) {
+ if (foundSet != null) {
+ return;
+ }
+ if (_this.indexValueForRecord(record) === indexValue) {
+ return foundSet = set;
+ }
+ });
+ if (foundSet != null) {
+ foundSet.foreignKeyValue = indexValue;
+ return foundSet;
}
+ return this._setsByValue.getOrSet(indexValue, function() {
+ return new _this.proxyClass(indexValue, _this);
+ });
});
PluralAssociation.prototype.getAccessor = function(self, model, label) {
@@ -9989,11 +10378,30 @@
}
};
+ PluralAssociation.prototype.parentSetIndex = function() {
+ this.parentIndex || (this.parentIndex = this.model.get('loaded').indexedByUnique(this.primaryKey));
+ return this.parentIndex;
+ };
+
PluralAssociation.prototype.setIndex = function() {
this.index || (this.index = new Batman.AssociationSetIndex(this, this[this.indexRelatedModelOn]));
return this.index;
};
+ PluralAssociation.prototype.indexValueForRecord = function(record) {
+ return record.get(this.primaryKey);
+ };
+
+ PluralAssociation.prototype.reset = function() {
+ PluralAssociation.__super__.reset.apply(this, arguments);
+ return this._resetSetHashes();
+ };
+
+ PluralAssociation.prototype._resetSetHashes = function() {
+ this._setsByRecord = new Batman.SimpleHash;
+ return this._setsByValue = new Batman.SimpleHash;
+ };
+
return PluralAssociation;
})(Batman.Association);
@@ -10202,18 +10610,33 @@
SingularAssociation.prototype.isSingular = true;
SingularAssociation.prototype.getAccessor = function(self, model, label) {
- var proxy, recordInAttributes;
+ var parent, proxy, record, recordInAttributes, _ref;
if (recordInAttributes = self.getFromAttributes(this)) {
return recordInAttributes;
}
if (self.getRelatedModel()) {
proxy = this.associationProxy(self);
- Batman.Property.withoutTracking(function() {
- if (!proxy.get('loaded') && self.options.autoload) {
- return proxy.load();
+ record = false;
+ parent = this;
+ if ((_ref = proxy._loadSetter) == null) {
+ proxy._loadSetter = proxy.once('loaded', function(child) {
+ return parent._withoutDirtyTracking(function() {
+ return this.set(self.label, child);
+ });
+ });
+ }
+ if (!Batman.Property.withoutTracking(function() {
+ return proxy.get('loaded');
+ })) {
+ if (self.options.autoload) {
+ Batman.Property.withoutTracking(function() {
+ return proxy.load();
+ });
+ } else {
+ record = proxy.loadFromLocal();
}
- });
- return proxy;
+ }
+ return record || proxy;
}
};
@@ -10275,15 +10698,14 @@
association = this;
return function(data, _, __, ___, parentRecord) {
var record, relatedModel;
+ if (!data) {
+ return;
+ }
relatedModel = association.getRelatedModel();
- record = new relatedModel();
- record._withoutDirtyTracking(function() {
- return this.fromJSON(data);
- });
+ record = relatedModel.createFromJSON(data);
if (association.options.inverseOf) {
record.set(association.options.inverseOf, parentRecord);
}
- record = relatedModel._mapIdentity(record);
return record;
};
};
@@ -10343,11 +10765,7 @@
return function(data, _, __, ___, childRecord) {
var inverse, record, relatedModel;
relatedModel = association.getRelatedModel();
- record = new relatedModel();
- record._withoutDirtyTracking(function() {
- return this.fromJSON(data);
- });
- record = relatedModel._mapIdentity(record);
+ record = relatedModel.createFromJSON(data);
if (association.options.inverseOf) {
if (inverse = association.inverse()) {
if (inverse instanceof Batman.HasManyAssociation) {
@@ -10496,11 +10914,7 @@
var foreignTypeValue, inverse, record, relatedModel;
foreignTypeValue = response[association.foreignTypeKey] || childRecord.get(association.foreignTypeKey);
relatedModel = association.getRelatedModelForType(foreignTypeValue);
- record = new relatedModel();
- record._withoutDirtyTracking(function() {
- return this.fromJSON(data);
- });
- record = relatedModel._mapIdentity(record);
+ record = relatedModel.createFromJSON(data);
if (association.options.inverseOf) {
if (inverse = association.inverseForType(foreignTypeValue)) {
if (inverse instanceof Batman.PolymorphicHasManyAssociation) {
@@ -10531,29 +10945,23 @@
__extends(Validator, _super);
- function Validator() {
- var mixins, options;
- options = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
- this.options = options;
- Validator.__super__.constructor.apply(this, mixins);
- }
-
- Validator.prototype.validate = function(record) {
- return Batman.developer.error("You must override validate in Batman.Validator subclasses.");
- };
-
- Validator.prototype.format = function(key, messageKey, interpolations) {
- return Batman.t("errors.messages." + messageKey, interpolations);
+ Validator.triggers = function() {
+ var triggers;
+ triggers = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ if (this._triggers != null) {
+ return this._triggers.concat(triggers);
+ } else {
+ return this._triggers = triggers;
+ }
};
Validator.options = function() {
var options;
options = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- Batman.initializeObject(this);
- if (this._batman.options) {
- return this._batman.options.concat(options);
+ if (this._options != null) {
+ return this._options.concat(options);
} else {
- return this._batman.options = options;
+ return this._options = options;
}
};
@@ -10563,7 +10971,10 @@
shouldReturn = false;
for (key in options) {
value = options[key];
- if (~((_ref = this._batman) != null ? (_ref1 = _ref.options) != null ? _ref1.indexOf(key) : void 0 : void 0)) {
+ if (~((_ref = this._options) != null ? _ref.indexOf(key) : void 0)) {
+ results[key] = value;
+ }
+ if (~((_ref1 = this._triggers) != null ? _ref1.indexOf(key) : void 0)) {
results[key] = value;
shouldReturn = true;
}
@@ -10573,6 +10984,27 @@
}
};
+ function Validator() {
+ var mixins, options;
+ options = arguments[0], mixins = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ this.options = options;
+ Validator.__super__.constructor.apply(this, mixins);
+ }
+
+ Validator.prototype.validate = function(record) {
+ return Batman.developer.error("You must override validate in Batman.Validator subclasses.");
+ };
+
+ Validator.prototype.format = function(key, messageKey, interpolations) {
+ return Batman.t("errors.messages." + messageKey, interpolations);
+ };
+
+ Validator.prototype.handleBlank = function(value) {
+ if (this.options.allowBlank && !Batman.PresenceValidator.prototype.isPresent(value)) {
+ return true;
+ }
+ };
+
return Validator;
})(Batman.Object);
@@ -10592,7 +11024,13 @@
wrong_length: "must be %{count} characters",
blank: "can't be blank",
not_numeric: "must be a number",
- not_matching: "is not valid"
+ greater_than: "must be greater than %{count}",
+ greater_than_or_equal_to: "must be greater than or equal to %{count}",
+ equal_to: "must be equal to %{count}",
+ less_than: "must be less than %{count}",
+ less_than_or_equal_to: "must be less than or equal to %{count}",
+ not_matching: "is not valid",
+ invalid_association: "is not valid"
}
}
});
@@ -10607,7 +11045,9 @@
__extends(RegExpValidator, _super);
- RegExpValidator.options('regexp', 'pattern');
+ RegExpValidator.triggers('regexp', 'pattern');
+
+ RegExpValidator.options('allowBlank');
function RegExpValidator(options) {
var _ref;
@@ -10618,10 +11058,11 @@
RegExpValidator.prototype.validateEach = function(errors, record, key, callback) {
var value;
value = record.get(key);
- if ((value != null) && value !== '') {
- if (!this.regexp.test(value)) {
- errors.add(key, this.format(key, 'not_matching'));
- }
+ if (this.handleBlank(value)) {
+ return callback();
+ }
+ if (!(value != null) || value === '' || !this.regexp.test(value)) {
+ errors.add(key, this.format(key, 'not_matching'));
}
return callback();
};
@@ -10646,17 +11087,21 @@
return PresenceValidator.__super__.constructor.apply(this, arguments);
}
- PresenceValidator.options('presence');
+ PresenceValidator.triggers('presence');
PresenceValidator.prototype.validateEach = function(errors, record, key, callback) {
var value;
value = record.get(key);
- if (this.options.presence && (!(value != null) || value === '')) {
+ if (!this.isPresent(value)) {
errors.add(key, this.format(key, 'blank'));
}
return callback();
};
+ PresenceValidator.prototype.isPresent = function(value) {
+ return (value != null) && value !== '';
+ };
+
return PresenceValidator;
})(Batman.Validator);
@@ -10677,17 +11122,57 @@
return NumericValidator.__super__.constructor.apply(this, arguments);
}
- NumericValidator.options('numeric');
+ NumericValidator.triggers('numeric', 'greaterThan', 'greaterThanOrEqualTo', 'equalTo', 'lessThan', 'lessThanOrEqualTo');
+
+ NumericValidator.options('allowBlank');
NumericValidator.prototype.validateEach = function(errors, record, key, callback) {
- var value;
+ var options, value;
+ options = this.options;
value = record.get(key);
- if (this.options.numeric && isNaN(parseFloat(value))) {
+ if (this.handleBlank(value)) {
+ return callback();
+ }
+ if (!(value != null) || !(this.isNumeric(value) || this.canCoerceToNumeric(value))) {
errors.add(key, this.format(key, 'not_numeric'));
+ } else {
+ if (options.greaterThan && value <= options.greaterThan) {
+ errors.add(key, this.format(key, 'greater_than', {
+ count: options.greaterThan
+ }));
+ }
+ if (options.greaterThanOrEqualTo && value < options.greaterThanOrEqualTo) {
+ errors.add(key, this.format(key, 'greater_than_or_equal_to', {
+ count: options.greaterThanOrEqualTo
+ }));
+ }
+ if (options.equalTo && value !== options.equalTo) {
+ errors.add(key, this.format(key, 'equal_to', {
+ count: options.equalTo
+ }));
+ }
+ if (options.lessThan && value >= options.lessThan) {
+ errors.add(key, this.format(key, 'less_than', {
+ count: options.lessThan
+ }));
+ }
+ if (options.lessThanOrEqualTo && value > options.lessThanOrEqualTo) {
+ errors.add(key, this.format(key, 'less_than_or_equal_to', {
+ count: options.lessThanOrEqualTo
+ }));
+ }
}
return callback();
};
+ NumericValidator.prototype.isNumeric = function(value) {
+ return !isNaN(parseFloat(value)) && isFinite(value);
+ };
+
+ NumericValidator.prototype.canCoerceToNumeric = function(value) {
+ return (value - 0) == value && value.length > 0;
+ };
+
return NumericValidator;
})(Batman.Validator);
@@ -10704,7 +11189,9 @@
__extends(LengthValidator, _super);
- LengthValidator.options('minLength', 'maxLength', 'length', 'lengthWithin', 'lengthIn');
+ LengthValidator.triggers('minLength', 'maxLength', 'length', 'lengthWithin', 'lengthIn');
+
+ LengthValidator.options('allowBlank');
function LengthValidator(options) {
var range;
@@ -10718,9 +11205,15 @@
}
LengthValidator.prototype.validateEach = function(errors, record, key, callback) {
- var options, value, _ref;
+ var options, value;
options = this.options;
- value = (_ref = record.get(key)) != null ? _ref : [];
+ value = record.get(key);
+ if (value !== '' && this.handleBlank(value)) {
+ return callback();
+ }
+ if (value == null) {
+ value = [];
+ }
if (options.minLength && value.length < options.minLength) {
errors.add(key, this.format(key, 'too_short', {
count: options.minLength
@@ -10751,6 +11244,60 @@
var __hasProp = {}.hasOwnProperty,
__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; };
+ Batman.AssociatedValidator = (function(_super) {
+
+ __extends(AssociatedValidator, _super);
+
+ function AssociatedValidator() {
+ return AssociatedValidator.__super__.constructor.apply(this, arguments);
+ }
+
+ AssociatedValidator.triggers('associated');
+
+ AssociatedValidator.prototype.validateEach = function(errors, record, key, callback) {
+ var childFinished, count, value,
+ _this = this;
+ value = record.get(key);
+ if (value != null) {
+ if (value instanceof Batman.AssociationProxy) {
+ value = typeof value.get === "function" ? value.get('target') : void 0;
+ }
+ count = 1;
+ childFinished = function(err, childErrors) {
+ if (childErrors.length > 0) {
+ errors.add(key, _this.format(key, 'invalid_association'));
+ }
+ if (--count === 0) {
+ return callback();
+ }
+ };
+ if ((value != null ? value.forEach : void 0) != null) {
+ value.forEach(function(record) {
+ count += 1;
+ return record.validate(childFinished);
+ });
+ } else if ((value != null ? value.validate : void 0) != null) {
+ count += 1;
+ value.validate(childFinished);
+ }
+ return childFinished(null, []);
+ } else {
+ return callback();
+ }
+ };
+
+ return AssociatedValidator;
+
+ })(Batman.Validator);
+
+ Batman.Validators.push(Batman.AssociatedValidator);
+
+}).call(this);
+
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __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; };
+
Batman.ControllerActionFrame = (function(_super) {
__extends(ControllerActionFrame, _super);
@@ -10842,8 +11389,10 @@
return parentNode.removeChild(this.placeholderNode);
}
} else {
- parentNode.insertBefore(this.placeholderNode, this.node);
- return Batman.DOM.removeNode(this.node);
+ if (this.node.parentNode != null) {
+ parentNode.insertBefore(this.placeholderNode, this.node);
+ return Batman.DOM.removeNode(this.node);
+ }
}
};
@@ -11011,7 +11560,7 @@
buntUndefined = function(f) {
return function(value) {
- if (typeof value === 'undefined') {
+ if (value == null) {
return void 0;
} else {
return f.apply(this, arguments);
@@ -11045,8 +11594,11 @@
return lhs || rhs;
},
not: function(value, binding) {
- return !!!value;
+ return !value;
},
+ trim: buntUndefined(function(value, binding) {
+ return value.trim();
+ }),
matches: buntUndefined(function(value, searchFor) {
return value.indexOf(searchFor) !== -1;
}),
@@ -11071,10 +11623,10 @@
}
},
prepend: function(value, string, binding) {
- return string + value;
+ return (string != null ? string : '') + (value != null ? value : '');
},
append: function(value, string, binding) {
- return value + string;
+ return (value != null ? value : '') + (string != null ? string : '');
},
replace: buntUndefined(function(value, searchFor, replaceWith, flags, binding) {
if (!binding) {
@@ -11102,7 +11654,7 @@
count = void 0;
}
}
- if (count) {
+ if (count != null) {
return Batman.helpers.pluralize(count, string, void 0, includeCount);
} else {
return Batman.helpers.pluralize(string);
@@ -11329,10 +11881,6 @@
__extends(ViewStore, _super);
- ViewStore.prefix = 'views';
-
- ViewStore.fetchFromRemote = true;
-
function ViewStore() {
ViewStore.__super__.constructor.apply(this, arguments);
this._viewContents = {};
@@ -11344,7 +11892,7 @@
ViewStore.prototype.fetchView = function(path) {
var _this = this;
return new Batman.Request({
- url: Batman.Navigator.normalizePath(this.constructor.prefix, "" + path + ".html"),
+ url: Batman.Navigator.normalizePath(Batman.config.viewPrefix, "" + path + ".html"),
type: 'html',
success: function(response) {
return _this.set(path, response);
@@ -11371,7 +11919,7 @@
if (contents = this._sourceFromDOM(path)) {
return contents;
}
- if (this.constructor.fetchFromRemote) {
+ if (Batman.config.fetchRemoteViews) {
this.fetchView(path);
} else {
throw new Error("Couldn't find view source for \'" + path + "\'!");
@@ -11446,28 +11994,21 @@
View.store = new Batman.ViewStore();
View.option = function() {
- var keys,
- _this = this;
+ var keys;
keys = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- keys.forEach(function(key) {
- return _this.accessor(_this.prototype._argumentBindingKey(key), function(bindingKey) {
- var context, keyPath, node, _ref;
- if (!((node = this.get('node')) && (context = this.get('context')))) {
- return;
- }
- keyPath = node.getAttribute(("data-view-" + key).toLowerCase());
- if (keyPath == null) {
- return;
- }
- if ((_ref = this[bindingKey]) != null) {
- _ref.die();
- }
- return this[bindingKey] = new Batman.DOM.ViewArgumentBinding(node, keyPath, context);
- });
- });
- return this.accessor.apply(this, __slice.call(keys).concat([function(key) {
- var _ref;
- return (_ref = this.get(this._argumentBindingKey(key))) != null ? _ref.get('filteredValue') : void 0;
+ return this.accessor.apply(this, __slice.call(keys).concat([{
+ get: function(key) {
+ var _ref;
+ return (_ref = this.get("argumentBindings." + key)) != null ? _ref.get('filteredValue') : void 0;
+ },
+ set: function(key, value) {
+ var _ref;
+ return (_ref = this.get("argumentBindings." + key)) != null ? _ref.set('filteredValue', value) : void 0;
+ },
+ unset: function(key) {
+ var _ref;
+ return (_ref = this.get("argumentBindings." + key)) != null ? _ref.unset('filteredValue') : void 0;
+ }
}]));
};
@@ -11481,6 +12022,25 @@
View.prototype.event('ready').oneShot = true;
+ View.accessor('argumentBindings', function() {
+ var _this = this;
+ return new Batman.TerminalAccessible(function(key) {
+ var bindingKey, context, keyPath, node, _ref;
+ if (!((node = _this.get('node')) && (context = _this.get('context')))) {
+ return;
+ }
+ keyPath = node.getAttribute(("data-view-" + key).toLowerCase());
+ if (keyPath == null) {
+ return;
+ }
+ bindingKey = "_argumentBinding" + key;
+ if ((_ref = _this[bindingKey]) != null) {
+ _ref.die();
+ }
+ return _this[bindingKey] = new Batman.DOM.ViewArgumentBinding(node, keyPath, context);
+ });
+ });
+
View.accessor('html', {
get: function() {
var source;
@@ -11508,7 +12068,7 @@
}
this.node = document.createElement('div');
this._setNodeOwner(this.node);
- Batman.setInnerHTML(this.node, html);
+ Batman.DOM.setInnerHTML(this.node, html);
}
return this.node;
},
@@ -11519,7 +12079,7 @@
this._setNodeOwner(node);
updateHTML = function(html) {
if (html != null) {
- Batman.setInnerHTML(_this.node, html);
+ Batman.DOM.setInnerHTML(_this.node, html);
return _this.forget('html', updateHTML);
}
};
@@ -11630,10 +12190,6 @@
});
};
- View.prototype._argumentBindingKey = function(key) {
- return "_" + key + "ArgumentBinding";
- };
-
View.prototype._setNodeOwner = function(node) {
return Batman._data(node, 'view', this);
};
@@ -11761,7 +12317,7 @@
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
- _results.push(Batman.removeOrDestroyNode(child));
+ _results.push(Batman.DOM.removeOrDestroyNode(child));
}
return _results;
});
@@ -11782,7 +12338,7 @@
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (!~this.currentVersionNodes.indexOf(child)) {
- _results.push(Batman.removeOrDestroyNode(child));
+ _results.push(Batman.DOM.removeOrDestroyNode(child));
}
}
return _results;
@@ -11790,7 +12346,7 @@
Yield.prototype.append = Yield.queued(function(node) {
this.currentVersionNodes.push(node);
- return Batman.appendChild(this.containerNode, node, true);
+ return Batman.DOM.appendChild(this.containerNode, node, true);
});
Yield.prototype.replace = Yield.queued(function(node) {
diff --git a/javascripts/dashing.coffee b/javascripts/dashing.coffee
index 6d1b5cc..d6d7e22 100644
--- a/javascripts/dashing.coffee
+++ b/javascripts/dashing.coffee
@@ -14,7 +14,7 @@ Batman.Filters.dashize = (str) ->
return str.replace(dashes_rx1, '$1_$2').replace(dashes_rx2, '$1_$2').replace('_', '-').toLowerCase()
Batman.Filters.shortenedNumber = (num) ->
- return if isNaN(num)
+ return num if isNaN(num)
if num >= 1000000000
(num / 1000000000).toFixed(1) + 'B'
else if num >= 1000000
diff --git a/templates/dashboard/%name%.erb.tt b/templates/dashboard/%name%.erb.tt
index d96e164..aa735d6 100644
--- a/templates/dashboard/%name%.erb.tt
+++ b/templates/dashboard/%name%.erb.tt
@@ -1,4 +1,3 @@
-<% content_for(:title) { "My super sweet dashboard" } %>
<div class="gridster">
<ul>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
diff --git a/templates/project/Gemfile b/templates/project/Gemfile
index 7724b81..75cc574 100644
--- a/templates/project/Gemfile
+++ b/templates/project/Gemfile
@@ -1,3 +1,3 @@
source :rubygems
-gem 'dashing', '0.1.0', :git => 'git@github.com:Shopify/dashing.git' \ No newline at end of file
+gem 'dashing' \ No newline at end of file
diff --git a/templates/project/README.md b/templates/project/README.md
index e69de29..260ac2a 100644
--- a/templates/project/README.md
+++ b/templates/project/README.md
@@ -0,0 +1 @@
+Check out http://shopify.github.com/dashing for more information. \ No newline at end of file
diff --git a/templates/project/assets/images/logo.png b/templates/project/assets/images/logo.png
new file mode 100644
index 0000000..aabf199
--- /dev/null
+++ b/templates/project/assets/images/logo.png
Binary files differ
diff --git a/templates/project/assets/javascripts/application.coffee b/templates/project/assets/javascripts/application.coffee
index c84aa05..be030e1 100644
--- a/templates/project/assets/javascripts/application.coffee
+++ b/templates/project/assets/javascripts/application.coffee
@@ -8,17 +8,17 @@
console.log("Yeah! The dashboard has started!")
Dashing.on 'ready', ->
- widget_margins = [5, 5]
- widget_base_dimensions = [240, 330]
- numColumns = 4
+ Dashing.widget_margins ||= [5, 5]
+ Dashing.widget_base_dimensions ||= [300, 360]
+ Dashing.numColumns ||= 4
- contentWidth = (widget_base_dimensions[0] + widget_margins[0] * 2) * numColumns
+ contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns
Batman.setImmediate ->
$('.gridster').width(contentWidth)
$('.gridster ul:first').gridster
- widget_margins: widget_margins
- widget_base_dimensions: widget_base_dimensions
+ widget_margins: Dashing.widget_margins
+ widget_base_dimensions: Dashing.widget_base_dimensions
avoid_overlapped_widgets: !Dashing.customGridsterLayout
draggable:
stop: Dashing.showGridsterInstructions
diff --git a/templates/project/assets/stylesheets/application.scss b/templates/project/assets/stylesheets/application.scss
index 7c5f300..ea2cd86 100644
--- a/templates/project/assets/stylesheets/application.scss
+++ b/templates/project/assets/stylesheets/application.scss
@@ -95,11 +95,11 @@ h1 {
margin-bottom: 12px;
text-align: center;
font-size: 30px;
- font-weight: 300;
+ font-weight: 400;
}
h2 {
text-transform: uppercase;
- font-size: 80px;
+ font-size: 76px;
font-weight: 700;
color: $text-color;
}
@@ -116,6 +116,16 @@ h3 {
margin: 0px auto;
}
+.icon-background {
+ width: 100%!important;
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: 0;
+ opacity: 0.1;
+ font-size: 275px;
+}
+
.list-nostyle {
list-style: none;
}
@@ -146,7 +156,7 @@ h3 {
display: inline-block;
}
- .title, .text-moreinfo {
+ .title, .more-info {
color: $text-warning-color;
}
}
@@ -160,14 +170,25 @@ h3 {
display: inline-block;
}
- .title, .text-moreinfo {
+ .title, .more-info {
color: $text-danger-color;
}
}
-.text-moreinfo {
- margin-top: 12px;
+.more-info {
font-size: 15px;
+ position: absolute;
+ bottom: 32px;
+ left: 0;
+ right: 0;
+}
+
+.updated-at {
+ font-size: 15px;
+ position: absolute;
+ bottom: 12px;
+ left: 0;
+ right: 0;
}
#save-gridster {
@@ -220,10 +241,15 @@ h3 {
display: none;
}
+#container {
+ padding-top: 5px;
+}
+
// ----------------------------------------------------------------------------
// Clearfix
// ----------------------------------------------------------------------------
.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
.clearfix:after { clear: both; }
-.clearfix { zoom: 1; } \ No newline at end of file
+.clearfix { zoom: 1; }
+
diff --git a/templates/project/dashboards/sample.erb b/templates/project/dashboards/sample.erb
index 453a969..ab5cb00 100644
--- a/templates/project/dashboards/sample.erb
+++ b/templates/project/dashboards/sample.erb
@@ -10,15 +10,15 @@
</li>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="2">
- <div data-id="buzzwords" data-view="List" data-unordered="true" data-title="Buzzwords"></div>
+ <div data-id="buzzwords" data-view="List" data-unordered="true" data-title="Buzzwords" data-moreinfo="# of times said around the office"></div>
</li>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
- <div data-id="valuation" data-view="Number" data-title="Current Valuation" data-moreinfo="In billions"></div>
+ <div data-id="valuation" data-view="Number" data-title="Current Valuation" data-moreinfo="In billions" data-prefix="$"></div>
</li>
<li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
- <div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#ff9618;"></div>
+ <div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#ff9618"></div>
</li>
</ul>
<center><div style="font-size: 12px">Try this: curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://localhost:3000/widgets/welcome</div></center>
diff --git a/templates/project/dashboards/sampletv.erb b/templates/project/dashboards/sampletv.erb
new file mode 100644
index 0000000..b52eed7
--- /dev/null
+++ b/templates/project/dashboards/sampletv.erb
@@ -0,0 +1,56 @@
+<script type='text/javascript'>
+$(function() {
+ // These settings override the defaults set in application.coffee. You can do this on a per dashboard basis.
+ Dashing.gridsterLayout('[{"col":2,"row":1},{"col":1,"row":1},{"col":3,"row":1},{"col":2,"row":2},{"col":3,"row":2},{"col":1,"row":2},{"col":5,"row":1},{"col":4,"row":2},{"col":2,"row":3}]')
+ Dashing.widget_base_dimensions = [370, 340]
+ Dashing.numColumns = 5
+});
+</script>
+
+
+<% content_for(:title) { "1080p dashboard" } %>
+
+<div class="gridster">
+ <ul>
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+ <div data-view="Clock"></div>
+ <i class="icon-time icon-background"></i>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+ <div data-view="Image" data-image="/logo.png"></div>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
+ <div data-id="welcome" data-view="Text" data-title="Hello" data-text="This is your shiny new 1080p dashboard." data-moreinfo="Protip: You can drag the widgets around!"></div>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+ <div data-id="synergy" data-view="Meter" data-title="Synergy" data-min="0" data-max="100"></div>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+ <div data-id="synergy" data-view="Meter" data-moreinfo="In sync with my neighbour!" data-title="Synergy" data-min="0" data-max="100"></div>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="2">
+ <div data-id="buzzwords" data-view="List" data-unordered="true" data-title="Buzzwords" data-moreinfo="# of times said around the office"></div>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+ <div data-id="karma" data-view="Number" data-title="Karma" style="background-color:#96bf48;"></div>
+ <i class="icon-heart icon-background"></i>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="2" data-sizey="2">
+ <div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#47bbb3;" data-moreinfo="poop" ></div>
+ </li>
+
+ <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
+ <div data-id="twitter_mentions" data-view="Comments" style="background-color:#ff9618;" data-moreinfo="Tweets tagged with #todayilearned"></div>
+ <i class="icon-twitter icon-background"></i>
+ </li>
+
+ </ul>
+ <center><div style="font-size: 12px">Try this: curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://localhost:3000/widgets/welcome</div></center>
+</div> \ No newline at end of file
diff --git a/templates/project/jobs/buzzwords.rb b/templates/project/jobs/buzzwords.rb
index 109dc60..2668d5a 100644
--- a/templates/project/jobs/buzzwords.rb
+++ b/templates/project/jobs/buzzwords.rb
@@ -3,7 +3,7 @@ buzzword_counts = Hash.new({ value: 0 })
SCHEDULER.every '2s' do
random_buzzword = buzzwords.sample
- buzzword_counts[random_buzzword] = { label: random_buzzword, value: buzzword_counts[random_buzzword][:value] + 1 }
+ buzzword_counts[random_buzzword] = { label: random_buzzword, value: (buzzword_counts[random_buzzword][:value] + 1) % 30 }
send_event('buzzwords', { items: buzzword_counts.values })
end \ No newline at end of file
diff --git a/templates/project/jobs/convergence.rb b/templates/project/jobs/convergence.rb
index f66b6a5..1da2a5b 100644
--- a/templates/project/jobs/convergence.rb
+++ b/templates/project/jobs/convergence.rb
@@ -1,3 +1,4 @@
+# Populate the graph with some random points
points = []
(1..10).each do |i|
points << { x: i, y: rand(50) }
diff --git a/templates/project/jobs/sample.rb b/templates/project/jobs/sample.rb
index 2314dc2..1fe008d 100644
--- a/templates/project/jobs/sample.rb
+++ b/templates/project/jobs/sample.rb
@@ -1,9 +1,13 @@
current_valuation = 0
+current_karma = 0
SCHEDULER.every '2s' do
last_valuation = current_valuation
+ last_karma = current_karma
current_valuation = rand(100)
+ current_karma = rand(200000)
send_event('valuation', { current: current_valuation, last: last_valuation })
+ send_event('karma', { current: current_karma, last: last_karma })
send_event('synergy', { value: rand(100) })
end \ No newline at end of file
diff --git a/templates/project/jobs/twitter.rb b/templates/project/jobs/twitter.rb
new file mode 100644
index 0000000..9359a89
--- /dev/null
+++ b/templates/project/jobs/twitter.rb
@@ -0,0 +1,17 @@
+require 'net/http'
+require 'json'
+
+search_term = URI::encode('#todayilearned')
+
+SCHEDULER.every '10m', :first_in => 0 do |job|
+ http = Net::HTTP.new('search.twitter.com')
+ response = http.request(Net::HTTP::Get.new("/search.json?q=#{search_term}"))
+ tweets = JSON.parse(response.body)["results"]
+ if tweets
+ tweets.map! do |tweet|
+ { name: tweet['from_user'], body: tweet['text'], avatar: tweet['profile_image_url_https'] }
+ end
+
+ send_event('twitter_mentions', comments: tweets)
+ end
+end \ No newline at end of file
diff --git a/templates/project/public/.empty_directory b/templates/project/public/.empty_directory
deleted file mode 100644
index 3da7f49..0000000
--- a/templates/project/public/.empty_directory
+++ /dev/null
@@ -1 +0,0 @@
-.empty_directory \ No newline at end of file
diff --git a/templates/project/widgets/clock/clock.coffee b/templates/project/widgets/clock/clock.coffee
new file mode 100644
index 0000000..cd6b116
--- /dev/null
+++ b/templates/project/widgets/clock/clock.coffee
@@ -0,0 +1,18 @@
+class Dashing.Clock extends Dashing.Widget
+
+ ready: ->
+ setInterval(@startTime, 500)
+
+ startTime: =>
+ today = new Date()
+
+ h = today.getHours()
+ m = today.getMinutes()
+ s = today.getSeconds()
+ m = @formatTime(m)
+ s = @formatTime(s)
+ @set('time', h + ":" + m + ":" + s)
+ @set('date', today.toDateString())
+
+ formatTime: (i) ->
+ if i < 10 then "0" + i else i \ No newline at end of file
diff --git a/templates/project/widgets/clock/clock.html b/templates/project/widgets/clock/clock.html
new file mode 100644
index 0000000..06759d4
--- /dev/null
+++ b/templates/project/widgets/clock/clock.html
@@ -0,0 +1,2 @@
+<h1 data-bind="date"></h1>
+<h2 data-bind="time"></h2> \ No newline at end of file
diff --git a/templates/project/widgets/clock/clock.scss b/templates/project/widgets/clock/clock.scss
new file mode 100644
index 0000000..831ea94
--- /dev/null
+++ b/templates/project/widgets/clock/clock.scss
@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #dc5945;
+
+// ----------------------------------------------------------------------------
+// Widget-clock styles
+// ----------------------------------------------------------------------------
+.widget-clock {
+
+ background-color: $background-color;
+
+} \ No newline at end of file
diff --git a/templates/project/widgets/comments/comments.coffee b/templates/project/widgets/comments/comments.coffee
new file mode 100644
index 0000000..1e81b56
--- /dev/null
+++ b/templates/project/widgets/comments/comments.coffee
@@ -0,0 +1,24 @@
+class Dashing.Comments extends Dashing.Widget
+
+ @accessor 'quote', ->
+ "“#{@get('current_comment')?.body}”"
+
+ ready: ->
+ @currentIndex = 0
+ @commentElem = $(@node).find('.comment-container')
+ @nextComment()
+ @startCarousel()
+
+ onData: (data) ->
+ @currentIndex = 0
+
+ startCarousel: ->
+ setInterval(@nextComment, 8000)
+
+ nextComment: =>
+ comments = @get('comments')
+ if comments
+ @commentElem.fadeOut =>
+ @currentIndex = (@currentIndex + 1) % comments.length
+ @set 'current_comment', comments[@currentIndex]
+ @commentElem.fadeIn() \ No newline at end of file
diff --git a/templates/project/widgets/comments/comments.html b/templates/project/widgets/comments/comments.html
new file mode 100644
index 0000000..844800f
--- /dev/null
+++ b/templates/project/widgets/comments/comments.html
@@ -0,0 +1,7 @@
+<h1 class="title" data-bind="title"></h1>
+<div class="comment-container">
+ <h3><img data-bind-src='current_comment.avatar'/><span data-bind='current_comment.name' class="name"></span></h3>
+ <p class="comment" data-bind='quote'></p>
+</div>
+
+<p class="more-info" data-bind="moreinfo | raw"></p> \ No newline at end of file
diff --git a/templates/project/widgets/comments/comments.scss b/templates/project/widgets/comments/comments.scss
new file mode 100644
index 0000000..40e2a6b
--- /dev/null
+++ b/templates/project/widgets/comments/comments.scss
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #eb9c3c;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-comment styles
+// ----------------------------------------------------------------------------
+.widget-comments {
+
+ background-color: $background-color;
+
+ .title {
+ color: $title-color;
+ margin-bottom: 15px;
+ }
+
+ .name {
+ padding-left: 5px;
+ }
+
+ .comment-container {
+ display: none;
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
+} \ No newline at end of file
diff --git a/templates/project/widgets/graph/graph.coffee b/templates/project/widgets/graph/graph.coffee
index 8b56a87..9b2eeec 100644
--- a/templates/project/widgets/graph/graph.coffee
+++ b/templates/project/widgets/graph/graph.coffee
@@ -1,22 +1,32 @@
class Dashing.Graph extends Dashing.Widget
@accessor 'current', ->
+ return @get('displayedValue') if @get('displayedValue')
points = @get('points')
if points
points[points.length - 1].y
ready: ->
+ container = $(@node).parent()
+ # Gross hacks. Let's fix this.
+ width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
+ height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
@graph = new Rickshaw.Graph(
element: @node
- width: $(@node).parent().width()
+ width: width
+ height: height
series: [
{
color: "#fff",
- data: [{ x: 0, y: 0}]
+ data: [{x:0, y:0}]
}
]
)
+
+ @graph.series[0].data = @get('points') if @get('points')
+
x_axis = new Rickshaw.Graph.Axis.Time(graph: @graph)
+ y_axis = new Rickshaw.Graph.Axis.Y(graph: @graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT)
@graph.render()
onData: (data) ->
diff --git a/templates/project/widgets/graph/graph.html b/templates/project/widgets/graph/graph.html
index 8680efb..d1794db 100644
--- a/templates/project/widgets/graph/graph.html
+++ b/templates/project/widgets/graph/graph.html
@@ -1,5 +1,5 @@
<h1 class="title" data-bind="title"></h1>
-<h2 class="value" data-bind="current | prettyNumber"></h2>
+<h2 class="value" data-bind="current | prettyNumber | prepend prefix"></h2>
-<p class="text-moreinfo" data-bind="moreinfo"></p> \ No newline at end of file
+<p class="more-info" data-bind="moreinfo"></p> \ No newline at end of file
diff --git a/templates/project/widgets/graph/graph.scss b/templates/project/widgets/graph/graph.scss
index f2a1e21..71cd4d4 100644
--- a/templates/project/widgets/graph/graph.scss
+++ b/templates/project/widgets/graph/graph.scss
@@ -1,13 +1,12 @@
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
-$background-color: #2e864f;
-$value-color: #fff;
+$background-color: #dc5945;
-$title-color: lighten($background-color, 75%);
-$moreinfo-color: lighten($background-color, 45%);
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.3);
+$tick-color: rgba(0, 0, 0, 0.4);
-$graph_color: lighten($background-color, 75%);
// ----------------------------------------------------------------------------
// Widget-graph styles
@@ -17,14 +16,13 @@ $graph_color: lighten($background-color, 75%);
background-color: $background-color;
position: relative;
+
svg {
- color: $graph_color;
+ position: absolute;
opacity: 0.4;
fill-opacity: 0.4;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
+ left: 0px;
+ top: 0px;
}
.title, .value {
@@ -36,7 +34,7 @@ $graph_color: lighten($background-color, 75%);
color: $title-color;
}
- .text-moreinfo {
+ .more-info {
color: $moreinfo-color;
font-weight: 600;
font-size: 20px;
@@ -46,6 +44,22 @@ $graph_color: lighten($background-color, 75%);
.x_tick {
position: absolute;
bottom: 0;
+ .title {
+ font-size: 20px;
+ color: $tick-color;
+ opacity: 0.5;
+ padding-bottom: 3px;
+ }
+ }
+
+ .y_ticks {
+ font-size: 20px;
+ fill: $tick-color;
+ fill-opacity: 1;
+ }
+
+ .domain {
+ display: none;
}
} \ No newline at end of file
diff --git a/templates/project/widgets/iframe/iframe.coffee b/templates/project/widgets/iframe/iframe.coffee
new file mode 100644
index 0000000..4d4ca9a
--- /dev/null
+++ b/templates/project/widgets/iframe/iframe.coffee
@@ -0,0 +1,9 @@
+class Dashing.Iframe extends Dashing.Widget
+
+ ready: ->
+ # This is fired when the widget is done being rendered
+
+ onData: (data) ->
+ # Handle incoming data
+ # You can access the html node of this widget with `@node`
+ # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in. \ No newline at end of file
diff --git a/templates/project/widgets/iframe/iframe.html b/templates/project/widgets/iframe/iframe.html
new file mode 100644
index 0000000..bfccba4
--- /dev/null
+++ b/templates/project/widgets/iframe/iframe.html
@@ -0,0 +1 @@
+<iframe data-bind-src="url" frameborder=0></iframe> \ No newline at end of file
diff --git a/templates/project/widgets/iframe/iframe.scss b/templates/project/widgets/iframe/iframe.scss
new file mode 100644
index 0000000..7c757bf
--- /dev/null
+++ b/templates/project/widgets/iframe/iframe.scss
@@ -0,0 +1,8 @@
+.widget-iframe {
+ padding: 3px 0px 0px 0px !important;
+
+ iframe {
+ width: 100%;
+ height: 100%;
+ }
+} \ No newline at end of file
diff --git a/templates/project/widgets/image/image.coffee b/templates/project/widgets/image/image.coffee
new file mode 100644
index 0000000..418e385
--- /dev/null
+++ b/templates/project/widgets/image/image.coffee
@@ -0,0 +1,9 @@
+class Dashing.Image extends Dashing.Widget
+
+ ready: ->
+ # This is fired when the widget is done being rendered
+
+ onData: (data) ->
+ # Handle incoming data
+ # You can access the html node of this widget with `@node`
+ # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in. \ No newline at end of file
diff --git a/templates/project/widgets/image/image.html b/templates/project/widgets/image/image.html
new file mode 100644
index 0000000..cf2b228
--- /dev/null
+++ b/templates/project/widgets/image/image.html
@@ -0,0 +1 @@
+<img data-bind-src="image | prepend '/assets'" data-bind-width="width"/> \ No newline at end of file
diff --git a/templates/project/widgets/image/image.scss b/templates/project/widgets/image/image.scss
new file mode 100644
index 0000000..10ca81d
--- /dev/null
+++ b/templates/project/widgets/image/image.scss
@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #4b4b4b;
+
+// ----------------------------------------------------------------------------
+// Widget-image styles
+// ----------------------------------------------------------------------------
+.widget-image {
+
+ background-color: $background-color;
+
+} \ No newline at end of file
diff --git a/templates/project/widgets/list/list.coffee b/templates/project/widgets/list/list.coffee
index 3ca892b..3cbf7a1 100644
--- a/templates/project/widgets/list/list.coffee
+++ b/templates/project/widgets/list/list.coffee
@@ -1,13 +1,6 @@
class Dashing.List extends Dashing.Widget
- @accessor 'current', Dashing.AnimatedValue
-
- @accessor 'arrow', ->
- if @get('last')
- if parseInt(@get('current')) > parseInt(@get('last')) then 'arrow-up' else 'arrow-down'
-
ready: ->
- Batman.setImmediate =>
- if @get('unordered')
- $(@node).find('ol').remove()
- else
- $(@node).find('ul').remove() \ No newline at end of file
+ if @get('unordered')
+ $(@node).find('ol').remove()
+ else
+ $(@node).find('ul').remove() \ No newline at end of file
diff --git a/templates/project/widgets/list/list.html b/templates/project/widgets/list/list.html
index cc90d42..86752bf 100644
--- a/templates/project/widgets/list/list.html
+++ b/templates/project/widgets/list/list.html
@@ -12,4 +12,7 @@
<span class="label" data-bind="item.label"></span>
<span class="value" data-bind="item.value"></span>
</li>
-</ul> \ No newline at end of file
+</ul>
+
+<p class="more-info" data-bind="moreinfo"></p>
+<p class="updated-at" data-bind="updatedAtMessage"></p> \ No newline at end of file
diff --git a/templates/project/widgets/list/list.scss b/templates/project/widgets/list/list.scss
index 098200d..90b6c84 100644
--- a/templates/project/widgets/list/list.scss
+++ b/templates/project/widgets/list/list.scss
@@ -1,12 +1,12 @@
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
-
$background-color: #12b0c5;
$value-color: #fff;
-$title-color: lighten($background-color, 45%);
-$label-color: lighten($background-color, 45%);
+$title-color: rgba(255, 255, 255, 0.7);
+$label-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
// ----------------------------------------------------------------------------
// Widget-list styles
@@ -49,4 +49,12 @@ $label-color: lighten($background-color, 45%);
color: $value-color;
}
+ .updated-at {
+ color: rgba(0, 0, 0, 0.3);
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
} \ No newline at end of file
diff --git a/templates/project/widgets/meter/meter.html b/templates/project/widgets/meter/meter.html
index 6051d2f..7d51b15 100644
--- a/templates/project/widgets/meter/meter.html
+++ b/templates/project/widgets/meter/meter.html
@@ -2,4 +2,6 @@
<input class="meter" data-angleOffset=-125 data-angleArc=250 data-width=200 data-readOnly=true data-bind-value="value | shortenedNumber" data-bind-data-min="min" data-bind-data-max="max">
-<p class="text-moreinfo" data-bind="moreinfo"></p> \ No newline at end of file
+<p class="more-info" data-bind="moreinfo"></p>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p> \ No newline at end of file
diff --git a/templates/project/widgets/meter/meter.scss b/templates/project/widgets/meter/meter.scss
index dec0cc6..98cf638 100644
--- a/templates/project/widgets/meter/meter.scss
+++ b/templates/project/widgets/meter/meter.scss
@@ -1,15 +1,12 @@
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
-
$background-color: #9c4274;
-$value-color: #fff;
-$title-color: lighten($background-color, 75%);
-$moreinfo-color: lighten($background-color, 45%);
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.3);
-$meter-background: darken($background-color, 10%);
-$meter-foreground: lighten($background-color, 75%);
+$meter-background: darken($background-color, 15%);
// ----------------------------------------------------------------------------
// Widget-meter styles
@@ -20,18 +17,19 @@ $meter-foreground: lighten($background-color, 75%);
input.meter {
background-color: $meter-background;
- color: $meter-foreground;
+ color: #fff;
}
.title {
color: $title-color;
}
- .text-moreinfo {
+ .more-info {
color: $moreinfo-color;
- font-weight: 600;
- font-size: 20px;
- margin-top: 0;
+ }
+
+ .updated-at {
+ color: rgba(0, 0, 0, 0.3);
}
} \ No newline at end of file
diff --git a/templates/project/widgets/number/number.coffee b/templates/project/widgets/number/number.coffee
index 78e7523..46cd6c2 100644
--- a/templates/project/widgets/number/number.coffee
+++ b/templates/project/widgets/number/number.coffee
@@ -8,17 +8,13 @@ class Dashing.Number extends Dashing.Widget
if last != 0
diff = Math.abs(Math.round((current - last) / last * 100))
"#{diff}%"
+ else
+ ""
@accessor 'arrow', ->
if @get('last')
if parseInt(@get('current')) > parseInt(@get('last')) then 'icon-arrow-up' else 'icon-arrow-down'
- @accessor 'statusStyle', ->
- "status-#{@get('status')}"
-
- @accessor 'needsAttention', ->
- @get('status') == 'warning' || @get('status') == 'danger'
-
onData: (data) ->
if data.status
$(@get('node')).addClass("status-#{data.status}") \ No newline at end of file
diff --git a/templates/project/widgets/number/number.html b/templates/project/widgets/number/number.html
index 6e9a03e..d7eeab9 100644
--- a/templates/project/widgets/number/number.html
+++ b/templates/project/widgets/number/number.html
@@ -1,9 +1,11 @@
-<h1 class="title"><i data-showif="needsAttention" class="icon-warning-sign"></i><span data-bind="title"></span></h1>
+<h1 class="title" data-bind="title"></h1>
-<h2 class="value" data-bind="current | prettyNumber"></h2>
+<h2 class="value" data-bind="current | shortenedNumber | prepend prefix"></h2>
<p class="change-rate">
- <i data-bind-class="arrow"></i><span data-bind="difference">50%</span>
+ <i data-bind-class="arrow"></i><span data-bind="difference"></span>
</p>
-<p class="text-moreinfo" data-bind="moreinfo">2012-07-26 10:59AM</p> \ No newline at end of file
+<p class="more-info" data-bind="moreinfo | raw"></p>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p>
diff --git a/templates/project/widgets/number/number.scss b/templates/project/widgets/number/number.scss
index d134561..608624b 100644
--- a/templates/project/widgets/number/number.scss
+++ b/templates/project/widgets/number/number.scss
@@ -1,11 +1,11 @@
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
-$background-color: #00b1a4;
+$background-color: #47bbb3;
$value-color: #fff;
-$title-color: lighten($background-color, 75%);
-$moreinfo-color: lighten($background-color, 45%);
+$title-color: rgba(255, 255, 255, 0.7);;
+$moreinfo-color: rgba(255, 255, 255, 0.7);;
// ----------------------------------------------------------------------------
// Widget-number styles
@@ -23,13 +23,17 @@ $moreinfo-color: lighten($background-color, 45%);
}
.change-rate {
- font-weight: 300;
+ font-weight: 500;
font-size: 30px;
color: $value-color;
}
- .text-moreinfo {
+ .more-info {
color: $moreinfo-color;
}
+
+ .updated-at {
+ color: rgba(0, 0, 0, 0.3);
+ }
} \ No newline at end of file
diff --git a/templates/project/widgets/text/text.html b/templates/project/widgets/text/text.html
index 02bd0f7..45322ca 100644
--- a/templates/project/widgets/text/text.html
+++ b/templates/project/widgets/text/text.html
@@ -1,5 +1,7 @@
<h1 class="title" data-bind="title"></h1>
-<h3 data-bind="text"></h3>
+<h3 data-bind="text | raw"></h3>
-<p class="text-moreinfo" data-bind="moreinfo">2012-07-26 10:59AM</p> \ No newline at end of file
+<p class="more-info" data-bind="moreinfo | raw"></p>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p> \ No newline at end of file
diff --git a/templates/project/widgets/text/text.scss b/templates/project/widgets/text/text.scss
index 4293602..4e6c6e3 100644
--- a/templates/project/widgets/text/text.scss
+++ b/templates/project/widgets/text/text.scss
@@ -1,11 +1,10 @@
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
-$background-color: #ec663c;;
-$value-color: #fff;
+$background-color: #ec663c;
-$title-color: lighten($background-color, 40%);
-$moreinfo-color: lighten($background-color, 30%);
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
// ----------------------------------------------------------------------------
// Widget-text styles
@@ -18,8 +17,16 @@ $moreinfo-color: lighten($background-color, 30%);
color: $title-color;
}
- .text-moreinfo {
+ .more-info {
color: $moreinfo-color;
}
+
+ .updated-at {
+ color: rgba(255, 255, 255, 0.7);
+ }
+
+ &.large h3 {
+ font-size: 65px;
+ }
} \ No newline at end of file