From a4a4564f760bb1bcd541366186cd46488d5a569b Mon Sep 17 00:00:00 2001 From: Daniel Beauchamp Date: Tue, 30 Oct 2012 05:16:35 -0400 Subject: Added new widgets, and made them more flexible. Ready for 0.1.3! --- javascripts/batman.jquery.js | 52 + javascripts/batman.js | 7170 +++++++++++++++++++++++------------------- javascripts/dashing.coffee | 2 +- 3 files changed, 3916 insertions(+), 3308 deletions(-) (limited to 'javascripts') 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 }; @@ -46,6 +48,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 = { + "&": "&", + "<": "<", + ">": ">", + "\"": """, + "'": "'" + }; + 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; }; @@ -321,370 +642,51 @@ Inflector.singular(/([^aeiouy]|qu)ies$/i, '$1y'); - Inflector.singular(/(s)eries$/i, '$1eries'); - - Inflector.singular(/(m)ovies$/i, '$1ovie'); - - Inflector.singular(/(x|ch|ss|sh)es$/i, '$1'); - - Inflector.singular(/([m|l])ice$/i, '$1ouse'); - - Inflector.singular(/(bus)es$/i, '$1'); - - Inflector.singular(/(o)es$/i, '$1'); - - Inflector.singular(/(shoe)s$/i, '$1'); - - Inflector.singular(/(cris|ax|test)es$/i, '$1is'); - - Inflector.singular(/(octop|vir)i$/i, '$1us'); - - Inflector.singular(/(alias|status)es$/i, '$1'); - - Inflector.singular(/^(ox)en/i, '$1'); - - Inflector.singular(/(vert|ind)ices$/i, '$1ex'); - - Inflector.singular(/(matr)ices$/i, '$1ix'); - - Inflector.singular(/(quiz)zes$/i, '$1'); - - Inflector.singular(/(database)s$/i, '$1'); - - Inflector.irregular('person', 'people'); - - Inflector.irregular('man', 'men'); - - Inflector.irregular('child', 'children'); - - Inflector.irregular('sex', 'sexes'); - - Inflector.irregular('move', 'moves'); - - Inflector.irregular('cow', 'kine'); - - Inflector.irregular('zombie', 'zombies'); - - Inflector.uncountable('equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep', 'jeans'); - -}).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; - }; + Inflector.singular(/(s)eries$/i, '$1eries'); - Batman.setImmediate = function() { - _implementImmediates(Batman.container); - return Batman.setImmediate.apply(this, arguments); - }; + Inflector.singular(/(m)ovies$/i, '$1ovie'); - Batman.clearImmediate = function() { - _implementImmediates(Batman.container); - return Batman.clearImmediate.apply(this, arguments); - }; + Inflector.singular(/(x|ch|ss|sh)es$/i, '$1'); - 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; - } - }; + Inflector.singular(/([m|l])ice$/i, '$1ouse'); - Batman.objectHasKey = function(object, key) { - if (typeof object.hasKey === 'function') { - return object.hasKey(key); - } else { - return key in object; - } - }; + Inflector.singular(/(bus)es$/i, '$1'); - 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); - } - }; + Inflector.singular(/(o)es$/i, '$1'); - Batman.get = function(base, key) { - if (typeof base.get === 'function') { - return base.get(key); - } else { - return Batman.Property.forBaseAndKey(base, key).getValue(); - } - }; + Inflector.singular(/(shoe)s$/i, '$1'); - 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; - }; + Inflector.singular(/(cris|ax|test)es$/i, '$1is'); - Batman.escapeHTML = (function() { - var replacements; - replacements = { - "&": "&", - "<": "<", - ">": ">", - "\"": """, - "'": "'" - }; - return function(s) { - return ("" + s).replace(/[&<>'"]/g, function(c) { - return replacements[c]; - }); - }; - })(); + Inflector.singular(/(octop|vir)i$/i, '$1us'); - Batman.translate = function(x, values) { - if (values == null) { - values = {}; - } - return Batman.helpers.interpolate(Batman.get(Batman.translate.messages, x), values); - }; + Inflector.singular(/(alias|status)es$/i, '$1'); - Batman.translate.messages = {}; + Inflector.singular(/^(ox)en/i, '$1'); - Batman.t = function() { - return Batman.translate.apply(Batman, arguments); - }; + Inflector.singular(/(vert|ind)ices$/i, '$1ex'); - Batman.redirect = function(url) { - var _ref; - return (_ref = Batman.navigator) != null ? _ref.redirect(url) : void 0; - }; + Inflector.singular(/(matr)ices$/i, '$1ix'); - Batman.initializeObject = function(object) { - if (object._batman != null) { - return object._batman.check(object); - } else { - return object._batman = new Batman._Batman(object); - } - }; + Inflector.singular(/(quiz)zes$/i, '$1'); + + Inflector.singular(/(database)s$/i, '$1'); + + Inflector.irregular('person', 'people'); + + Inflector.irregular('man', 'men'); + + Inflector.irregular('child', 'children'); + + Inflector.irregular('sex', 'sexes'); + + Inflector.irregular('move', 'moves'); + + Inflector.irregular('cow', 'kine'); + + Inflector.irregular('zombie', 'zombies'); + + Inflector.uncountable('equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep', 'jeans'); }).call(this); @@ -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; - - BatmanObject.prototype._batmanID = function() { - var c; - this._batmanID = function() { - return c; - }; - return c = counter++; - }; + __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.hashKey = function() { - var key; - if (typeof this.isEqual === 'function') { - return; - } - this.hashKey = function() { - return key; + getAccessorObject = function(base, accessor) { + var deprecated, _i, _len, _ref; + if (typeof accessor === 'function') { + accessor = { + get: accessor }; - return key = ""; - }; - - 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 = ""; + }; + + 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); @@ -5417,9 +5553,97 @@ }).call(this); -(function() { +(function() { + + Batman.Encoders = new 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.ParamsReplacer = (function(_super) { + + __extends(ParamsReplacer, _super); + + function ParamsReplacer(navigator, params) { + this.navigator = navigator; + this.params = params; + } + + ParamsReplacer.prototype.redirect = function() { + return this.navigator.replace(this.toObject()); + }; + + ParamsReplacer.prototype.replace = function(params) { + this.params.replace(params); + return this.redirect(); + }; + + ParamsReplacer.prototype.update = function(params) { + this.params.update(params); + return this.redirect(); + }; + + ParamsReplacer.prototype.clear = function() { + this.params.clear(); + return this.redirect(); + }; + + ParamsReplacer.prototype.toObject = function() { + return this.params.toObject(); + }; + + ParamsReplacer.accessor({ + get: function(k) { + return this.params.get(k); + }, + set: function(k, v) { + var oldValue, result; + oldValue = this.params.get(k); + result = this.params.set(k, v); + if (oldValue !== v) { + this.redirect(); + } + return result; + }, + unset: function(k) { + var hadKey, result; + hadKey = this.params.hasKey(k); + result = this.params.unset(k); + if (hadKey) { + this.redirect(); + } + return result; + } + }); + + return ParamsReplacer; + + })(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.ParamsPusher = (function(_super) { + + __extends(ParamsPusher, _super); + + function ParamsPusher() { + return ParamsPusher.__super__.constructor.apply(this, arguments); + } + + ParamsPusher.prototype.redirect = function() { + return this.navigator.push(this.toObject()); + }; + + return ParamsPusher; - Batman.Encoders = new Batman.Object; + })(Batman.ParamsReplacer); }).call(this); @@ -5427,75 +5651,139 @@ 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) { + Batman.NamedRouteQuery = (function(_super) { - __extends(AssociationProxy, _super); + __extends(NamedRouteQuery, _super); - AssociationProxy.prototype.isProxy = true; + NamedRouteQuery.prototype.isNamedRouteQuery = true; - function AssociationProxy(association, model) { - this.association = association; - this.model = model; + function NamedRouteQuery(routeMap, args) { + var key; + if (args == null) { + args = []; + } + NamedRouteQuery.__super__.constructor.call(this, { + routeMap: routeMap, + args: args + }); + for (key in this.get('routeMap').childrenByName) { + this[key] = this._queryAccess.bind(this, key); + } } - AssociationProxy.prototype.loaded = false; - - AssociationProxy.prototype.toJSON = function() { - var target; - target = this.get('target'); - if (target != null) { - return this.get('target').toJSON(); + NamedRouteQuery.accessor('route', function() { + var collectionRoute, memberRoute, route, _i, _len, _ref, _ref1; + _ref = this.get('routeMap'), memberRoute = _ref.memberRoute, collectionRoute = _ref.collectionRoute; + _ref1 = [memberRoute, collectionRoute]; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + route = _ref1[_i]; + if (route != null) { + if (route.namedArguments.length === this.get('args').length) { + return route; + } + } } - }; + return collectionRoute || memberRoute; + }); - AssociationProxy.prototype.load = function(callback) { - var _this = this; - this.fetch(function(err, proxiedRecord) { - if (!err) { - _this.set('loaded', true); - _this.set('target', proxiedRecord); + NamedRouteQuery.accessor('path', function() { + return this.path(); + }); + + NamedRouteQuery.accessor('routeMap', 'args', 'cardinality', 'hashValue', Batman.Property.defaultAccessor); + + NamedRouteQuery.accessor({ + get: function(key) { + if (key == null) { + return; } - return typeof callback === "function" ? callback(err, proxiedRecord) : void 0; + if (typeof key === 'string') { + return this.nextQueryForName(key); + } else { + return this.nextQueryWithArgument(key); + } + }, + cache: false + }); + + NamedRouteQuery.accessor('withHash', function() { + var _this = this; + return new Batman.Accessible(function(hashValue) { + return _this.withHash(hashValue); }); - return this.get('target'); + }); + + NamedRouteQuery.prototype.withHash = function(hashValue) { + var clone; + clone = this.clone(); + clone.set('hashValue', hashValue); + return clone; }; - 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); + NamedRouteQuery.prototype.nextQueryForName = function(key) { + var map; + if (map = this.get('routeMap').childrenByName[key]) { + return new Batman.NamedRouteQuery(map, this.args); } else { - return this.fetchFromRemote(callback); + return Batman.developer.error("Couldn't find a route for the name " + key + "!"); } }; - AssociationProxy.accessor('loaded', Batman.Property.defaultAccessor); + NamedRouteQuery.prototype.nextQueryWithArgument = function(arg) { + var args; + args = this.args.slice(0); + args.push(arg); + return this.clone(args); + }; - AssociationProxy.accessor('target', { - get: function() { - return this.fetchFromLocal(); - }, - set: function(_, v) { - return v; + NamedRouteQuery.prototype.path = function() { + var argumentName, argumentValue, index, namedArguments, params, _i, _len; + params = {}; + namedArguments = this.get('route.namedArguments'); + for (index = _i = 0, _len = namedArguments.length; _i < _len; index = ++_i) { + argumentName = namedArguments[index]; + if ((argumentValue = this.get('args')[index]) != null) { + params[argumentName] = this._toParam(argumentValue); + } } - }); + if (this.get('hashValue') != null) { + params['#'] = this.get('hashValue'); + } + return this.get('route').pathFromParams(params); + }; - 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; + NamedRouteQuery.prototype.toString = function() { + return this.path(); + }; + + NamedRouteQuery.prototype.clone = function(args) { + if (args == null) { + args = this.args; } - }); + return new Batman.NamedRouteQuery(this.routeMap, args); + }; - return AssociationProxy; + NamedRouteQuery.prototype._toParam = function(arg) { + if (arg instanceof Batman.AssociationProxy) { + arg = arg.get('target'); + } + if ((arg != null ? arg.toParam : void 0) != null) { + return arg.toParam(); + } else { + return arg; + } + }; + + NamedRouteQuery.prototype._queryAccess = function(key, arg) { + var query; + query = this.nextQueryForName(key); + if (arg != null) { + query = query.nextQueryWithArgument(arg); + } + return query; + }; + + return NamedRouteQuery; })(Batman.Object); @@ -5505,2148 +5793,2438 @@ 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); - } + Batman.Dispatcher = (function(_super) { + var ControllerDirectory; - HasOneProxy.accessor('primaryValue', function() { - return this.model.get(this.association.primaryKey); - }); + __extends(Dispatcher, _super); - HasOneProxy.prototype.fetchFromLocal = function() { - return this.association.setIndex().get(this.get('primaryValue')); + Dispatcher.canInferRoute = function(argument) { + return argument instanceof Batman.Model || argument instanceof Batman.AssociationProxy || argument.prototype instanceof Batman.Model; }; - 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; + Dispatcher.paramsFromArgument = function(argument) { + var resourceNameFromModel; + resourceNameFromModel = function(model) { + return Batman.helpers.camelize(Batman.helpers.pluralize(model.get('resourceName')), true); + }; + if (!this.canInferRoute(argument)) { + return argument; + } + if (argument instanceof Batman.Model || argument instanceof Batman.AssociationProxy) { + if (argument.isProxy) { + argument = argument.get('target'); } - if (!loadedRecords || loadedRecords.length <= 0) { - return callback(new Error("Couldn't find related record!"), void 0); + if (argument != null) { + return { + controller: resourceNameFromModel(argument.constructor), + action: 'show', + id: argument.get('id') + }; } else { - return callback(void 0, loadedRecords[0]); + return {}; } - }); + } else if (argument.prototype instanceof Batman.Model) { + return { + controller: resourceNameFromModel(argument), + action: 'index' + }; + } else { + return argument; + } }; - 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); + ControllerDirectory = (function(_super1) { - function BelongsToProxy() { - return BelongsToProxy.__super__.constructor.apply(this, arguments); - } + __extends(ControllerDirectory, _super1); - BelongsToProxy.accessor('foreignValue', function() { - return this.model.get(this.association.foreignKey); - }); + function ControllerDirectory() { + return ControllerDirectory.__super__.constructor.apply(this, arguments); + } - BelongsToProxy.prototype.fetchFromLocal = function() { - return this.association.setIndex().get(this.get('foreignValue')); - }; + ControllerDirectory.accessor('__app', Batman.Property.defaultAccessor); - 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); + ControllerDirectory.accessor(function(key) { + return this.get("__app." + (Batman.helpers.capitalize(key)) + "Controller.sharedController"); }); - }; - - 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; }; + return ControllerDirectory; - Batman.PolymorphicBelongsToProxy = (function(_super) { + })(Batman.Object); - __extends(PolymorphicBelongsToProxy, _super); + Dispatcher.accessor('controllers', function() { + return new ControllerDirectory({ + __app: this.get('app') + }); + }); - function PolymorphicBelongsToProxy() { - return PolymorphicBelongsToProxy.__super__.constructor.apply(this, arguments); + function Dispatcher(app, routeMap) { + Dispatcher.__super__.constructor.call(this, { + app: app, + routeMap: routeMap + }); } - PolymorphicBelongsToProxy.accessor('foreignTypeValue', function() { - return this.model.get(this.association.foreignTypeKey); - }); + Dispatcher.prototype.routeForParams = function(params) { + params = this.constructor.paramsFromArgument(params); + return this.get('routeMap').routeForParams(params); + }; - PolymorphicBelongsToProxy.prototype.fetchFromLocal = function() { - return this.association.setIndexForType(this.get('foreignTypeValue')).get(this.get('foreignValue')); + Dispatcher.prototype.pathFromParams = function(params) { + var _ref; + if (typeof params === 'string') { + return params; + } + params = this.constructor.paramsFromArgument(params); + return (_ref = this.routeForParams(params)) != null ? _ref.pathFromParams(params) : void 0; }; - 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; + Dispatcher.prototype.dispatch = function(params) { + var error, inferredParams, path, route, _ref, _ref1; + inferredParams = this.constructor.paramsFromArgument(params); + route = this.routeForParams(inferredParams); + if (route) { + _ref = route.pathAndParamsFromArgument(inferredParams), path = _ref[0], params = _ref[1]; + this.set('app.currentRoute', route); + this.set('app.currentURL', path); + this.get('app.currentParams').replace(params || {}); + route.dispatch(params); + } else { + if (Batman.typeOf(params) === 'Object' && !this.constructor.canInferRoute(params)) { + return this.get('app.currentParams').replace(params); + } else { + this.get('app.currentParams').clear(); } - return callback(void 0, loadedRecord); - }); + 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'); + } + } + return path; }; - return PolymorphicBelongsToProxy; + return Dispatcher; - })(Batman.BelongsToProxy); + }).call(this, 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; + __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.StateMachine = (function(_super) { + Batman.Route = (function(_super) { - __extends(StateMachine, _super); + __extends(Route, _super); - StateMachine.InvalidTransitionError = function(message) { - this.message = message != null ? message : ""; + Route.regexps = { + namedParam: /:([\w\d]+)/g, + splatParam: /\*([\w\d]+)/g, + queryParam: '(?:\\?.+)?', + namedOrSplat: /[:|\*]([\w\d]+)/g, + namePrefix: '[:|\*]', + escapeRegExp: /[-[\]{}+?.,\\^$|#\s]/g, + openOptParam: /\(/g, + closeOptParam: /\)/g }; - StateMachine.InvalidTransitionError.prototype = new Error; + Route.prototype.optionKeys = ['member', 'collection']; - 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; + Route.prototype.testKeys = ['controller', 'action']; + + Route.prototype.isRoute = true; + + function Route(templatePath, baseParams) { + var k, matches, namedArguments, pattern, properties, regexp, regexps, _i, _len, _ref; + regexps = this.constructor.regexps; + if (templatePath.indexOf('/') !== 0) { + templatePath = "/" + templatePath; } - 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; + 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 = []; + while (matches = regexps.namedOrSplat.exec(pattern)) { + _results.push(matches[1]); } - 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); - }; + return _results; + })()); + properties = { + templatePath: templatePath, + pattern: pattern, + regexp: regexp, + namedArguments: namedArguments, + baseParams: baseParams }; - for (k in _ref) { - transitions = _ref[k]; - if (!(!this.prototype[k])) { - continue; + _ref = this.optionKeys; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + k = _ref[_i]; + properties[k] = baseParams[k]; + delete baseParams[k]; + } + Route.__super__.constructor.call(this, properties); + } + + Route.prototype.paramsFromPath = function(pathAndQuery) { + var index, match, matches, name, namedArguments, params, uri, _i, _len; + uri = new Batman.URI(pathAndQuery); + namedArguments = this.get('namedArguments'); + params = Batman.extend({ + path: uri.path + }, this.get('baseParams')); + matches = this.get('regexp').exec(uri.path).slice(1); + for (index = _i = 0, _len = matches.length; _i < _len; index = ++_i) { + match = matches[index]; + name = namedArguments[index]; + params[name] = match; + } + return Batman.extend(params, uri.queryParams); + }; + + Route.prototype.pathFromParams = function(argumentParams) { + var hash, key, name, newPath, params, path, query, regexp, regexps, _i, _j, _len, _len1, _ref, _ref1; + params = Batman.extend({}, argumentParams); + path = this.get('templatePath'); + regexps = this.constructor.regexps; + _ref = this.get('namedArguments'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + name = _ref[_i]; + regexp = RegExp("" + regexps.namePrefix + name); + newPath = path.replace(regexp, (params[name] != null ? params[name] : '')); + if (newPath !== path) { + delete params[name]; + path = newPath; } - _fn(k); - for (fromState in transitions) { - toState = transitions[fromState]; - definePredicate(fromState); - definePredicate(toState); + } + path = path.replace(regexps.openOptParam, '').replace(regexps.closeOptParam, '').replace(/([^\/])\/+$/, '$1'); + _ref1 = this.testKeys; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + key = _ref1[_j]; + delete params[key]; + } + if (params['#']) { + hash = params['#']; + delete params['#']; + } + query = Batman.URI.queryFromParams(params); + if (query) { + path += "?" + query; + } + if (hash) { + path += "#" + hash; + } + return path; + }; + + Route.prototype.test = function(pathOrParams) { + var key, path, value, _i, _len, _ref; + if (typeof pathOrParams === 'string') { + path = pathOrParams; + } else if (pathOrParams.path != null) { + path = pathOrParams.path; + } else { + path = this.pathFromParams(pathOrParams); + _ref = this.testKeys; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if ((value = this.get(key)) != null) { + if (pathOrParams[key] !== value) { + return false; + } + } } } - if (predicateKeys.length) { - this.accessor.apply(this, __slice.call(predicateKeys).concat([function(key) { - return this[key](); - }])); + return this.get('regexp').test(path); + }; + + Route.prototype.pathAndParamsFromArgument = function(pathOrParams) { + var params, path; + if (typeof pathOrParams === 'string') { + params = this.paramsFromPath(pathOrParams); + path = pathOrParams; + } else { + params = pathOrParams; + path = this.pathFromParams(pathOrParams); } - return this; + return [path, params]; + }; + + Route.prototype.dispatch = function(params) { + if (!this.test(params)) { + return false; + } + return this.get('callback')(params); + }; + + Route.prototype.callback = function() { + throw new Batman.DevelopmentError("Override callback in a Route subclass"); }; - function StateMachine(startState) { - this.nextEvents = []; - this.set('_state', startState); - } + return Route; - StateMachine.accessor('state', function() { - return this.get('_state'); - }); + })(Batman.Object); - StateMachine.prototype.isTransitioning = false; +}).call(this); - StateMachine.prototype.transitionTable = {}; +(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; }; - StateMachine.prototype.onTransition = function(from, into, callback) { - return this.on("" + from + "->" + into, callback); - }; + Batman.ControllerActionRoute = (function(_super) { - StateMachine.prototype.onEnter = function(into, callback) { - return this.on("enter " + into, callback); - }; + __extends(ControllerActionRoute, _super); - StateMachine.prototype.onExit = function(from, callback) { - return this.on("exit " + from, callback); - }; + ControllerActionRoute.prototype.optionKeys = ['member', 'collection', 'app', 'controller', 'action']; - 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; - }); + function ControllerActionRoute(templatePath, options) { + this.callback = __bind(this.callback, this); - StateMachine.prototype.canStartTransition = function(event, fromState) { - if (fromState == null) { - fromState = this.get('state'); + var action, controller, _ref; + if (options.signature) { + _ref = options.signature.split('#'), controller = _ref[0], action = _ref[1]; + action || (action = 'index'); + options.controller = controller; + options.action = action; + delete options.signature; } - return !!this.nextStateForEvent(event, fromState); - }; + ControllerActionRoute.__super__.constructor.call(this, templatePath, options); + } - 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; + ControllerActionRoute.prototype.callback = function(params) { + var controller; + controller = this.get("app.dispatcher.controllers." + (this.get('controller'))); + return controller.dispatch(this.get('action'), params); }; - return StateMachine; + return ControllerActionRoute; - })(Batman.Object); + })(Batman.Route); - Batman.DelegatingStateMachine = (function(_super) { +}).call(this); - __extends(DelegatingStateMachine, _super); +(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; }; - function DelegatingStateMachine(startState, base) { - this.base = base; - DelegatingStateMachine.__super__.constructor.call(this, startState); + Batman.CallbackActionRoute = (function(_super) { + + __extends(CallbackActionRoute, _super); + + function CallbackActionRoute() { + return CallbackActionRoute.__super__.constructor.apply(this, arguments); } - DelegatingStateMachine.prototype.fire = function() { - var result, _ref; - result = DelegatingStateMachine.__super__.fire.apply(this, arguments); - (_ref = this.base).fire.apply(_ref, arguments); - return result; - }; + CallbackActionRoute.prototype.optionKeys = ['member', 'collection', 'callback', 'app']; - return DelegatingStateMachine; + CallbackActionRoute.prototype.controller = false; - })(Batman.StateMachine); + CallbackActionRoute.prototype.action = false; + + return CallbackActionRoute; + + })(Batman.Route); }).call(this); (function() { - var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - __hasProp = {}.hasOwnProperty, + 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.Model = (function(_super) { - var functionName, _i, _j, _len, _len1, _ref, _ref1; - - __extends(Model, _super); - - Model.primaryKey = 'id'; + Batman.Hash = (function(_super) { + var k, _fn, _i, _j, _len, _len1, _ref, _ref1, + _this = this; - Model.storageKey = null; + __extends(Hash, _super); - 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; - }; + Hash.Metadata = (function(_super1) { - Model.storageAdapter = function() { - Batman.initializeObject(this.prototype); - return this.prototype._batman.storage; - }; + __extends(Metadata, _super1); - 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); - } - }; + Batman.extend(Metadata.prototype, Batman.Enumerable); - Model.defaultEncoder = { - encode: function(x) { - return x; - }, - decode: function(x) { - return x; + function Metadata(hash) { + this.hash = hash; } - }; - Model.observeAndFire('primaryKey', function(newPrimaryKey, oldPrimaryKey) { - this.encode(oldPrimaryKey, { - encode: false, - decode: false + Metadata.accessor('length', function() { + this.hash.registerAsMutableSource(); + return this.hash.length; }); - return this.encode(newPrimaryKey, { - encode: false, - decode: this.defaultEncoder.decode + + Metadata.accessor('isEmpty', 'keys', 'toArray', function(key) { + this.hash.registerAsMutableSource(); + return this.hash[key](); }); - }); - 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); - } + Metadata.prototype.forEach = function() { + var _ref; + return (_ref = this.hash).forEach.apply(_ref, arguments); + }; + + return Metadata; + + })(Batman.Object); + + function Hash() { + this.meta = new this.constructor.Metadata(this); + Batman.SimpleHash.apply(this, arguments); + Hash.__super__.constructor.apply(this, arguments); + } + + Batman.extend(Hash.prototype, Batman.Enumerable); + + Hash.prototype.propertyClass = Batman.Property; + + Hash.defaultAccessor = { + get: Batman.SimpleHash.prototype.get, + set: Hash.mutation(function(key, value) { + var result; + result = Batman.SimpleHash.prototype.set.call(this, key, value); + this.fire('itemsWereAdded', key); + return result; + }), + unset: Hash.mutation(function(key) { + var result; + result = Batman.SimpleHash.prototype.unset.call(this, key); + if (result != null) { + this.fire('itemsWereRemoved', key); } - return _results; + return result; + }), + cache: false + }; + + Hash.accessor(Hash.defaultAccessor); + + Hash.prototype._preventMutationEvents = function(block) { + this.prevent('change'); + this.prevent('itemsWereAdded'); + this.prevent('itemsWereRemoved'); + try { + return block.call(this); + } finally { + this.allow('change'); + this.allow('itemsWereAdded'); + this.allow('itemsWereRemoved'); } }; - 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' - } + Hash.prototype.clear = Hash.mutation(function() { + var keys, result; + keys = this.keys(); + this._preventMutationEvents(function() { + var _this = this; + return this.forEach(function(k) { + return _this.unset(k); + }); }); - - 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)); + result = Batman.SimpleHash.prototype.clear.call(this); + this.fire.apply(this, ['itemsWereRemoved'].concat(__slice.call(keys))); + return result; }); - 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."); + Hash.prototype.update = Hash.mutation(function(object) { + var addedKeys; + addedKeys = []; + this._preventMutationEvents(function() { + var _this = this; + return Batman.forEach(object, function(k, v) { + if (!_this.hasKey(k)) { + addedKeys.push(k); } - return Batman.helpers.underscore(Batman.functionName(this)); - } + return _this.set(k, v); + }); + }); + if (addedKeys.length > 0) { + return this.fire.apply(this, ['itemsWereAdded'].concat(__slice.call(addedKeys))); } }); - 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); + Hash.prototype.replace = Hash.mutation(function(object) { + var addedKeys, removedKeys; + addedKeys = []; + removedKeys = []; + this._preventMutationEvents(function() { + var _this = this; + this.forEach(function(k, _) { + if (!Batman.objectHasKey(object, k)) { + _this.unset(k); + return removedKeys.push(k); + } + }); + return Batman.forEach(object, function(k, v) { + if (!_this.hasKey(k)) { + addedKeys.push(k); + } + return _this.set(k, v); + }); + }); + if (addedKeys.length > 0) { + this.fire.apply(this, ['itemsWereAdded'].concat(__slice.call(addedKeys))); } - }); - - Model.classAccessor('loaded', { - get: function() { - return this._loaded || (this._loaded = new Batman.Set); - }, - set: function(k, v) { - return this._loaded = v; + if (removedKeys.length > 0) { + return this.fire.apply(this, ['itemsWereRemoved'].concat(__slice.call(removedKeys))); } }); - 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; - }; + _ref = ['equality', 'hashKeyFor', 'objectKey', 'prefixedKey', 'unprefixedKey']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + k = _ref[_i]; + Hash.prototype[k] = Batman.SimpleHash.prototype[k]; + } - 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; + _ref1 = ['hasKey', 'forEach', 'isEmpty', 'keys', 'toArray', 'merge', 'toJSON', 'toObject']; + _fn = function(k) { + return Hash.prototype[k] = function() { + this.registerAsMutableSource(); + return Batman.SimpleHash.prototype[k].apply(this, arguments); + }; }; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + k = _ref1[_j]; + _fn(k); + } - 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')))); - } - }; + return Hash; - 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; - }; + }).call(this, Batman.Object); - 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; - }; +}).call(this); - 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; - } - } - }; +(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; }; - 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); - }; + Batman.RenderCache = (function(_super) { - _ref = ['find', 'load', 'create']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - functionName = _ref[_i]; - Model[functionName] = Batman.Property.wrapTrackingPrevention(Model[functionName]); - } + __extends(RenderCache, _super); - Model.InstanceLifecycleStateMachine = (function(_super1) { + RenderCache.prototype.maximumLength = 4; - __extends(InstanceLifecycleStateMachine, _super1); + function RenderCache() { + RenderCache.__super__.constructor.apply(this, arguments); + this.keyQueue = []; + } - function InstanceLifecycleStateMachine() { - return InstanceLifecycleStateMachine.__super__.constructor.apply(this, arguments); + RenderCache.prototype.viewForOptions = function(options) { + var _this = this; + if (options.cache === false || options.viewClass.prototype.cache === false) { + return this._newViewFromOptions(options); } - - 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' + return this.getOrSet(options, function() { + return _this._newViewFromOptions(Batman.extend({}, options)); + }); + }; + + RenderCache.prototype._newViewFromOptions = function(options) { + return new options.viewClass(options); + }; + + RenderCache.wrapAccessor(function(core) { + return { + cache: false, + get: function(key) { + var result; + result = core.get.call(this, key); + if (result) { + this._addOrBubbleKey(key); + } + return result; }, - set: { - from: ['dirty', 'clean'], - to: 'dirty' + set: function(key, value) { + var result; + result = core.set.apply(this, arguments); + result.set('cached', true); + this._addOrBubbleKey(key); + this._evictExpiredKeys(); + return result; }, - error: { - from: ['saving', 'creating', 'loading', 'destroying'], - to: 'error' + unset: function(key) { + var result; + result = core.unset.apply(this, arguments); + result.set('cached', false); + this._removeKeyFromQueue(key); + return result; } - }); - - return InstanceLifecycleStateMachine; + }; + }); - })(Batman.DelegatingStateMachine); + RenderCache.prototype.equality = function(incomingOptions, storageOptions) { + var key; + if (Object.keys(incomingOptions).length !== Object.keys(storageOptions).length) { + return false; + } + for (key in incomingOptions) { + if (!(key === 'view')) { + if (incomingOptions[key] !== storageOptions[key]) { + return false; + } + } + } + return true; + }; - function Model(idOrAttributes) { - if (idOrAttributes == null) { - idOrAttributes = {}; + RenderCache.prototype.reset = function() { + var key, _i, _len, _ref, _results; + _ref = this.keyQueue.slice(0); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push(this.unset(key)); } - this.destroy = __bind(this.destroy, this); + return _results; + }; - this.save = __bind(this.save, this); + RenderCache.prototype._addOrBubbleKey = function(key) { + this._removeKeyFromQueue(key); + return this.keyQueue.unshift(key); + }; - this.load = __bind(this.load, this); + RenderCache.prototype._removeKeyFromQueue = function(key) { + var index, queuedKey, _i, _len, _ref; + _ref = this.keyQueue; + for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) { + queuedKey = _ref[index]; + if (this.equality(queuedKey, key)) { + this.keyQueue.splice(index, 1); + break; + } + } + return key; + }; - 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); + RenderCache.prototype._evictExpiredKeys = function() { + var currentKeys, i, key, _i, _ref, _ref1; + if (this.length > this.maximumLength) { + currentKeys = this.keyQueue.slice(0); + for (i = _i = _ref = this.maximumLength, _ref1 = currentKeys.length; _ref <= _ref1 ? _i < _ref1 : _i > _ref1; i = _ref <= _ref1 ? ++_i : --_i) { + key = currentKeys[i]; + if (!this.get(key).isInDOM()) { + this.unset(key); + } + } } - } + }; - Model.accessor('lifecycle', function() { - return this.lifecycle || (this.lifecycle = new Batman.Model.InstanceLifecycleStateMachine('clean', this)); - }); + return RenderCache; - Model.accessor('attributes', function() { - return this.attributes || (this.attributes = new Batman.Hash); - }); + })(Batman.Hash); - Model.accessor('dirtyKeys', function() { - return this.dirtyKeys || (this.dirtyKeys = new Batman.Hash); - }); +}).call(this); - Model.accessor('_dirtiedKeys', function() { - return this._dirtiedKeys || (this._dirtiedKeys = new Batman.SimpleSet); - }); +(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, + __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; }; - Model.accessor('errors', function() { - return this.errors || (this.errors = new Batman.ErrorsSet); - }); + Batman.Controller = (function(_super) { + var _optionsFromFilterArguments; - Model.accessor('isNew', function() { - return this.isNew(); - }); + __extends(Controller, _super); - 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); - } - }); + Controller.singleton('sharedController'); - Model.wrapAccessor('id', function(core) { + Controller.wrapAccessor('routingKey', 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); + if (this.routingKey != null) { + return this.routingKey; } else { - return this.set(primaryKey, value); + if (Batman.config.minificationErrors) { + Batman.developer.error("Please define `routingKey` on the prototype of " + (Batman.functionName(this.constructor)) + " in order for your controller to be minification safe."); + } + return Batman.functionName(this.constructor).replace(/Controller$/, ''); } } }; }); - Model.prototype.isNew = function() { - return typeof this.get('id') === 'undefined'; + Controller.accessor('_renderContext', function() { + return Batman.RenderContext.root().descend(this); + }); + + _optionsFromFilterArguments = function(options, nameOrFunction) { + if (!nameOrFunction) { + nameOrFunction = options; + options = {}; + } else { + if (typeof options === 'string') { + options = { + only: [options] + }; + } else { + if (options.only && Batman.typeOf(options.only) !== 'Array') { + options.only = [options.only]; + } + if (options.except && Batman.typeOf(options.except) !== 'Array') { + options.except = [options.except]; + } + } + } + options.block = nameOrFunction; + return options; }; - Model.prototype.updateAttributes = function(attrs) { - this.mixin(attrs); - return this; + Controller.beforeFilter = function() { + var filters, options, _base; + Batman.initializeObject(this); + options = _optionsFromFilterArguments.apply(null, arguments); + filters = (_base = this._batman).beforeFilters || (_base.beforeFilters = []); + return filters.push(options); }; - Model.prototype.toString = function() { - return "" + (this.constructor.get('resourceName')) + ": " + (this.get('id')); + Controller.afterFilter = function() { + var filters, options, _base; + Batman.initializeObject(this); + options = _optionsFromFilterArguments.apply(null, arguments); + filters = (_base = this._batman).afterFilters || (_base.afterFilters = []); + return filters.push(options); }; - Model.prototype.toParam = function() { - return this.get('id'); + Controller.afterFilter(function(params) { + if (this.autoScrollToHash && (params['#'] != null)) { + return this.scrollToHash(params['#']); + } + }); + + 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; }; - Model.prototype.toJSON = function() { - var encoders, obj, + Controller.prototype.errorHandler = function(callback) { + var errorFrame, _ref, _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; + 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 obj; + return handled; }; - 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); - } - }); + 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(); + } + + Controller.prototype.renderCache = new Batman.RenderCache; + + Controller.prototype.defaultRenderYield = 'main'; + + Controller.prototype.autoScrollToHash = true; + + Controller.prototype.dispatch = function(action, params) { + var redirectTo; + if (params == null) { + params = {}; } - if (this.constructor.primaryKey !== 'id') { - obj.id = data[this.constructor.primaryKey]; + params.controller || (params.controller = this.get('routingKey')); + params.action || (params.action = action); + params.target || (params.target = this); + this._resetActionFrames(); + this.set('action', action); + this.set('params', params); + Batman.DOM.Yield.cycleAll(); + this.executeAction(action, params); + Batman.DOM.Yield.clearAllStale(); + redirectTo = this._afterFilterRedirect; + delete this._afterFilterRedirect; + if (redirectTo) { + return Batman.redirect(redirectTo); } - 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, + Controller.prototype.executeAction = function(action, params) { + var frame, oldRedirect, parentFrame, result, _ref, _ref1, _this = this; - if (!callback) { - _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1]; + if (params == null) { + params = this.get('params'); } - 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!")); + Batman.developer.assert(this[action], "Error! Controller action " + (this.get('routingKey')) + "." + action + " couldn't be found!"); + parentFrame = this._actionFrames[this._actionFrames.length - 1]; + frame = new Batman.ControllerActionFrame({ + parentFrame: parentFrame, + action: action + }, function() { + var _ref; + if (!_this._afterFilterRedirect) { + _this._runFilters(action, params, 'afterFilters'); } - return; + _this._resetActionFrames(); + return (_ref = Batman.navigator) != null ? _ref.redirect = oldRedirect : void 0; + }); + this._actionFrames.push(frame); + frame.startOperation({ + internal: true + }); + oldRedirect = (_ref = Batman.navigator) != null ? _ref.redirect : void 0; + if ((_ref1 = Batman.navigator) != null) { + _ref1.redirect = this.redirect; } - if (this.get('lifecycle').load()) { - callbackQueue = []; - if (callback != null) { - callbackQueue.push(callback); + this._runFilters(action, params, 'beforeFilters'); + if (!this._afterFilterRedirect) { + result = this[action](params); + } + if (!frame.operationOccurred) { + this.render(); + } + frame.finishOperation(); + return result; + }; + + Controller.prototype.redirect = function(url) { + var frame; + frame = this._actionFrames[this._actionFrames.length - 1]; + if (frame) { + if (frame.operationOccurred) { + Batman.developer.warn("Warning! Trying to redirect but an action has already been taken during " + (this.get('routingKey')) + "." + (frame.action || this.get('action'))); } - if (!hasOptions) { - this._currentLoad = callbackQueue; + frame.startAndFinishOperation(); + if (this._afterFilterRedirect != null) { + return Batman.developer.warn("Warning! Multiple actions trying to redirect!"); + } else { + return this._afterFilterRedirect = url; } - 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); + if (Batman.typeOf(url) === 'Object') { + if (!url.controller) { + url.controller = this; } - } else { - return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't load while in state " + (this.get('lifecycle.state')))) : void 0; } + return Batman.redirect(url); } }; - Model.prototype.save = function(options, callback) { - var endState, isNew, startState, storageOperation, _ref1, _ref2, _ref3, + Controller.prototype.render = function(options) { + var action, frame, view, _ref, _ref1, _this = this; - if (!callback) { - _ref1 = [{}, options], options = _ref1[0], callback = _ref1[1]; + if (options == null) { + options = {}; } - if ((_ref2 = this.get('lifecycle').get('state')) === 'destroying' || _ref2 === 'destroyed') { - if (typeof callback === "function") { - callback(new Error("Can't save a destroyed record!")); - } + if (frame = (_ref = this._actionFrames) != null ? _ref[this._actionFrames.length - 1] : void 0) { + frame.startOperation(); + } + if (options === false) { + frame.finishOperation(); 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]; + action = (frame != null ? frame.action : void 0) || this.get('action'); + if (options) { + options.into || (options.into = this.defaultRenderYield); } - 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(); + if (!options.view) { + options.viewClass || (options.viewClass = this._viewClassForAction(action)); + options.context || (options.context = this.get('_renderContext')); + options.source || (options.source = Batman.helpers.underscore(this.get('routingKey') + '/' + action)); + view = this.renderCache.viewForOptions(options); + } else { + view = options.view; + options.view = null; + } + if (view) { + if ((_ref1 = Batman.currentApp) != null) { + _ref1.prevent('ready'); + } + view.on('ready', function() { + var _ref2; + Batman.DOM.Yield.withName(options.into).replace(view.get('node')); + if ((_ref2 = Batman.currentApp) != null) { + _ref2.allowAndFire('ready'); } - return typeof callback === "function" ? callback(err, record, env) : void 0; + return frame != null ? frame.finishOperation() : void 0; }); - } else { - return typeof callback === "function" ? callback(new Batman.StateMachine.InvalidTransitionError("Can't destroy while in state " + (this.get('lifecycle.state')))) : void 0; } + return view; + }; + + Controller.prototype.scrollToHash = function(hash) { + if (hash == null) { + hash = this.get('params')['#']; + } + return Batman.DOM.scrollIntoView(hash); + }; + + Controller.prototype._resetActionFrames = function() { + return this._actionFrames = []; + }; + + Controller.prototype._viewClassForAction = function(action) { + var classPrefix, _ref; + classPrefix = this.get('routingKey').replace('/', '_'); + return ((_ref = Batman.currentApp) != null ? _ref[Batman.helpers.camelize("" + classPrefix + "_" + action + "_view")] : void 0) || Batman.View; }; - 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); + Controller.prototype._runFilters = function(action, params, filters) { + var block, options, _i, _len, _ref; + if (filters = (_ref = this.constructor._batman) != null ? _ref.get(filters) : void 0) { + for (_i = 0, _len = filters.length; _i < _len; _i++) { + options = filters[_i]; + if (options.only && __indexOf.call(options.only, action) < 0) { + continue; + } + if (options.except && __indexOf.call(options.except, action) >= 0) { + continue; + } + if (this._afterFilterRedirect) { + return; + } + block = options.block; + if (typeof block === 'function') { + block.call(this, params); + } else { + if (typeof this[block] === "function") { + this[block](params); } } } } }; - 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]; - }; + return Controller; - 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); + })(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.Set = (function(_super) { + var k, _fn, _i, _j, _len, _len1, _ref, _ref1, + _this = this; + + __extends(Set, _super); + + function Set() { + Batman.SimpleSet.apply(this, arguments); + } + + Batman.extend(Set.prototype, Batman.Enumerable); + + Set._applySetAccessors = function(klass) { + var accessor, accessors, key, _results; + accessors = { + first: function() { + return this.toArray()[0]; + }, + last: function() { + return this.toArray()[this.length - 1]; + }, + isEmpty: function() { + return this.isEmpty(); + }, + toArray: function() { + return this.toArray(); + }, + length: function() { + this.registerAsMutableSource(); + return this.length; + }, + indexedBy: function() { + var _this = this; + return new Batman.TerminalAccessible(function(key) { + return _this.indexedBy(key); + }); + }, + indexedByUnique: function() { + var _this = this; + return new Batman.TerminalAccessible(function(key) { + return _this.indexedByUnique(key); + }); + }, + sortedBy: function() { + var _this = this; + return new Batman.TerminalAccessible(function(key) { + return _this.sortedBy(key); + }); + }, + sortedByDescending: function() { + var _this = this; + return new Batman.TerminalAccessible(function(key) { + return _this.sortedBy(key, 'desc'); + }); } - return true; - } else { - return false; + }; + _results = []; + for (key in accessors) { + accessor = accessors[key]; + _results.push(klass.accessor(key, accessor)); } + return _results; }; - 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); - }); - }; + Set._applySetAccessors(Set); - Model.prototype._withoutDirtyTracking = function(block) { - var result; - this._pauseDirtyTracking = true; - result = block.call(this); - this._pauseDirtyTracking = false; - return result; - }; + _ref = ['add', 'remove', 'clear', 'replace', 'indexedBy', 'indexedByUnique', 'sortedBy', 'equality', '_indexOfItem']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + k = _ref[_i]; + Set.prototype[k] = Batman.SimpleSet.prototype[k]; + } - _ref1 = ['load', 'save', 'validate', 'destroy']; + _ref1 = ['find', 'merge', 'forEach', 'toArray', 'isEmpty', 'has']; + _fn = function(k) { + return Set.prototype[k] = function() { + this.registerAsMutableSource(); + return Batman.SimpleSet.prototype[k].apply(this, arguments); + }; + }; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - functionName = _ref1[_j]; - Model.prototype[functionName] = Batman.Property.wrapTrackingPrevention(Model.prototype[functionName]); + k = _ref1[_j]; + _fn(k); } - return Model; + Set.prototype.toJSON = Set.prototype.toArray; + + return Set; }).call(this, Batman.Object); }).call(this); (function() { - var k, _fn, _i, _len, _ref, - _this = this; + 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; }; - _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)); + Batman.ErrorsSet = (function(_super) { + + __extends(ErrorsSet, _super); + + function ErrorsSet() { + return ErrorsSet.__super__.constructor.apply(this, arguments); + } + + ErrorsSet.accessor(function(key) { + return this.indexedBy('attribute').get(key); + }); + + ErrorsSet.prototype.add = function(key, error) { + return ErrorsSet.__super__.add.call(this, new Batman.ValidationError(key, error)); }; - }; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - k = _ref[_i]; - _fn(k); - } + + return ErrorsSet; + + })(Batman.Set); }).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; }; + __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.ParamsReplacer = (function(_super) { + Batman.SetProxy = (function(_super) { + var k, _fn, _i, _len, _ref, + _this = this; - __extends(ParamsReplacer, _super); + __extends(SetProxy, _super); - function ParamsReplacer(navigator, params) { - this.navigator = navigator; - this.params = params; + function SetProxy(base) { + var _this = this; + this.base = base; + SetProxy.__super__.constructor.call(this); + this.length = this.base.length; + this.base.on('itemsWereAdded', function() { + var items; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _this.set('length', _this.base.length); + return _this.fire.apply(_this, ['itemsWereAdded'].concat(__slice.call(items))); + }); + this.base.on('itemsWereRemoved', function() { + var items; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _this.set('length', _this.base.length); + return _this.fire.apply(_this, ['itemsWereRemoved'].concat(__slice.call(items))); + }); } - ParamsReplacer.prototype.redirect = function() { - return this.navigator.replace(this.toObject()); - }; - - ParamsReplacer.prototype.replace = function(params) { - this.params.replace(params); - return this.redirect(); - }; + Batman.extend(SetProxy.prototype, Batman.Enumerable); - ParamsReplacer.prototype.update = function(params) { - this.params.update(params); - return this.redirect(); + SetProxy.prototype.filter = function(f) { + var r; + r = new Batman.Set(); + return this.reduce((function(r, e) { + if (f(e)) { + r.add(e); + } + return r; + }), r); }; - ParamsReplacer.prototype.clear = function() { - this.params.clear(); - return this.redirect(); + SetProxy.prototype.replace = function() { + var length, result; + length = this.property('length'); + length.isolate(); + result = this.base.replace.apply(this, arguments); + length.expose(); + return result; }; - ParamsReplacer.prototype.toObject = function() { - return this.params.toObject(); + Batman.Set._applySetAccessors(SetProxy); + + _ref = ['add', 'remove', 'find', 'clear', 'has', 'merge', 'toArray', 'isEmpty', 'indexedBy', 'indexedByUnique', 'sortedBy']; + _fn = function(k) { + return SetProxy.prototype[k] = function() { + var _ref1; + return (_ref1 = this.base)[k].apply(_ref1, arguments); + }; }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + k = _ref[_i]; + _fn(k); + } - ParamsReplacer.accessor({ - get: function(k) { - return this.params.get(k); - }, - set: function(k, v) { - var oldValue, result; - oldValue = this.params.get(k); - result = this.params.set(k, v); - if (oldValue !== v) { - this.redirect(); - } - return result; + SetProxy.accessor('length', { + get: function() { + this.registerAsMutableSource(); + return this.length; }, - unset: function(k) { - var hadKey, result; - hadKey = this.params.hasKey(k); - result = this.params.unset(k); - if (hadKey) { - this.redirect(); - } - return result; + set: function(_, v) { + return this.length = v; } }); - return ParamsReplacer; + return SetProxy; - })(Batman.Object); + }).call(this, 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; }; + 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.ParamsPusher = (function(_super) { + Batman.BinarySetOperation = (function(_super) { - __extends(ParamsPusher, _super); + __extends(BinarySetOperation, _super); - function ParamsPusher() { - return ParamsPusher.__super__.constructor.apply(this, arguments); + function BinarySetOperation(left, right) { + this.left = left; + this.right = right; + this._setup = __bind(this._setup, this); + + BinarySetOperation.__super__.constructor.call(this); + this._setup(this.left, this.right); + this._setup(this.right, this.left); } - ParamsPusher.prototype.redirect = function() { - return this.navigator.push(this.toObject()); + BinarySetOperation.prototype._setup = function(set, opposite) { + var _this = this; + set.on('itemsWereAdded', function() { + var items; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return _this._itemsWereAddedToSource.apply(_this, [set, opposite].concat(__slice.call(items))); + }); + set.on('itemsWereRemoved', function() { + var items; + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return _this._itemsWereRemovedFromSource.apply(_this, [set, opposite].concat(__slice.call(items))); + }); + return this._itemsWereAddedToSource.apply(this, [set, opposite].concat(__slice.call(set.toArray()))); }; - return ParamsPusher; + BinarySetOperation.prototype.merge = function() { + var merged, others, set, _i, _len; + others = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + merged = new Batman.Set; + others.unshift(this); + for (_i = 0, _len = others.length; _i < _len; _i++) { + set = others[_i]; + set.forEach(function(v) { + return merged.add(v); + }); + } + return merged; + }; - })(Batman.ParamsReplacer); + BinarySetOperation.prototype.filter = Batman.SetProxy.prototype.filter; + + return BinarySetOperation; + + })(Batman.Set); }).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.NamedRouteQuery = (function(_super) { + __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; - __extends(NamedRouteQuery, _super); + Batman.SetUnion = (function(_super) { - NamedRouteQuery.prototype.isNamedRouteQuery = true; + __extends(SetUnion, _super); - function NamedRouteQuery(routeMap, args) { - var key; - if (args == null) { - args = []; - } - NamedRouteQuery.__super__.constructor.call(this, { - routeMap: routeMap, - args: args - }); - for (key in this.get('routeMap').childrenByName) { - this[key] = this._queryAccess.bind(this, key); - } + function SetUnion() { + return SetUnion.__super__.constructor.apply(this, arguments); } - NamedRouteQuery.accessor('route', function() { - var collectionRoute, memberRoute, route, _i, _len, _ref, _ref1; - _ref = this.get('routeMap'), memberRoute = _ref.memberRoute, collectionRoute = _ref.collectionRoute; - _ref1 = [memberRoute, collectionRoute]; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - route = _ref1[_i]; - if (route != null) { - if (route.namedArguments.length === this.get('args').length) { - return route; + SetUnion.prototype._itemsWereAddedToSource = function() { + var items, opposite, source; + source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + return this.add.apply(this, items); + }; + + SetUnion.prototype._itemsWereRemovedFromSource = function() { + var item, items, itemsToRemove, opposite, source; + source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + itemsToRemove = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (!opposite.has(item)) { + _results.push(item); } } - } - return collectionRoute || memberRoute; - }); + return _results; + })(); + return this.remove.apply(this, itemsToRemove); + }; - NamedRouteQuery.accessor('path', function() { - return this.path(); - }); + return SetUnion; - NamedRouteQuery.accessor('routeMap', 'args', 'cardinality', 'hashValue', Batman.Property.defaultAccessor); + })(Batman.BinarySetOperation); - NamedRouteQuery.accessor({ - get: function(key) { - if (key == null) { - return; - } - if (typeof key === 'string') { - return this.nextQueryForName(key); - } else { - return this.nextQueryWithArgument(key); - } - }, - cache: false - }); +}).call(this); - NamedRouteQuery.accessor('withHash', function() { - var _this = this; - return new Batman.Accessible(function(hashValue) { - return _this.withHash(hashValue); - }); - }); +(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; - NamedRouteQuery.prototype.withHash = function(hashValue) { - var clone; - clone = this.clone(); - clone.set('hashValue', hashValue); - return clone; - }; + Batman.SetIntersection = (function(_super) { - NamedRouteQuery.prototype.nextQueryForName = function(key) { - var map; - if (map = this.get('routeMap').childrenByName[key]) { - return new Batman.NamedRouteQuery(map, this.args); - } else { - return Batman.developer.error("Couldn't find a route for the name " + key + "!"); - } - }; + __extends(SetIntersection, _super); - NamedRouteQuery.prototype.nextQueryWithArgument = function(arg) { - var args; - args = this.args.slice(0); - args.push(arg); - return this.clone(args); - }; + function SetIntersection() { + return SetIntersection.__super__.constructor.apply(this, arguments); + } - NamedRouteQuery.prototype.path = function() { - var argumentName, argumentValue, index, namedArguments, params, _i, _len; - params = {}; - namedArguments = this.get('route.namedArguments'); - for (index = _i = 0, _len = namedArguments.length; _i < _len; index = ++_i) { - argumentName = namedArguments[index]; - if ((argumentValue = this.get('args')[index]) != null) { - params[argumentName] = this._toParam(argumentValue); + SetIntersection.prototype._itemsWereAddedToSource = function() { + var item, items, itemsToAdd, opposite, source; + source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + itemsToAdd = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (opposite.has(item)) { + _results.push(item); + } } - } - if (this.get('hashValue') != null) { - params['#'] = this.get('hashValue'); - } - return this.get('route').pathFromParams(params); + return _results; + })(); + return this.add.apply(this, itemsToAdd); + }; + + SetIntersection.prototype._itemsWereRemovedFromSource = function() { + var items, opposite, source; + source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + return this.remove.apply(this, items); }; - NamedRouteQuery.prototype.toString = function() { - return this.path(); - }; + return SetIntersection; + + })(Batman.BinarySetOperation); + +}).call(this); - NamedRouteQuery.prototype.clone = function(args) { - if (args == null) { - args = this.args; +(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.SetComplement = (function(_super) { + + __extends(SetComplement, _super); + + function SetComplement() { + return SetComplement.__super__.constructor.apply(this, arguments); + } + + SetComplement.prototype._itemsWereAddedToSource = function() { + var item, items, itemsToAdd, itemsToRemove, opposite, source; + source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + if (source === this.left) { + itemsToAdd = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (!opposite.has(item)) { + _results.push(item); + } + } + return _results; + })(); + return this.add.apply(this, itemsToAdd); + } else { + itemsToRemove = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (opposite.has(item)) { + _results.push(item); + } + } + return _results; + })(); + return this.remove.apply(this, itemsToRemove); } - return new Batman.NamedRouteQuery(this.routeMap, args); }; - NamedRouteQuery.prototype._toParam = function(arg) { - if (arg instanceof Batman.AssociationProxy) { - arg = arg.get('target'); - } - if ((arg != null ? arg.toParam : void 0) != null) { - return arg.toParam(); + SetComplement.prototype._itemsWereRemovedFromSource = function() { + var item, items, itemsToAdd, opposite, source; + source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + if (source === this.left) { + return this.remove.apply(this, items); } else { - return arg; + itemsToAdd = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (opposite.has(item)) { + _results.push(item); + } + } + return _results; + })(); + return this.add.apply(this, itemsToAdd); } }; - NamedRouteQuery.prototype._queryAccess = function(key, arg) { - var query; - query = this.nextQueryForName(key); - if (arg != null) { - query = query.nextQueryWithArgument(arg); - } - return query; + SetComplement.prototype._addComplement = function(items, opposite) { + var item; + return this.add.apply(this, (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (opposite.has(item)) { + _results.push(item); + } + } + return _results; + })()); }; - return NamedRouteQuery; + return SetComplement; - })(Batman.Object); + })(Batman.BinarySetOperation); }).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; }; + __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.Dispatcher = (function(_super) { - var ControllerDirectory; + Batman.StateMachine = (function(_super) { - __extends(Dispatcher, _super); + __extends(StateMachine, _super); - Dispatcher.canInferRoute = function(argument) { - return argument instanceof Batman.Model || argument instanceof Batman.AssociationProxy || argument.prototype instanceof Batman.Model; + StateMachine.InvalidTransitionError = function(message) { + this.message = message != null ? message : ""; }; - Dispatcher.paramsFromArgument = function(argument) { - var resourceNameFromModel; - resourceNameFromModel = function(model) { - return Batman.helpers.camelize(Batman.helpers.pluralize(model.get('resourceName')), true); - }; - if (!this.canInferRoute(argument)) { - return argument; - } - if (argument instanceof Batman.Model || argument instanceof Batman.AssociationProxy) { - if (argument.isProxy) { - argument = argument.get('target'); + 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; } - if (argument != null) { - return { - controller: resourceNameFromModel(argument.constructor), - action: 'show', - id: argument.get('id') - }; + object = {}; + if (v.from.forEach) { + v.from.forEach(function(fromKey) { + return object[fromKey] = v.to; + }); } else { - return {}; + object[v.from] = v.to; } - } else if (argument.prototype instanceof Batman.Model) { - return { - controller: resourceNameFromModel(argument), - action: 'index' + 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; }; - } else { - return argument; + }; + _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; }; - ControllerDirectory = (function(_super1) { + function StateMachine(startState) { + this.nextEvents = []; + this.set('_state', startState); + } - __extends(ControllerDirectory, _super1); + StateMachine.accessor('state', function() { + return this.get('_state'); + }); - function ControllerDirectory() { - return ControllerDirectory.__super__.constructor.apply(this, arguments); - } + StateMachine.prototype.isTransitioning = false; - ControllerDirectory.accessor('__app', Batman.Property.defaultAccessor); + StateMachine.prototype.transitionTable = {}; - ControllerDirectory.accessor(function(key) { - return this.get("__app." + (Batman.helpers.capitalize(key)) + "Controller.sharedController"); - }); + StateMachine.prototype.onTransition = function(from, into, callback) { + return this.on("" + from + "->" + into, callback); + }; - return ControllerDirectory; + StateMachine.prototype.onEnter = function(into, callback) { + return this.on("enter " + into, callback); + }; - })(Batman.Object); + StateMachine.prototype.onExit = function(from, callback) { + return this.on("exit " + from, callback); + }; - Dispatcher.accessor('controllers', function() { - return new ControllerDirectory({ - __app: this.get('app') - }); + 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; }); - function Dispatcher(app, routeMap) { - Dispatcher.__super__.constructor.call(this, { - app: app, - routeMap: routeMap - }); - } - - Dispatcher.prototype.routeForParams = function(params) { - params = this.constructor.paramsFromArgument(params); - return this.get('routeMap').routeForParams(params); + StateMachine.prototype.canStartTransition = function(event, fromState) { + if (fromState == null) { + fromState = this.get('state'); + } + return !!this.nextStateForEvent(event, fromState); }; - Dispatcher.prototype.pathFromParams = function(params) { + StateMachine.prototype.nextStateForEvent = function(event, fromState) { var _ref; - if (typeof params === 'string') { - return params; + if (fromState == null) { + fromState = this.get('state'); } - params = this.constructor.paramsFromArgument(params); - return (_ref = this.routeForParams(params)) != null ? _ref.pathFromParams(params) : void 0; + return (_ref = this.transitionTable[event]) != null ? _ref[fromState] : void 0; }; - Dispatcher.prototype.dispatch = function(params) { - var inferredParams, path, route, _ref; - inferredParams = this.constructor.paramsFromArgument(params); - route = this.routeForParams(inferredParams); - if (route) { - _ref = route.pathAndParamsFromArgument(inferredParams), path = _ref[0], params = _ref[1]; - this.set('app.currentRoute', route); - this.set('app.currentURL', path); - this.get('app.currentParams').replace(params || {}); - route.dispatch(params); - } else { - if (Batman.typeOf(params) === 'Object' && !this.constructor.canInferRoute(params)) { - return this.get('app.currentParams').replace(params); - } else { - this.get('app.currentParams').clear(); - } - if (params !== '/404') { - return Batman.redirect('/404'); - } - } - return path; + 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 Dispatcher; + return DelegatingStateMachine; - }).call(this, Batman.Object); + })(Batman.StateMachine); }).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; }; + 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.Route = (function(_super) { + Batman.Model = (function(_super) { + var functionName, _i, _j, _len, _len1, _ref, _ref1; - __extends(Route, _super); + __extends(Model, _super); - Route.regexps = { - namedParam: /:([\w\d]+)/g, - splatParam: /\*([\w\d]+)/g, - queryParam: '(?:\\?.+)?', - namedOrSplat: /[:|\*]([\w\d]+)/g, - namePrefix: '[:|\*]', - escapeRegExp: /[-[\]{}+?.,\\^$|#\s]/g, - openOptParam: /\(/g, - closeOptParam: /\)/g - }; + Model.storageKey = null; - Route.prototype.optionKeys = ['member', 'collection']; + Model.primaryKey = 'id'; - Route.prototype.testKeys = ['controller', 'action']; + 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; + }; - Route.prototype.isRoute = true; + Model.storageAdapter = function() { + Batman.initializeObject(this.prototype); + return this.prototype._batman.storage; + }; - function Route(templatePath, baseParams) { - var k, matches, namedArguments, pattern, properties, regexp, regexps, _i, _len, _ref; - regexps = this.constructor.regexps; - if (templatePath.indexOf('/') !== 0) { - templatePath = "/" + templatePath; + 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; } - pattern = templatePath.replace(regexps.escapeRegExp, '\\$&'); - regexp = RegExp("^" + (pattern.replace(regexps.openOptParam, '(?:').replace(regexps.closeOptParam, ')?').replace(regexps.namedParam, '([^\/]+)').replace(regexps.splatParam, '(.*?)')) + regexps.queryParam + "$"); - namedArguments = ((function() { - var _results; + 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 = []; - while (matches = regexps.namedOrSplat.exec(pattern)) { - _results.push(matches[1]); + 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; - })()); - properties = { - templatePath: templatePath, - pattern: pattern, - regexp: regexp, - namedArguments: namedArguments, - baseParams: baseParams - }; - _ref = this.optionKeys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - k = _ref[_i]; - properties[k] = baseParams[k]; - delete baseParams[k]; - } - Route.__super__.constructor.call(this, properties); - } - - Route.prototype.paramsFromPath = function(pathAndQuery) { - var index, match, matches, name, namedArguments, params, uri, _i, _len; - uri = new Batman.URI(pathAndQuery); - namedArguments = this.get('namedArguments'); - params = Batman.extend({ - path: uri.path - }, this.get('baseParams')); - matches = this.get('regexp').exec(uri.path).slice(1); - for (index = _i = 0, _len = matches.length; _i < _len; index = ++_i) { - match = matches[index]; - name = namedArguments[index]; - params[name] = match; } - return Batman.extend(params, uri.queryParams); }; - Route.prototype.pathFromParams = function(argumentParams) { - var hash, key, name, newPath, params, path, query, regexp, regexps, _i, _j, _len, _len1, _ref, _ref1; - params = Batman.extend({}, argumentParams); - path = this.get('templatePath'); - regexps = this.constructor.regexps; - _ref = this.get('namedArguments'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - regexp = RegExp("" + regexps.namePrefix + name); - newPath = path.replace(regexp, (params[name] != null ? params[name] : '')); - if (newPath !== path) { - delete params[name]; - path = newPath; + 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)); } } - path = path.replace(regexps.openOptParam, '').replace(regexps.closeOptParam, '').replace(/([^\/])\/+$/, '$1'); - _ref1 = this.testKeys; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - key = _ref1[_j]; - delete params[key]; + }); + + 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); } - if (params['#']) { - hash = params['#']; - delete params['#']; + }); + + Model.classAccessor('loaded', { + get: function() { + return this._loaded || (this._loaded = new Batman.Set); + }, + set: function(k, v) { + return this._loaded = v; } - query = Batman.URI.queryFromParams(params); - if (query) { - path += "?" + query; + }); + + 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(); } - if (hash) { - path += "#" + hash; + 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 = {}; } - return path; + 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; }; - Route.prototype.test = function(pathOrParams) { - var key, path, value, _i, _len, _ref; - if (typeof pathOrParams === 'string') { - path = pathOrParams; - } else if (pathOrParams.path != null) { - path = pathOrParams.path; + Model.load = function(options, callback) { + var _ref; + if ((_ref = typeof options) === 'function' || _ref === 'undefined') { + callback = options; + options = {}; } else { - path = this.pathFromParams(pathOrParams); - _ref = this.testKeys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - key = _ref[_i]; - if ((value = this.get(key)) != null) { - if (pathOrParams[key] !== value) { - return false; + 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; } - } - return this.get('regexp').test(path); + }); }; - Route.prototype.pathAndParamsFromArgument = function(pathOrParams) { - var params, path; - if (typeof pathOrParams === 'string') { - params = this.paramsFromPath(pathOrParams); - path = pathOrParams; - } else { - params = pathOrParams; - path = this.pathFromParams(pathOrParams); + Model.create = function(attrs, callback) { + var obj, _ref; + if (!callback) { + _ref = [{}, attrs], attrs = _ref[0], callback = _ref[1]; } - return [path, params]; + obj = new this(attrs); + obj.save(callback); + return obj; }; - Route.prototype.dispatch = function(params) { - if (!this.test(params)) { - return false; + 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 this.get('callback')(params); + return record; }; - Route.prototype.callback = function() { - throw new Batman.DevelopmentError("Override callback in a Route subclass"); + Model.createFromJSON = function(json) { + var record; + record = new this; + record._withoutDirtyTracking(function() { + return this.fromJSON(json); + }); + return this._mapIdentity(record); }; - return Route; - - })(Batman.Object); - -}).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; }; - - Batman.ControllerActionRoute = (function(_super) { - - __extends(ControllerActionRoute, _super); - - ControllerActionRoute.prototype.optionKeys = ['member', 'collection', 'app', 'controller', 'action']; - - function ControllerActionRoute(templatePath, options) { - this.callback = __bind(this.callback, this); - - var action, controller, _ref; - if (options.signature) { - _ref = options.signature.split('#'), controller = _ref[0], action = _ref[1]; - action || (action = 'index'); - options.controller = controller; - options.action = action; - delete options.signature; + 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; + } } - ControllerActionRoute.__super__.constructor.call(this, templatePath, options); - } - - ControllerActionRoute.prototype.callback = function(params) { - var controller; - controller = this.get("app.dispatcher.controllers." + (this.get('controller'))); - return controller.dispatch(this.get('action'), params); }; - return ControllerActionRoute; - - })(Batman.Route); - -}).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.CallbackActionRoute = (function(_super) { - - __extends(CallbackActionRoute, _super); + 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); + }; - function CallbackActionRoute() { - return CallbackActionRoute.__super__.constructor.apply(this, arguments); + _ref = ['find', 'load', 'create']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + functionName = _ref[_i]; + Model[functionName] = Batman.Property.wrapTrackingPrevention(Model[functionName]); } - CallbackActionRoute.prototype.optionKeys = ['member', 'collection', 'callback', 'app']; - - CallbackActionRoute.prototype.controller = false; - - CallbackActionRoute.prototype.action = false; - - return CallbackActionRoute; - - })(Batman.Route); - -}).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.Hash = (function(_super) { - var k, _fn, _i, _j, _len, _len1, _ref, _ref1, - _this = this; - - __extends(Hash, _super); - - Hash.Metadata = (function(_super1) { - - __extends(Metadata, _super1); + Model.InstanceLifecycleStateMachine = (function(_super1) { - Batman.extend(Metadata.prototype, Batman.Enumerable); + __extends(InstanceLifecycleStateMachine, _super1); - function Metadata(hash) { - this.hash = hash; + function InstanceLifecycleStateMachine() { + return InstanceLifecycleStateMachine.__super__.constructor.apply(this, arguments); } - Metadata.accessor('length', function() { - this.hash.registerAsMutableSource(); - return this.hash.length; + 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' + } }); - Metadata.accessor('isEmpty', 'keys', 'toArray', function(key) { - this.hash.registerAsMutableSource(); - return this.hash[key](); - }); + return InstanceLifecycleStateMachine; - Metadata.prototype.forEach = function() { - var _ref; - return (_ref = this.hash).forEach.apply(_ref, arguments); - }; + })(Batman.DelegatingStateMachine); + + function Model(idOrAttributes) { + if (idOrAttributes == null) { + idOrAttributes = {}; + } + this.destroy = __bind(this.destroy, this); - return Metadata; + this.save = __bind(this.save, this); - })(Batman.Object); + this.loadWithOptions = __bind(this.loadWithOptions, this); - function Hash() { - this.meta = new this.constructor.Metadata(this); - Batman.SimpleHash.apply(this, arguments); - Hash.__super__.constructor.apply(this, arguments); + 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); + } } - Batman.extend(Hash.prototype, Batman.Enumerable); + Model.accessor('lifecycle', function() { + return this.lifecycle || (this.lifecycle = new Batman.Model.InstanceLifecycleStateMachine('clean', this)); + }); - Hash.prototype.propertyClass = Batman.Property; + Model.accessor('attributes', function() { + return this.attributes || (this.attributes = new Batman.Hash); + }); - Hash.defaultAccessor = { - get: Batman.SimpleHash.prototype.get, - set: Hash.mutation(function(key, value) { - var result; - result = Batman.SimpleHash.prototype.set.call(this, key, value); - this.fire('itemsWereAdded', key); - return result; - }), - unset: Hash.mutation(function(key) { - var result; - result = Batman.SimpleHash.prototype.unset.call(this, key); - if (result != null) { - this.fire('itemsWereRemoved', key); - } - return result; - }), - cache: false - }; + Model.accessor('dirtyKeys', function() { + return this.dirtyKeys || (this.dirtyKeys = new Batman.Hash); + }); - Hash.accessor(Hash.defaultAccessor); + Model.accessor('_dirtiedKeys', function() { + return this._dirtiedKeys || (this._dirtiedKeys = new Batman.SimpleSet); + }); - Hash.prototype._preventMutationEvents = function(block) { - this.prevent('change'); - this.prevent('itemsWereAdded'); - this.prevent('itemsWereRemoved'); - try { - return block.call(this); - } finally { - this.allow('change'); - this.allow('itemsWereAdded'); - this.allow('itemsWereRemoved'); - } - }; + Model.accessor('errors', function() { + return this.errors || (this.errors = new Batman.ErrorsSet); + }); - Hash.prototype.clear = Hash.mutation(function() { - var keys, result; - keys = this.keys(); - this._preventMutationEvents(function() { - var _this = this; - return this.forEach(function(k) { - return _this.unset(k); - }); - }); - result = Batman.SimpleHash.prototype.clear.call(this); - this.fire.apply(this, ['itemsWereRemoved'].concat(__slice.call(keys))); - return result; + Model.accessor('isNew', function() { + return this.isNew(); }); - Hash.prototype.update = Hash.mutation(function(object) { - var addedKeys; - addedKeys = []; - this._preventMutationEvents(function() { - var _this = this; - return Batman.forEach(object, function(k, v) { - if (!_this.hasKey(k)) { - addedKeys.push(k); - } - return _this.set(k, v); - }); - }); - if (addedKeys.length > 0) { - return this.fire.apply(this, ['itemsWereAdded'].concat(__slice.call(addedKeys))); + 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); } }); - Hash.prototype.replace = Hash.mutation(function(object) { - var addedKeys, removedKeys; - addedKeys = []; - removedKeys = []; - this._preventMutationEvents(function() { - var _this = this; - this.forEach(function(k, _) { - if (!Batman.objectHasKey(object, k)) { - _this.unset(k); - return removedKeys.push(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); } - }); - return Batman.forEach(object, function(k, v) { - if (!_this.hasKey(k)) { - addedKeys.push(k); + }, + set: function(key, value) { + var parsedValue, primaryKey; + if ((typeof value === "string") && (value.match(/[^0-9]/) === null) && (("" + (parsedValue = parseInt(value, 10))) === value)) { + value = parsedValue; } - return _this.set(k, v); - }); - }); - if (addedKeys.length > 0) { - this.fire.apply(this, ['itemsWereAdded'].concat(__slice.call(addedKeys))); - } - if (removedKeys.length > 0) { - return this.fire.apply(this, ['itemsWereRemoved'].concat(__slice.call(removedKeys))); - } + primaryKey = this.constructor.primaryKey; + if (primaryKey === 'id') { + this._willSet(key); + return core.set.apply(this, arguments); + } else { + return this.set(primaryKey, value); + } + } + }; }); - _ref = ['equality', 'hashKeyFor', 'objectKey', 'prefixedKey', 'unprefixedKey']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - k = _ref[_i]; - Hash.prototype[k] = Batman.SimpleHash.prototype[k]; - } - - _ref1 = ['hasKey', 'forEach', 'isEmpty', 'keys', 'toArray', 'merge', 'toJSON', 'toObject']; - _fn = function(k) { - return Hash.prototype[k] = function() { - this.registerAsMutableSource(); - return Batman.SimpleHash.prototype[k].apply(this, arguments); - }; + Model.prototype.isNew = function() { + return typeof this.get('id') === 'undefined'; }; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - k = _ref1[_j]; - _fn(k); - } - - return Hash; - - }).call(this, 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; }; + Model.prototype.isDirty = function() { + return this.get('lifecycle.state') === 'dirty'; + }; - Batman.RenderCache = (function(_super) { + Model.prototype.updateAttributes = function(attrs) { + this.mixin(attrs); + return this; + }; - __extends(RenderCache, _super); + Model.prototype.toString = function() { + return "" + (this.constructor.get('resourceName')) + ": " + (this.get('id')); + }; - RenderCache.prototype.maximumLength = 4; + Model.prototype.toParam = function() { + return this.get('id'); + }; - function RenderCache() { - RenderCache.__super__.constructor.apply(this, arguments); - this.keyQueue = []; - } + 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; + }; - RenderCache.prototype.viewForOptions = function(options) { - var _this = this; - if (options.cache === false || options.viewClass.prototype.cache === false) { - return this._newViewFromOptions(options); + 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); + } + }); } - return this.getOrSet(options, function() { - return _this._newViewFromOptions(Batman.extend({}, options)); + 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; }; - RenderCache.prototype._newViewFromOptions = function(options) { - return new options.viewClass(options); + 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); }; - RenderCache.wrapAccessor(function(core) { - return { - cache: false, - get: function(key) { - var result; - result = core.get.call(this, key); - if (result) { - this._addOrBubbleKey(key); - } - return result; - }, - set: function(key, value) { - var result; - result = core.set.apply(this, arguments); - result.set('cached', true); - this._addOrBubbleKey(key); - this._evictExpiredKeys(); - return result; - }, - unset: function(key) { - var result; - result = core.unset.apply(this, arguments); - result.set('cached', false); - this._removeKeyFromQueue(key); - return result; + 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!")); } - }; - }); - - RenderCache.prototype.equality = function(incomingOptions, storageOptions) { - var key; - if (Object.keys(incomingOptions).length !== Object.keys(storageOptions).length) { - return false; + return; } - for (key in incomingOptions) { - if (!(key === 'view')) { - if (incomingOptions[key] !== storageOptions[key]) { - return false; + 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; } } - return true; }; - RenderCache.prototype.reset = function() { - var key, _i, _len, _ref, _results; - _ref = this.keyQueue.slice(0); - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - key = _ref[_i]; - _results.push(this.unset(key)); + 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; } - return _results; }; - RenderCache.prototype._addOrBubbleKey = function(key) { - this._removeKeyFromQueue(key); - return this.keyQueue.unshift(key); + 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; + } }; - RenderCache.prototype._removeKeyFromQueue = function(key) { - var index, queuedKey, _i, _len, _ref; - _ref = this.keyQueue; - for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) { - queuedKey = _ref[index]; - if (this.equality(queuedKey, key)) { - this.keyQueue.splice(index, 1); - break; + 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); + } + } } } - return key; }; - RenderCache.prototype._evictExpiredKeys = function() { - var currentKeys, i, key, _i, _ref, _ref1; - if (this.length > this.maximumLength) { - currentKeys = this.keyQueue.slice(0); - for (i = _i = _ref = this.maximumLength, _ref1 = currentKeys.length; _ref <= _ref1 ? _i < _ref1 : _i > _ref1; i = _ref <= _ref1 ? ++_i : --_i) { - key = currentKeys[i]; - if (!this.get(key).isInDOM()) { - this.unset(key); - } + 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; } }; - return RenderCache; + 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; - })(Batman.Hash); + _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 __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; }, - __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; }; + 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.Controller = (function(_super) { - var _optionsFromFilterArguments; + Batman.Proxy = (function(_super) { - __extends(Controller, _super); + __extends(Proxy, _super); - Controller.singleton('sharedController'); + Proxy.prototype.isProxy = true; - Controller.wrapAccessor('routingKey', function(core) { - return { - get: function() { - if (this.routingKey != null) { - return this.routingKey; - } else { - if (Batman.config.minificationErrors) { - Batman.developer.error("Please define `routingKey` on the prototype of " + (Batman.functionName(this.constructor)) + " in order for your controller to be minification safe."); - } - return Batman.functionName(this.constructor).replace(/Controller$/, ''); - } - } - }; - }); + function Proxy(target) { + Proxy.__super__.constructor.call(this); + if (target != null) { + this.set('target', target); + } + } - Controller.accessor('_renderContext', function() { - return Batman.RenderContext.root().descend(this); - }); + Proxy.accessor('target', Batman.Property.defaultAccessor); - _optionsFromFilterArguments = function(options, nameOrFunction) { - if (!nameOrFunction) { - nameOrFunction = options; - options = {}; - } else { - if (typeof options === 'string') { - options = { - only: [options] - }; - } else { - if (options.only && Batman.typeOf(options.only) !== 'Array') { - options.only = [options.only]; - } - if (options.except && Batman.typeOf(options.except) !== 'Array') { - options.except = [options.except]; - } - } + 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; } - options.block = nameOrFunction; - return options; - }; + }); - Controller.beforeFilter = function() { - 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); - }; + return Proxy; - 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); - }; + })(Batman.Object); - Controller.afterFilter(function(params) { - if (this.autoScrollToHash && (params['#'] != null)) { - return this.scrollToHash(params['#']); - } - }); +}).call(this); - function Controller() { - this.redirect = __bind(this.redirect, this); - Controller.__super__.constructor.apply(this, arguments); - this._resetActionFrames(); - } +(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; }; - Controller.prototype.renderCache = new Batman.RenderCache; + Batman.AssociationProxy = (function(_super) { - Controller.prototype.defaultRenderYield = 'main'; + __extends(AssociationProxy, _super); - Controller.prototype.autoScrollToHash = true; + AssociationProxy.prototype.loaded = false; - Controller.prototype.dispatch = function(action, params) { - var redirectTo; - if (params == null) { - params = {}; - } - params.controller || (params.controller = this.get('routingKey')); - params.action || (params.action = action); - params.target || (params.target = this); - this._resetActionFrames(); - this.set('action', action); - this.set('params', params); - Batman.DOM.Yield.cycleAll(); - this.executeAction(action, params); - Batman.DOM.Yield.clearAllStale(); - redirectTo = this._afterFilterRedirect; - delete this._afterFilterRedirect; - if (redirectTo) { - return Batman.redirect(redirectTo); - } - }; + function AssociationProxy(association, model) { + this.association = association; + this.model = model; + AssociationProxy.__super__.constructor.call(this); + } - Controller.prototype.executeAction = function(action, params) { - var frame, oldRedirect, parentFrame, result, _ref, _ref1, - _this = this; - if (params == null) { - params = this.get('params'); - } - Batman.developer.assert(this[action], "Error! Controller action " + (this.get('routingKey')) + "." + action + " couldn't be found!"); - parentFrame = this._actionFrames[this._actionFrames.length - 1]; - frame = new Batman.ControllerActionFrame({ - parentFrame: parentFrame, - action: action - }, function() { - var _ref; - _this._runFilters(action, params, 'afterFilters'); - _this._resetActionFrames(); - return (_ref = Batman.navigator) != null ? _ref.redirect = oldRedirect : void 0; - }); - this._actionFrames.push(frame); - frame.startOperation({ - internal: true - }); - oldRedirect = (_ref = Batman.navigator) != null ? _ref.redirect : void 0; - if ((_ref1 = Batman.navigator) != null) { - _ref1.redirect = this.redirect; - } - this._runFilters(action, params, 'beforeFilters'); - result = this[action](params); - if (!frame.operationOccurred) { - this.render(); + AssociationProxy.prototype.toJSON = function() { + var target; + target = this.get('target'); + if (target != null) { + return this.get('target').toJSON(); } - frame.finishOperation(); - return result; }; - Controller.prototype.redirect = function(url) { - var frame; - frame = this._actionFrames[this._actionFrames.length - 1]; - if (frame) { - if (frame.operationOccurred) { - Batman.developer.warn("Warning! Trying to redirect but an action has already been taken during " + (this.get('routingKey')) + "." + (frame.action || this.get('action'))); - } - frame.startAndFinishOperation(); - if (this._afterFilterRedirect != null) { - return Batman.developer.warn("Warning! Multiple actions trying to redirect!"); - } else { - return this._afterFilterRedirect = url; - } - } else { - if (Batman.typeOf(url) === 'Object') { - if (!url.controller) { - url.controller = this; - } + AssociationProxy.prototype.load = function(callback) { + var _this = this; + this.fetch(function(err, proxiedRecord) { + if (!err) { + _this._setTarget(proxiedRecord); } - return Batman.redirect(url); - } + return typeof callback === "function" ? callback(err, proxiedRecord) : void 0; + }); + return this.get('target'); }; - Controller.prototype.render = function(options) { - var action, frame, view, _ref, _ref1, - _this = this; - if (options == null) { - options = {}; - } - if (frame = (_ref = this._actionFrames) != null ? _ref[this._actionFrames.length - 1] : void 0) { - frame.startOperation(); - } - if (options === false) { - frame.finishOperation(); + AssociationProxy.prototype.loadFromLocal = function() { + var target; + if (!this._canLoad()) { return; } - action = (frame != null ? frame.action : void 0) || this.get('action'); - if (options) { - options.into || (options.into = this.defaultRenderYield); - } - if (!options.view) { - options.viewClass || (options.viewClass = this._viewClassForAction(action)); - options.context || (options.context = this.get('_renderContext')); - options.source || (options.source = Batman.helpers.underscore(this.get('routingKey') + '/' + action)); - view = this.renderCache.viewForOptions(options); - } else { - view = options.view; - options.view = null; - } - if (view) { - if ((_ref1 = Batman.currentApp) != null) { - _ref1.prevent('ready'); - } - view.on('ready', function() { - var _ref2; - Batman.DOM.Yield.withName(options.into).replace(view.get('node')); - if ((_ref2 = Batman.currentApp) != null) { - _ref2.allowAndFire('ready'); - } - return frame != null ? frame.finishOperation() : void 0; - }); + if (target = this.fetchFromLocal()) { + this._setTarget(target); } - return view; + return target; }; - Controller.prototype.scrollToHash = function(hash) { - if (hash == null) { - hash = this.get('params')['#']; + 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); } - return Batman.DOM.scrollIntoView(hash); }; - Controller.prototype._resetActionFrames = function() { - return this._actionFrames = []; - }; + AssociationProxy.accessor('loaded', Batman.Property.defaultAccessor); + + AssociationProxy.accessor('target', { + get: function() { + return this.fetchFromLocal(); + }, + set: function(_, v) { + return v; + } + }); - Controller.prototype._viewClassForAction = function(action) { - var classPrefix, _ref; - classPrefix = this.get('routingKey').replace('/', '_'); - return ((_ref = Batman.currentApp) != null ? _ref[Batman.helpers.camelize("" + classPrefix + "_" + action + "_view")] : void 0) || Batman.View; + AssociationProxy.prototype._canLoad = function() { + return (this.get('foreignValue') || this.get('primaryValue')) != null; }; - Controller.prototype._runFilters = function(action, params, filters) { - var _ref, - _this = this; - if (filters = (_ref = this.constructor._batman) != null ? _ref.get(filters) : void 0) { - return filters.forEach(function(_, options) { - var block; - if (options.only && __indexOf.call(options.only, action) < 0) { - return; - } - if (options.except && __indexOf.call(options.except, action) >= 0) { - return; - } - block = options.block; - if (typeof block === 'function') { - return block.call(_this, params); - } else { - return typeof _this[block] === "function" ? _this[block](params) : void 0; - } - }); - } + AssociationProxy.prototype._setTarget = function(target) { + this.set('target', target); + this.set('loaded', true); + return this.fire('loaded', target); }; - return Controller; + return AssociationProxy; - })(Batman.Object); + })(Batman.Proxy); }).call(this); @@ -7654,95 +8232,89 @@ 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.Set = (function(_super) { - var k, _fn, _i, _j, _len, _len1, _ref, _ref1, - _this = this; + Batman.HasOneProxy = (function(_super) { - __extends(Set, _super); + __extends(HasOneProxy, _super); - function Set() { - Batman.SimpleSet.apply(this, arguments); + function HasOneProxy() { + return HasOneProxy.__super__.constructor.apply(this, arguments); } - Batman.extend(Set.prototype, Batman.Enumerable); + HasOneProxy.accessor('primaryValue', function() { + return this.model.get(this.association.primaryKey); + }); - Set._applySetAccessors = function(klass) { - var accessor, accessors, key, _results; - accessors = { - first: function() { - return this.toArray()[0]; - }, - last: function() { - return this.toArray()[this.length - 1]; - }, - isEmpty: function() { - return this.isEmpty(); - }, - toArray: function() { - return this.toArray(); - }, - length: function() { - this.registerAsMutableSource(); - return this.length; - }, - indexedBy: function() { - var _this = this; - return new Batman.TerminalAccessible(function(key) { - return _this.indexedBy(key); - }); - }, - indexedByUnique: function() { - var _this = this; - return new Batman.TerminalAccessible(function(key) { - return _this.indexedByUnique(key); - }); - }, - sortedBy: function() { - var _this = this; - return new Batman.TerminalAccessible(function(key) { - return _this.sortedBy(key); - }); - }, - sortedByDescending: function() { - var _this = this; - return new Batman.TerminalAccessible(function(key) { - return _this.sortedBy(key, 'desc'); - }); - } + HasOneProxy.prototype.fetchFromLocal = function() { + return this.association.setIndex().get(this.get('primaryValue')); + }; + + HasOneProxy.prototype.fetchFromRemote = function(callback) { + var loadOptions, + _this = this; + loadOptions = { + data: {} }; - _results = []; - for (key in accessors) { - accessor = accessors[key]; - _results.push(klass.accessor(key, accessor)); + loadOptions.data[this.association.foreignKey] = this.get('primaryValue'); + if (this.association.options.url) { + loadOptions.collectionUrl = this.association.options.url; + loadOptions.urlContext = this.model; } - return _results; + 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]); + } + }); }; - Set._applySetAccessors(Set); + return HasOneProxy; - _ref = ['add', 'remove', 'clear', 'replace', 'indexedBy', 'indexedByUnique', 'sortedBy', 'equality', '_indexOfItem']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - k = _ref[_i]; - Set.prototype[k] = Batman.SimpleSet.prototype[k]; + })(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); } - _ref1 = ['find', 'merge', 'forEach', 'toArray', 'isEmpty', 'has']; - _fn = function(k) { - return Set.prototype[k] = function() { - this.registerAsMutableSource(); - return Batman.SimpleSet.prototype[k].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')); }; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - k = _ref1[_j]; - _fn(k); - } - Set.prototype.toJSON = Set.prototype.toArray; + 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 Set; + return BelongsToProxy; - }).call(this, Batman.Object); + })(Batman.AssociationProxy); }).call(this); @@ -7750,373 +8322,424 @@ 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.ErrorsSet = (function(_super) { + Batman.PolymorphicBelongsToProxy = (function(_super) { - __extends(ErrorsSet, _super); + __extends(PolymorphicBelongsToProxy, _super); - function ErrorsSet() { - return ErrorsSet.__super__.constructor.apply(this, arguments); + function PolymorphicBelongsToProxy() { + return PolymorphicBelongsToProxy.__super__.constructor.apply(this, arguments); } - ErrorsSet.accessor(function(key) { - return this.indexedBy('attribute').get(key); + PolymorphicBelongsToProxy.accessor('foreignTypeValue', function() { + return this.model.get(this.association.foreignTypeKey); }); - ErrorsSet.prototype.add = function(key, error) { - return ErrorsSet.__super__.add.call(this, new Batman.ValidationError(key, error)); + PolymorphicBelongsToProxy.prototype.fetchFromLocal = function() { + return this.association.setIndexForType(this.get('foreignTypeValue')).get(this.get('foreignValue')); }; - return ErrorsSet; + 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); + }); + }; - })(Batman.Set); + 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; + __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.SetProxy = (function(_super) { - var k, _fn, _i, _len, _ref, - _this = this; + Batman.Accessible = (function(_super) { - __extends(SetProxy, _super); + __extends(Accessible, _super); - function SetProxy(base) { - var _this = this; - this.base = base; - SetProxy.__super__.constructor.call(this); - this.length = this.base.length; - this.base.on('itemsWereAdded', function() { - var items; - items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - _this.set('length', _this.base.length); - return _this.fire.apply(_this, ['itemsWereAdded'].concat(__slice.call(items))); - }); - this.base.on('itemsWereRemoved', function() { - var items; - items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - _this.set('length', _this.base.length); - return _this.fire.apply(_this, ['itemsWereRemoved'].concat(__slice.call(items))); - }); + function Accessible() { + this.accessor.apply(this, arguments); } - Batman.extend(SetProxy.prototype, Batman.Enumerable); + return Accessible; - SetProxy.prototype.filter = function(f) { - var r; - r = new Batman.Set(); - return this.reduce((function(r, e) { - if (f(e)) { - r.add(e); - } - return r; - }), r); - }; + })(Batman.Object); - SetProxy.prototype.replace = function() { - var length, result; - length = this.property('length'); - length.isolate(); - result = this.base.replace.apply(this, arguments); - length.expose(); - return result; - }; + Batman.TerminalAccessible = (function(_super) { - _ref = ['add', 'remove', 'find', 'clear', 'has', 'merge', 'toArray', 'isEmpty', 'indexedBy', 'indexedByUnique', 'sortedBy']; - _fn = function(k) { - return SetProxy.prototype[k] = function() { - var _ref1; - return (_ref1 = this.base)[k].apply(_ref1, arguments); - }; - }; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - k = _ref[_i]; - _fn(k); + __extends(TerminalAccessible, _super); + + function TerminalAccessible() { + return TerminalAccessible.__super__.constructor.apply(this, arguments); } - Batman.Set._applySetAccessors(SetProxy); + TerminalAccessible.prototype.propertyClass = Batman.Property; + + return TerminalAccessible; + + })(Batman.Accessible); - SetProxy.accessor('length', { - get: function() { - this.registerAsMutableSource(); - return this.length; - }, - set: function(_, v) { - return this.length = v; - } - }); +}).call(this); - return SetProxy; +(function() { - }).call(this, Batman.Object); + Batman.mixins = new Batman.Object; }).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.BinarySetOperation = (function(_super) { + Batman.URI = (function() { + /* + # URI parsing + */ - __extends(BinarySetOperation, _super); + var attributes, childKeyMatchers, decodeQueryComponent, encodeComponent, encodeQueryComponent, keyVal, nameParser, normalizeParams, plus, queryFromParams, r20, strictParser; - function BinarySetOperation(left, right) { - this.left = left; - this.right = right; - this._setup = __bind(this._setup, this); + strictParser = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/; - BinarySetOperation.__super__.constructor.call(this); - this._setup(this.left, this.right); - this._setup(this.right, this.left); + 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; } - BinarySetOperation.prototype._setup = function(set, opposite) { - var _this = this; - set.on('itemsWereAdded', function() { - var items; - items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - return _this._itemsWereAddedToSource.apply(_this, [set, opposite].concat(__slice.call(items))); - }); - set.on('itemsWereRemoved', function() { - var items; - items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - return _this._itemsWereRemovedFromSource.apply(_this, [set, opposite].concat(__slice.call(items))); - }); - return this._itemsWereAddedToSource.apply(this, [set, opposite].concat(__slice.call(set.toArray()))); + URI.prototype.queryString = function() { + return this.constructor.queryFromParams(this.queryParams); }; - BinarySetOperation.prototype.merge = function() { - var merged, others, set, _i, _len; - others = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - merged = new Batman.Set; - others.unshift(this); - for (_i = 0, _len = others.length; _i < _len; _i++) { - set = others[_i]; - set.forEach(function(v) { - return merged.add(v); - }); - } - return merged; + URI.prototype.toString = function() { + return [this.protocol ? "" + this.protocol + ":" : void 0, this.authority() ? "//" : void 0, this.authority(), this.relative()].join(""); }; - BinarySetOperation.prototype.filter = Batman.SetProxy.prototype.filter; - - return BinarySetOperation; + URI.prototype.userInfo = function() { + return [this.user, this.password ? ":" + this.password : void 0].join(""); + }; - })(Batman.Set); + URI.prototype.authority = function() { + return [this.userInfo(), this.user || this.password ? "@" : void 0, this.hostname, this.port ? ":" + this.port : void 0].join(""); + }; -}).call(this); + URI.prototype.relative = function() { + var query; + query = this.queryString(); + return [this.path, query ? "?" + query : void 0, this.hash ? "#" + this.hash : void 0].join(""); + }; -(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; + URI.prototype.directory = function() { + var splitPath; + splitPath = this.path.split('/'); + if (splitPath.length > 1) { + return splitPath.slice(0, splitPath.length - 1).join('/') + "/"; + } else { + return ""; + } + }; - Batman.SetUnion = (function(_super) { + URI.prototype.file = function() { + var splitPath; + splitPath = this.path.split("/"); + return splitPath[splitPath.length - 1]; + }; - __extends(SetUnion, _super); + /* + # query parsing + */ - function SetUnion() { - return SetUnion.__super__.constructor.apply(this, arguments); - } - SetUnion.prototype._itemsWereAddedToSource = function() { - var items, opposite, source; - source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - return this.add.apply(this, items); + 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; }; - SetUnion.prototype._itemsWereRemovedFromSource = function() { - var item, items, itemsToRemove, opposite, source; - source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - itemsToRemove = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if (!opposite.has(item)) { - _results.push(item); - } - } - return _results; - })(); - return this.remove.apply(this, itemsToRemove); + URI.decodeQueryComponent = decodeQueryComponent = function(str) { + return decodeURIComponent(str.replace(plus, '%20')); }; - return SetUnion; + nameParser = /^[\[\]]*([^\[\]]+)\]*(.*)/; - })(Batman.BinarySetOperation); + childKeyMatchers = [/^\[\]\[([^\[\]]+)\]$/, /^\[\](.+)$/]; -}).call(this); + plus = /\+/g; -(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; + r20 = /%20/g; - Batman.SetIntersection = (function(_super) { + keyVal = /^([^=]*)=(.*)/; - __extends(SetIntersection, _super); + 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; + }; - function SetIntersection() { - return SetIntersection.__super__.constructor.apply(this, arguments); - } + /* + # query building + */ - SetIntersection.prototype._itemsWereAddedToSource = function() { - var item, items, itemsToAdd, opposite, source; - source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - itemsToAdd = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if (opposite.has(item)) { - _results.push(item); + + 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); } - } - return _results; - })(); - return this.add.apply(this, itemsToAdd); + } }; - SetIntersection.prototype._itemsWereRemovedFromSource = function() { - var items, opposite, source; - source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - return this.remove.apply(this, items); + URI.encodeComponent = encodeComponent = function(str) { + if (str != null) { + return encodeURIComponent(str); + } else { + return ''; + } }; - return SetIntersection; + URI.encodeQueryComponent = encodeQueryComponent = function(str) { + return encodeComponent(str).replace(r20, '+'); + }; - })(Batman.BinarySetOperation); + 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; }, - __slice = [].slice; - - Batman.SetComplement = (function(_super) { + __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; }; - __extends(SetComplement, _super); + Batman.Request = (function(_super) { + var dataHasFileUploads; - function SetComplement() { - return SetComplement.__super__.constructor.apply(this, arguments); - } + __extends(Request, _super); - SetComplement.prototype._itemsWereAddedToSource = function() { - var item, items, itemsToAdd, itemsToRemove, opposite, source; - source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - if (source === this.left) { - itemsToAdd = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if (!opposite.has(item)) { - _results.push(item); - } - } - return _results; - })(); - return this.add.apply(this, itemsToAdd); - } else { - itemsToRemove = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if (opposite.has(item)) { - _results.push(item); - } + 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 : ""]]; } - return _results; })(); - return this.remove.apply(this, itemsToRemove); + }; + 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; }; - SetComplement.prototype._itemsWereRemovedFromSource = function() { - var item, items, itemsToAdd, opposite, source; - source = arguments[0], opposite = arguments[1], items = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - if (source === this.left) { - return this.remove.apply(this, items); - } else { - itemsToAdd = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if (opposite.has(item)) { - _results.push(item); + 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 _results; - })(); - return this.add.apply(this, itemsToAdd); } + return false; }; - SetComplement.prototype._addComplement = function(items, opposite) { - var item; - return this.add.apply(this, (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if (opposite.has(item)) { - _results.push(item); - } + 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); } - return _results; - })()); - }; - - return SetComplement; - - })(Batman.BinarySetOperation); - -}).call(this); - -(function() { - - Batman.mixins = new 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.Accessible = (function(_super) { - - __extends(Accessible, _super); - - function Accessible() { - this.accessor.apply(this, arguments); - } + }; + }); - return Accessible; + Request.prototype.method = 'GET'; - })(Batman.Object); + Request.prototype.hasFileUploads = function() { + return dataHasFileUploads(this.data); + }; - Batman.TerminalAccessible = (function(_super) { + Request.prototype.contentType = 'application/x-www-form-urlencoded'; - __extends(TerminalAccessible, _super); + Request.prototype.autosend = true; - function TerminalAccessible() { - return TerminalAccessible.__super__.constructor.apply(this, arguments); + 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(); + } + }); + } } - TerminalAccessible.prototype.propertyClass = Batman.Property; + Request.prototype.send = function() { + return Batman.developer.error("Please source a dependency file for a request implementation"); + }; - return TerminalAccessible; + return Request; - })(Batman.Accessible); + })(Batman.Object); }).call(this); @@ -8273,15 +8896,36 @@ return this.get('_storage'); }; - SetSort.prototype.forEach = function(iterator, ctx) { - var e, i, _i, _len, _ref, _results; - _ref = this.get('_storage'); - _results = []; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - e = _ref[i]; - _results.push(iterator.call(ctx, e, i, this)); - } - return _results; + SetSort.prototype.forEach = function(iterator, ctx) { + var e, i, _i, _len, _ref, _results; + _ref = this.get('_storage'); + _results = []; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + e = _ref[i]; + _results.push(iterator.call(ctx, e, i, this)); + } + 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) { @@ -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; }; @@ -8552,370 +9208,92 @@ }; SetIndex.prototype._removeItemFromKey = function(item, key) { - return this._resultSetForKey(key).remove(item); - }; - - SetIndex.prototype._resultSetForKey = function(key) { - return this._storage.getOrSet(key, function() { - return new Batman.Set; - }); - }; - - SetIndex.prototype._keyForItem = function(item) { - return Batman.Keypath.forBaseAndKey(item, this.key).getValue(); - }; - - return SetIndex; - - })(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.PolymorphicAssociationSetIndex = (function(_super) { - - __extends(PolymorphicAssociationSetIndex, _super); - - function PolymorphicAssociationSetIndex(association, type, key) { - this.association = association; - this.type = type; - PolymorphicAssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModelForType(type).get('loaded'), key); - } - - PolymorphicAssociationSetIndex.prototype._resultSetForKey = function(key) { - var _this = this; - return this._storage.getOrSet(key, function() { - return new _this.association.proxyClass(key, _this.type, _this.association); - }); - }; - - return PolymorphicAssociationSetIndex; - - })(Batman.SetIndex); - -}).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.AssociationSetIndex = (function(_super) { - - __extends(AssociationSetIndex, _super); - - function AssociationSetIndex(association, key) { - this.association = association; - AssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModel().get('loaded'), key); - } - - AssociationSetIndex.prototype._resultSetForKey = function(key) { - var _this = this; - return this._storage.getOrSet(key, function() { - return new _this.association.proxyClass(key, _this.association); - }); - }; - - AssociationSetIndex.prototype._setResultSet = function(key, set) { - return this._storage.set(key, set); - }; - - return AssociationSetIndex; - - })(Batman.SetIndex); - -}).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.UniqueSetIndex = (function(_super) { - - __extends(UniqueSetIndex, _super); - - function UniqueSetIndex() { - this._uniqueIndex = new Batman.Hash; - UniqueSetIndex.__super__.constructor.apply(this, arguments); - } - - UniqueSetIndex.accessor(function(key) { - return this._uniqueIndex.get(key); - }); - - UniqueSetIndex.prototype._addItemToKey = function(item, key) { - this._resultSetForKey(key).add(item); - if (!this._uniqueIndex.hasKey(key)) { - return this._uniqueIndex.set(key, item); - } - }; - - UniqueSetIndex.prototype._removeItemFromKey = function(item, key) { - var resultSet; - resultSet = this._resultSetForKey(key); - UniqueSetIndex.__super__._removeItemFromKey.apply(this, arguments); - if (resultSet.isEmpty()) { - return this._uniqueIndex.unset(key); - } else { - return this._uniqueIndex.set(key, resultSet.toArray()[0]); - } - }; - - return UniqueSetIndex; - - })(Batman.SetIndex); - -}).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.UniqueAssociationSetIndex = (function(_super) { - - __extends(UniqueAssociationSetIndex, _super); - - function UniqueAssociationSetIndex(association, key) { - this.association = association; - UniqueAssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModel().get('loaded'), key); - } - - return UniqueAssociationSetIndex; - - })(Batman.UniqueSetIndex); - -}).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.PolymorphicUniqueAssociationSetIndex = (function(_super) { - - __extends(PolymorphicUniqueAssociationSetIndex, _super); - - function PolymorphicUniqueAssociationSetIndex(association, type, key) { - this.association = association; - this.type = type; - PolymorphicUniqueAssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModelForType(type).get('loaded'), key); - } - - return PolymorphicUniqueAssociationSetIndex; - - })(Batman.UniqueSetIndex); - -}).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; + return this._resultSetForKey(key).remove(item); }; - URI.decodeQueryComponent = decodeQueryComponent = function(str) { - return decodeURIComponent(str.replace(plus, '%20')); + SetIndex.prototype._resultSetForKey = function(key) { + return this._storage.getOrSet(key, function() { + return new Batman.Set; + }); }; - nameParser = /^[\[\]]*([^\[\]]+)\]*(.*)/; + SetIndex.prototype._keyForItem = function(item) { + return Batman.Keypath.forBaseAndKey(item, this.key).getValue(); + }; - childKeyMatchers = [/^\[\]\[([^\[\]]+)\]$/, /^\[\](.+)$/]; + return SetIndex; - plus = /\+/g; + })(Batman.Object); - r20 = /%20/g; +}).call(this); - keyVal = /^([^=]*)=(.*)/; +(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; }; - 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; + Batman.PolymorphicAssociationSetIndex = (function(_super) { + + __extends(PolymorphicAssociationSetIndex, _super); + + function PolymorphicAssociationSetIndex(association, type, key) { + this.association = association; + this.type = type; + PolymorphicAssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModelForType(type).get('loaded'), key); + } + + PolymorphicAssociationSetIndex.prototype._resultSetForKey = function(key) { + var _this = this; + return this._storage.getOrSet(key, function() { + return new _this.association.proxyClass(key, _this.type, _this.association); + }); }; - /* - # query building - */ + return PolymorphicAssociationSetIndex; + })(Batman.SetIndex); - 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); - } - } +}).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.AssociationSetIndex = (function(_super) { + + __extends(AssociationSetIndex, _super); + + function AssociationSetIndex(association, key) { + this.association = association; + AssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModel().get('loaded'), key); + } + + AssociationSetIndex.prototype._resultSetForKey = function(key) { + return this.association.setForKey(key); }; - URI.encodeComponent = encodeComponent = function(str) { - if (str != null) { - return encodeURIComponent(str); - } else { - return ''; - } + AssociationSetIndex.prototype.forEach = function(iterator, ctx) { + var _this = this; + 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); + } + }); }; - URI.encodeQueryComponent = encodeQueryComponent = function(str) { - return encodeComponent(str).replace(r20, '+'); + AssociationSetIndex.prototype.toArray = function() { + var results; + results = []; + this.forEach(function(key) { + return results.push(key); + }); + return results; }; - return URI; + return AssociationSetIndex; - })(); + })(Batman.SetIndex); }).call(this); @@ -8923,131 +9301,79 @@ 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; + Batman.UniqueSetIndex = (function(_super) { - __extends(Request, _super); + __extends(UniqueSetIndex, _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; - }; + function UniqueSetIndex() { + this._uniqueIndex = new Batman.Hash; + UniqueSetIndex.__super__.constructor.apply(this, arguments); + } - 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; - } - } + UniqueSetIndex.accessor(function(key) { + return this._uniqueIndex.get(key); + }); + + UniqueSetIndex.prototype._addItemToKey = function(item, key) { + this._resultSetForKey(key).add(item); + if (!this._uniqueIndex.hasKey(key)) { + return this._uniqueIndex.set(key, item); } - 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); - } - }; - }); + UniqueSetIndex.prototype._removeItemFromKey = function(item, key) { + var resultSet; + resultSet = this._resultSetForKey(key); + UniqueSetIndex.__super__._removeItemFromKey.apply(this, arguments); + if (resultSet.isEmpty()) { + return this._uniqueIndex.unset(key); + } else { + return this._uniqueIndex.set(key, resultSet.toArray()[0]); + } + }; - Request.prototype.method = 'GET'; + return UniqueSetIndex; - Request.prototype.hasFileUploads = function() { - return dataHasFileUploads(this.data); - }; + })(Batman.SetIndex); - Request.prototype.contentType = 'application/x-www-form-urlencoded'; +}).call(this); - Request.prototype.autosend = true; +(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; }; - 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(); - } - }); - } + Batman.UniqueAssociationSetIndex = (function(_super) { + + __extends(UniqueAssociationSetIndex, _super); + + function UniqueAssociationSetIndex(association, key) { + this.association = association; + UniqueAssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModel().get('loaded'), key); } - Request.prototype.send = function() { - return Batman.developer.error("Please source a dependency file for a request implementation"); - }; + return UniqueAssociationSetIndex; - return Request; + })(Batman.UniqueSetIndex); - })(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.PolymorphicUniqueAssociationSetIndex = (function(_super) { + + __extends(PolymorphicUniqueAssociationSetIndex, _super); + + function PolymorphicUniqueAssociationSetIndex(association, type, key) { + this.association = association; + this.type = type; + PolymorphicUniqueAssociationSetIndex.__super__.constructor.call(this, this.association.getRelatedModelForType(type).get('loaded'), key); + } + + return PolymorphicUniqueAssociationSetIndex; + + })(Batman.UniqueSetIndex); }).call(this); @@ -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 @@ -10747,6 +11240,60 @@ }).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.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; }; @@ -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 -- cgit v1.2.3