diff --git a/vendor/assets/javascripts/batman/batman.js b/vendor/assets/javascripts/batman/batman.js index a15277e..ef8a5a2 100644 --- a/vendor/assets/javascripts/batman/batman.js +++ b/vendor/assets/javascripts/batman/batman.js @@ -1,6 +1,11 @@ (function() { - var $addEventListener, $block, $extendsEnumerable, $findName, $functionName, $get, $hasAddEventListener, $isChildOf, $mixin, $passError, $preventDefault, $redirect, $removeEventListener, $removeNode, $setInnerHTML, $typeOf, $unbindNode, $unbindTree, $unmixin, Batman, BatmanObject, Binding, Validators, buntUndefined, camelize_rx, capitalize_rx, container, developer, div, filters, helpers, isEmptyDataObject, k, mixins, t, underscore_rx1, underscore_rx2, _Batman, _i, _len, _objectToString, _ref, _stateMachine_setState; - var __slice = Array.prototype.slice, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + var $addEventListener, $appendChild, $block, $clearImmediate, $extendsEnumerable, $findName, $functionName, $get, $hasAddEventListener, $isChildOf, $mixin, $passError, $preventDefault, $redirect, $removeEventListener, $removeNode, $setImmediate, $setInnerHTML, $trackBinding, $typeOf, $unbindNode, $unbindTree, $unmixin, Batman, BatmanObject, Binding, Validators, buntUndefined, camelize_rx, capitalize_rx, container, developer, div, filters, helpers, isEmptyDataObject, k, mixins, t, underscore_rx1, underscore_rx2, _Batman, _i, _implementImmediates, _len, _objectToString, _ref, _stateMachine_setState; + var __slice = Array.prototype.slice, __hasProp = Object.prototype.hasOwnProperty, __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }, __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; @@ -133,6 +138,97 @@ } return false; }; + _implementImmediates = function() { + var $clearImmediate, $setImmediate, 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) { + $setImmediate = container.setImmediate; + $clearImmediate = container.clearImmediate; + } else if (container.msSetImmediate) { + $setImmediate = msSetImmediate; + $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); + } + $setImmediate = function(f) { + var handle; + tasks.set(handle = getHandle(), f); + container.postMessage(prefix + handle, "*"); + return handle; + }; + $clearImmediate = function(handle) { + return tasks.unset(handle); + }; + } else if (typeof document !== 'undefined' && __indexOf.call(document.createElement("script"), "onreadystatechange") >= 0) { + $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; + }; + $clearImmediate = function(handle) { + return tasks.unset(handle); + }; + } else { + $setImmediate = function(f) { + return setTimeout(f, 0); + }; + $clearImmediate = function(handle) { + return clearTimeout(handle); + }; + } + Batman.setImmediate = $setImmediate; + return Batman.clearImmediate = $clearImmediate; + }; + Batman.setImmediate = $setImmediate = function() { + _implementImmediates(); + return Batman.setImmediate.apply(this, arguments); + }; + Batman.clearImmediate = $clearImmediate = function() { + _implementImmediates(); + return Batman.clearImmediate.apply(this, arguments); + }; Batman.translate = function(x, values) { if (values == null) { values = {}; @@ -231,6 +327,7 @@ } }; Batman.developer = developer; + developer.assert((function() {}).bind, "Error! Batman needs Function.bind to work! Please shim it using something like es5-shim or augmentjs!"); camelize_rx = /(?:^|_|\-)(.)/g; capitalize_rx = /(^|\s)([a-z])/g; underscore_rx1 = /([A-Z]+)([A-Z][a-z])/g; @@ -351,7 +448,7 @@ key = this.key; return this.base._batman.ancestors(function(ancestor) { var handlers; - if (ancestor.isEventEmitter) { + if (ancestor.isEventEmitter && ancestor.hasEvent(key)) { handlers = ancestor.event(key).handlers; return handlers.forEach(iterator); } @@ -401,13 +498,17 @@ })(); Batman.EventEmitter = { isEventEmitter: true, + hasEvent: function(key) { + var _ref, _ref2; + return (_ref = this._batman) != null ? typeof _ref.get === "function" ? (_ref2 = _ref.get('events')) != null ? _ref2.hasKey(key) : void 0 : void 0 : void 0; + }, event: function(key) { var eventClass, events, existingEvent, existingEvents, newEvent, _base, _ref; Batman.initializeObject(this); eventClass = this.eventClass || Batman.Event; events = (_base = this._batman).events || (_base.events = new Batman.SimpleHash); - if (existingEvent = events.get(key)) { - return existingEvent; + if (events.hasKey(key)) { + return existingEvent = events.get(key); } else { existingEvents = this._batman.get('events'); newEvent = events.set(key, new eventClass(this, key)); @@ -538,7 +639,7 @@ if (this.base.isObservable) { return this.base._batman.ancestors(function(ancestor) { var handlers, property; - if (ancestor.isObservable) { + if (ancestor.isObservable && ancestor.hasProperty(key)) { property = ancestor.property(key); handlers = property.event('change').handlers; return handlers.forEach(iterator); @@ -549,9 +650,15 @@ Property.prototype.pushSourceTracker = function() { return Batman.Property._sourceTrackerStack.push(new Batman.SimpleSet); }; + Property.prototype.pushDummySourceTracker = function() { + return Batman.Property._sourceTrackerStack.push(null); + }; + Property.prototype.popSourceTracker = function() { + return Batman.Property._sourceTrackerStack.pop(); + }; Property.prototype.updateSourcesFromTracker = function() { var handler, newSources; - newSources = Batman.Property._sourceTrackerStack.pop(); + newSources = this.popSourceTracker(); handler = this.sourceChangeHandler(); this._eachSourceChangeEvent(function(e) { return e.removeHandler(handler); @@ -613,15 +720,19 @@ }; Property.prototype.setValue = function(val) { var result, _ref; + this.pushDummySourceTracker(); this.cached = false; result = (_ref = this.accessor().set) != null ? _ref.call(this.base, this.key, val) : void 0; this.refresh(); + this.popSourceTracker(); return result; }; Property.prototype.unsetValue = function() { var result, _ref; + this.pushDummySourceTracker(); result = (_ref = this.accessor().unset) != null ? _ref.call(this.base, this.key) : void 0; this.refresh(); + this.popSourceTracker(); return result; }; Property.prototype.forget = function(handler) { @@ -640,6 +751,14 @@ this.getValue(); return this; }; + Property.prototype.removeSourceHandlers = function() { + var handler; + handler = this.sourceChangeHandler(); + this._eachSourceChangeEvent(function(e) { + return e.removeHandler(handler); + }); + return this.forget(); + }; Property.prototype.fire = function() { var _ref; return (_ref = this.changeEvent()).fire.apply(_ref, arguments); @@ -727,6 +846,10 @@ })(); Batman.Observable = { isObservable: true, + hasProperty: function(key) { + var _ref, _ref2; + return (_ref = this._batman) != null ? (_ref2 = _ref.properties) != null ? typeof _ref2.hasKey === "function" ? _ref2.hasKey(key) : void 0 : void 0 : void 0; + }, property: function(key) { var properties, propertyClass, _base; Batman.initializeObject(this); @@ -914,7 +1037,7 @@ for (key in this) { if (!__hasProp.call(this, key)) continue; value = this[key]; - if (key !== "_batman") { + if (key !== "_batman" && key !== "hashKey" && key !== "_objectID") { obj[key] = value.toJSON ? value.toJSON() : value; } } @@ -1121,12 +1244,16 @@ return val; }; SimpleHash.prototype.unset = function(key) { - var index, obj, pair, pairs, value, _len, _ref; - if (pairs = this._storage[this.hashKeyFor(key)]) { + var hashKey, index, obj, pair, pairs, value, _len, _ref; + hashKey = this.hashKeyFor(key); + if (pairs = this._storage[hashKey]) { for (index = 0, _len = pairs.length; index < _len; index++) { _ref = pairs[index], obj = _ref[0], value = _ref[1]; if (this.equality(obj, key)) { pair = pairs.splice(index, 1); + if (!pairs.length) { + delete this._storage[hashKey]; + } this.length--; return pair[0][1]; } @@ -1425,8 +1552,8 @@ __extends(SetObserver, Batman.Object); function SetObserver(base) { this.base = base; - this._itemObservers = new Batman.Hash; - this._setObservers = new Batman.Hash; + this._itemObservers = new Batman.SimpleHash; + this._setObservers = new Batman.SimpleHash; this._setObservers.set("itemsWereAdded", __bind(function() { return this.fire.apply(this, ['itemsWereAdded'].concat(__slice.call(arguments))); }, this)); @@ -1441,7 +1568,7 @@ SetObserver.prototype._getOrSetObserverForItemAndKey = function(item, key) { return this._itemObservers.getOrSet(item, __bind(function() { var observersByKey; - observersByKey = new Batman.Hash; + observersByKey = new Batman.SimpleHash; return observersByKey.getOrSet(key, __bind(function() { return this.observerForItemAndKey(item, key); }, this)); @@ -1679,7 +1806,7 @@ this.base = base; this.key = key; SetIndex.__super__.constructor.call(this); - this._storage = new Batman.Hash; + this._storage = new Batman.SimpleHash; if (this.base.isEventEmitter) { this._setObserver = new Batman.SetObserver(this.base); this._setObserver.observedItemKeys = [this.key]; @@ -1735,7 +1862,12 @@ return this._removeItemFromKey(item, this._keyForItem(item)); }; SetIndex.prototype._removeItemFromKey = function(item, key) { - return this._resultSetForKey(key).remove(item); + var results; + results = this._resultSetForKey(key); + results.remove(item); + if (results.isEmpty()) { + return this._storage.unset(key); + } }; SetIndex.prototype._resultSetForKey = function(key) { return this._storage.getOrSet(key, function() { @@ -1765,8 +1897,8 @@ UniqueSetIndex.prototype._removeItemFromKey = function(item, key) { var resultSet; resultSet = this._resultSetForKey(key); - resultSet.remove(item); - if (resultSet.length === 0) { + UniqueSetIndex.__super__._removeItemFromKey.apply(this, arguments); + if (resultSet.isEmpty()) { return this._uniqueIndex.unset(key); } else { return this._uniqueIndex.set(key, resultSet.toArray()[0]); @@ -1915,9 +2047,9 @@ } } Request.observeAll('url', function() { - return this._autosendTimeout = setTimeout((__bind(function() { + return this._autosendTimeout = $setImmediate(__bind(function() { return this.send(); - }, this)), 0); + }, this)); }); Request.prototype.send = function() { return developer.error("Please source a dependency file for a request implementation"); @@ -1935,8 +2067,8 @@ App.__super__.constructor.apply(this, arguments); } App.requirePath = ''; - developer["do"](function() { - return App.require = function() { + developer["do"](__bind(function() { + App.require = function() { var base, name, names, path, _i, _len; path = arguments[0], names = 2 <= arguments.length ? __slice.call(arguments, 1) : []; base = this.requirePath + path; @@ -1961,22 +2093,23 @@ } return this; }; - }); - App.controller = function() { - var names; - names = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - names = names.map(function(n) { - return n + '_controller'; - }); - return this.require.apply(this, ['controllers'].concat(__slice.call(names))); - }; - App.model = function() { - return this.require.apply(this, ['models'].concat(__slice.call(arguments))); - }; - App.view = function() { - return this.require.apply(this, ['views'].concat(__slice.call(arguments))); - }; + this.controller = function() { + var names; + names = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + names = names.map(function(n) { + return n + '_controller'; + }); + return this.require.apply(this, ['controllers'].concat(__slice.call(names))); + }; + this.model = function() { + return this.require.apply(this, ['models'].concat(__slice.call(arguments))); + }; + return this.view = function() { + return this.require.apply(this, ['views'].concat(__slice.call(arguments))); + }; + }, App)); App.layout = void 0; + App.event('ready').oneShot = true; App.event('run').oneShot = true; App.run = function() { if (Batman.currentApp) { @@ -1998,14 +2131,18 @@ if (typeof this.dispatcher === 'undefined') { this.dispatcher || (this.dispatcher = new Batman.Dispatcher(this)); } + this.observe('layout', __bind(function(layout) { + return layout != null ? layout.on('ready', __bind(function() { + return this.fire('ready'); + }, this)) : void 0; + }, this)); if (typeof this.layout === 'undefined') { this.set('layout', new Batman.View({ contexts: [this], node: document })); - this.get('layout').on('ready', __bind(function() { - return this.fire('ready'); - }, this)); + } else if (typeof this.layout === 'string') { + this.set('layout', new this[helpers.camelize(this.layout) + 'View']); } if (typeof this.historyManager === 'undefined' && this.dispatcher.routeMap) { this.on('run', __bind(function() { @@ -2030,7 +2167,7 @@ return this; }; return App; - })(); + }).call(this); Batman.Route = (function() { var escapeRegExp, namedOrSplat, namedParam, queryParam, splatParam; __extends(Route, Batman.Object); @@ -2181,7 +2318,7 @@ } }; Dispatcher.prototype.findUrl = function(params) { - var action, controller, key, matches, options, route, url, value, _ref, _ref2; + var action, controller, key, matches, options, paramsCopy, queryString, regex, route, url, value, _ref, _ref2; _ref = this.routeMap; for (url in _ref) { route = _ref[url]; @@ -2192,21 +2329,43 @@ } else { action = route.get('action'); if (typeof action === 'function') { - continue; - } - _ref2 = action, controller = _ref2.controller, action = _ref2.action; - if (controller === params.controller && action === (params.action || 'index')) { matches = true; + } else { + _ref2 = action, controller = _ref2.controller, action = _ref2.action; + if (controller === params.controller && action === (params.action || 'index')) { + matches = true; + } } } if (!matches) { continue; } + $mixin(paramsCopy = {}, params); + $unmixin(paramsCopy, { + controller: null, + action: null, + resource: null, + url: null, + signature: null, + target: null + }); for (key in params) { value = params[key]; - url = url.replace(new RegExp('[:|\*]' + key), value); + regex = new RegExp('[:|\*]' + key); + if (!regex.test(url)) { + continue; + } + url = url.replace(regex, value); + paramsCopy[key] = null; + delete paramsCopy[key]; + } + queryString = ''; + for (key in paramsCopy) { + value = paramsCopy[key]; + queryString += !queryString ? '?' : '&'; + queryString += key + '=' + value; } - return url; + return url + queryString; } }; Dispatcher.prototype.dispatch = function(url) { @@ -2265,7 +2424,7 @@ } this.first = true; Batman.currentApp.prevent('ready'); - return setTimeout(this.parseHash, 0); + return $setImmediate(this.parseHash); }; HashHistory.prototype.stop = function() { if (this.interval) { @@ -2347,6 +2506,9 @@ } resource = helpers.pluralize(resource); controller = options.controller || resource; + if (options.parentResource) { + resource = "" + options.parentResource + "/:" + (helpers.singularize(options.parentResource)) + "Id/" + resource; + } if (options.index !== false) { this.route(resource, "" + controller + "#index", { resource: controller, @@ -2387,7 +2549,18 @@ return app.route("" + resource + "/:id/" + url, "" + controller + "#" + (methodName || url)); } }) : void 0; - } + }, + resources: __bind(function(childResource, options, callback) { + if (options == null) { + options = {}; + } + if (typeof options === 'function') { + callback = options; + options = {}; + } + options.parentResource = resource; + return this.resources(childResource, options, callback); + }, this) }; return callback.call(ops); } @@ -2509,6 +2682,8 @@ return; } if (!options.view) { + options.contexts || (options.contexts = []); + options.contexts.push(this); options.source || (options.source = helpers.underscore($functionName(this.constructor).replace('Controller', '')) + '/' + this._currentAction + '.html'); options.view = new Batman.View(options); } @@ -2516,10 +2691,9 @@ if ((_ref = Batman.currentApp) != null) { _ref.prevent('ready'); } - view.contexts.push(this); view.on('ready', function() { var _ref2, _ref3; - Batman.DOM.replace('main', view.get('node')); + Batman.DOM.replace(options.into || 'main', view.get('node')); if ((_ref2 = Batman.currentApp) != null) { _ref2.allow('ready'); } @@ -2737,7 +2911,7 @@ Model.accessor('id', { get: function() { var pk; - pk = this.constructor.get('primaryKey'); + pk = this.constructor.primaryKey; if (pk === 'id') { return this.id; } else { @@ -2749,7 +2923,7 @@ if (typeof v === "string" && !isNaN(intId = parseInt(v, 10))) { v = intId; } - pk = this.constructor.get('primaryKey'); + pk = this.constructor.primaryKey; if (pk === 'id') { return this.id = v; } else { @@ -2856,6 +3030,13 @@ } }); } + developer["do"](function() { + if ((!decoders) || decoders.reduce((function(sum, x) { + return sum + x; + }), 0) <= 1) { + return developer.warn("Warning: Model " + (this.get('storageKey')) + " has suspiciously few decoders!"); + } + }); return this.mixin(obj); }; Model.actsAsStateMachine(true); @@ -3379,6 +3560,7 @@ RestStorage.prototype.serializeAsForm = true; function RestStorage() { RestStorage.__super__.constructor.apply(this, arguments); + this.defaultOptions = $mixin({}, this.defaultOptions); this.recordJsonNamespace = helpers.singularize(this.modelKey); this.collectionJsonNamespace = helpers.pluralize(this.modelKey); } @@ -3567,63 +3749,93 @@ }; return RestStorage; })(); + Batman.ViewSourceCache = (function() { + __extends(ViewSourceCache, Batman.Object); + function ViewSourceCache() { + ViewSourceCache.__super__.constructor.apply(this, arguments); + this.sources = {}; + this.requests = {}; + } + ViewSourceCache.prototype.propertyClass = Batman.Property; + ViewSourceCache.accessor({ + get: function(k) { + var url; + if (this.sources[k] != null) { + return this.sources[k]; + } + if (this.requests[k] == null) { + this.requests = new Batman.Request({ + url: url = "" + Batman.View.prototype.prefix + "/" + k, + type: 'html', + success: __bind(function(response) { + return this.set(k, response); + }, this), + error: function(response) { + throw new Error("Could not load view from " + url); + } + }); + } + }, + set: function(k, v) { + return this.sources[k] = v; + } + }); + return ViewSourceCache; + })(); Batman.View = (function() { __extends(View, Batman.Object); function View(options) { - var context; + var context, node; this.contexts = []; View.__super__.constructor.call(this, options); if (context = this.get('context')) { this.contexts.push(context); this.unset('context'); } + if (node = this.get('node')) { + this.render(node); + } else { + this.observe('node', __bind(function(node) { + return this.render(node); + }, this)); + } } - View.viewSources = {}; + View.sourceCache = new Batman.ViewSourceCache(); View.prototype.source = ''; View.prototype.html = ''; View.prototype.node = null; View.prototype.contentFor = null; View.prototype.event('ready').oneShot = true; View.prototype.prefix = 'views'; - View.observeAll('source', function() { - return setTimeout((__bind(function() { - return this.reloadSource(); - }, this)), 0); - }); - View.prototype.reloadSource = function() { - var source, url; + View.accessor('html', function() { + var source; + if (this.html && this.html.length > 0) { + return this.html; + } source = this.get('source'); if (!source) { return; } - if (Batman.View.viewSources[source]) { - return this.set('html', Batman.View.viewSources[source]); - } else { - return new Batman.Request({ - url: url = "" + this.prefix + "/" + this.source, - type: 'html', - success: __bind(function(response) { - Batman.View.viewSources[source] = response; - return this.set('html', response); - }, this), - error: function(response) { - throw new Error("Could not load view from " + url); + return this.html = this.constructor.sourceCache.get(source); + }); + View.accessor('node', { + get: function() { + var html; + if (!this.node) { + html = this.get('html'); + if (!(html && html.length > 0)) { + return; } - }); - } - }; - View.observeAll('html', function(html) { - var node; - node = this.node || document.createElement('div'); - $setInnerHTML(node, html); - if (this.node !== node) { - return this.set('node', node); + this.node = document.createElement('div'); + $setInnerHTML(this.node, html); + } + return this.node; + }, + set: function(_, node) { + return this.node = node; } }); - View.observeAll('node', function(node) { - if (!node) { - return; - } + View.prototype.render = function(node) { this.event('ready').resetOneShot(); if (this._renderer) { this._renderer.forgetAll(); @@ -3637,7 +3849,7 @@ } if (this.contentFor && node) { $setInnerHTML(this.contentFor, ''); - return this.contentFor.appendChild(node); + return $appendChild(this.contentFor, node); } else if (yieldTo) { if (contents = Batman.DOM._yieldContents[yieldTo]) { return contents.push(node); @@ -3650,12 +3862,13 @@ return this.fire('ready', node); }, this)); } - }); + }; return View; })(); Batman.Renderer = (function() { var bindingRegexp, k, sortBindings, _i, _len, _ref; __extends(Renderer, Batman.Object); + Renderer.prototype.deferEvery = 50; function Renderer(node, callback, contexts) { var _ref; this.node = node; @@ -3669,7 +3882,7 @@ this.on('parsed', callback); } this.context = contexts instanceof Batman.RenderContext ? contexts : (_ref = Batman.RenderContext).start.apply(_ref, contexts); - this.timeout = setTimeout(this.start, 0); + this.timeout = $setImmediate(this.start); } Renderer.prototype.start = function() { this.startTime = new Date; @@ -3714,9 +3927,9 @@ }; Renderer.prototype.parseNode = function(node) { var attr, bindings, key, name, nextNode, readerArgs, result, skipChildren, varIndex, _base, _base2, _j, _len2, _name, _name2, _ref2; - if (new Date - this.startTime > 50) { + if (this.deferEvery && (new Date - this.startTime) > this.deferEvery) { this.resumeNode = node; - this.timeout = setTimeout(this.resume, 0); + this.timeout = $setImmediate(this.resume); return; } if (node.getAttribute && node.attributes) { @@ -3871,6 +4084,19 @@ } this; } + Binding.prototype.destroy = function() { + var k, _results; + this.forget(); + this._batman.properties.forEach(function(key, property) { + return property.removeSourceHandlers(); + }); + _results = []; + for (k in this) { + if (!__hasProp.call(this, k)) continue; + _results.push(delete this[k]); + } + return _results; + }; Binding.prototype.parseFilter = function() { var args, filter, filterName, filterString, filters, key, keyPath, orig, split; this.filterFunctions = []; @@ -4159,21 +4385,21 @@ return true; }, yield: function(node, key) { - setTimeout((function() { + $setImmediate(function() { return Batman.DOM.yield(key, node); - }), 0); + }); return true; }, contentfor: function(node, key) { - setTimeout((function() { + $setImmediate(function() { return Batman.DOM.contentFor(key, node); - }), 0); + }); return true; }, replace: function(node, key) { - setTimeout((function() { + $setImmediate(function() { return Batman.DOM.replace(key, node); - }), 0); + }); return true; } }, @@ -4315,75 +4541,7 @@ }, binders: { select: function(node, key, context, renderer, only) { - var boundValue, container, updateOptionBindings, updateSelectBinding, _ref; - _ref = context.findKey(key), boundValue = _ref[0], container = _ref[1]; - updateSelectBinding = __bind(function() { - var c, selections; - selections = node.multiple ? (function() { - var _i, _len, _ref2, _results; - _ref2 = node.children; - _results = []; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - c = _ref2[_i]; - if (c.selected) { - _results.push(c.value); - } - } - return _results; - })() : node.value; - if (selections.length === 1) { - selections = selections[0]; - } - return container.set(key, selections); - }, this); - updateOptionBindings = __bind(function() { - var child, data, subBoundValue, subContainer, subContext, subKey, _i, _len, _ref2, _ref3, _results; - _ref2 = node.children; - _results = []; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - child = _ref2[_i]; - _results.push((data = Batman.data(child, 'selected')) ? (subContext = data.context) && (subKey = data.key) ? ((_ref3 = subContext.findKey(subKey), subBoundValue = _ref3[0], subContainer = _ref3[1], _ref3), child.selected !== subBoundValue ? subContainer.set(subKey, child.selected) : void 0) : void 0 : void 0); - } - return _results; - }, this); - renderer.on('rendered', function() { - var dataChange, nodeChange; - dataChange = function(newValue) { - var child, match, matches, value, valueToChild, _i, _j, _k, _len, _len2, _len3, _ref2, _ref3; - if (newValue instanceof Array) { - valueToChild = {}; - _ref2 = node.children; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - child = _ref2[_i]; - child.selected = false; - matches = valueToChild[child.value]; - if (matches) { - matches.push(child); - } else { - matches = [child]; - } - valueToChild[child.value] = matches; - } - for (_j = 0, _len2 = newValue.length; _j < _len2; _j++) { - value = newValue[_j]; - _ref3 = valueToChild[value]; - for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { - match = _ref3[_k]; - match.selected = true; - } - } - } else { - node.value = newValue; - } - return updateOptionBindings(); - }; - nodeChange = function() { - updateSelectBinding(); - return updateOptionBindings(); - }; - Batman.data(node, 'updateBinding', updateSelectBinding); - return context.bind(node, key, dataChange, nodeChange, only); - }); + new Batman.DOM.Select(node, key, context, renderer, only); return true; }, style: function(node, attr, key, context, renderer, only) { @@ -4408,14 +4566,18 @@ return true; }, file: function(node, key, context, renderer, only) { - context.bind(node, key, function() { - return developer.warn("Can't write to file inputs! Tried to on key " + key + "."); - }, function(node, subContext) { - var actualObject, adapter, _i, _len, _ref; - if (subContext instanceof Batman.RenderContext.ContextProxy) { - actualObject = subContext.get('proxiedObject'); + context.bind(node, key, (function() {}), function(node, subContext) { + var actualObject, adapter, keyContext, segments, _i, _len, _ref; + segments = key.split('.'); + if (segments.length > 1) { + keyContext = subContext.get(segments.slice(0, -1).join('.')); } else { - actualObject = subContext; + keyContext = subContext; + } + if (keyContext instanceof Batman.RenderContext.ContextProxy) { + actualObject = keyContext.get('proxiedObject'); + } else { + actualObject = keyContext; } if (actualObject.hasStorage && actualObject.hasStorage()) { _ref = actualObject._batman.get('storage'); @@ -4519,13 +4681,15 @@ Batman.DOM._yields[name] = node; if (contents = Batman.DOM._yieldContents[name]) { if (_replaceContent) { - $setInnerHTML(node, ''); + $setInnerHTML(node, '', true); } for (_i = 0, _len = contents.length; _i < _len; _i++) { content = contents[_i]; if (!Batman.data(content, 'yielded')) { - content = $isChildOf(node, content) ? content.cloneNode(true) : content; - node.appendChild(content); + if ($isChildOf(node, content)) { + content = content.cloneNode(true); + } + $appendChild(node, content, true); Batman.data(content, 'yielded', true); } } @@ -4548,8 +4712,21 @@ replace: function(name, node) { return Batman.DOM.contentFor(name, node, true); }, + trackBinding: $trackBinding = function(binding, node) { + var bindings; + if (bindings = Batman.data(node, 'bindings')) { + return bindings.add(binding); + } else { + return Batman.data(node, 'bindings', new Batman.SimpleSet(binding)); + } + }, unbindNode: $unbindNode = function(node) { - var eventListeners, eventName, listeners; + var bindings, eventListeners, eventName, listeners; + if (bindings = Batman.data(node, 'bindings')) { + bindings.forEach(function(binding) { + return binding.destroy(); + }); + } if (listeners = Batman.data(node, 'listeners')) { for (eventName in listeners) { eventListeners = listeners[eventName]; @@ -4579,7 +4756,16 @@ } return _results; }, - setInnerHTML: $setInnerHTML = function(node, html) { + setInnerHTML: $setInnerHTML = function() { + var args, child, hide, html, node, _i, _len, _ref; + node = arguments[0], html = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + _ref = node.childNodes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + if (hide = Batman.data(child, 'hide')) { + hide.apply(child, args); + } + } $unbindTree(node, false); return node != null ? node.innerHTML = html : void 0; }, @@ -4588,6 +4774,14 @@ $unbindTree(node); return node != null ? (_ref = node.parentNode) != null ? _ref.removeChild(node) : void 0 : void 0; }, + appendChild: $appendChild = function() { + var args, child, parent, _ref; + parent = arguments[0], child = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + if ((_ref = Batman.data(child, 'show')) != null) { + _ref.apply(child, args); + } + return parent.appendChild(child); + }, valueForNode: function(node, value) { var isSetting; if (value == null) { @@ -4653,26 +4847,131 @@ }, hasAddEventListener: $hasAddEventListener = !!(typeof window !== "undefined" && window !== null ? window.addEventListener : void 0) }; + Batman.DOM.AbstractBinding = (function() { + function AbstractBinding(node) { + Batman.DOM.trackBinding(this, node); + } + AbstractBinding.prototype.destroy = function() {}; + return AbstractBinding; + })(); + Batman.DOM.Select = (function() { + __extends(Select, Batman.DOM.AbstractBinding); + function Select(node, key, context, renderer, only) { + this.node = node; + this.key = key; + this.context = context; + this.renderer = renderer; + this.only = only; + this.updateOptionBindings = __bind(this.updateOptionBindings, this); + this.updateSelectBinding = __bind(this.updateSelectBinding, this); + this.nodeChange = __bind(this.nodeChange, this); + this.dataChange = __bind(this.dataChange, this); + Select.__super__.constructor.apply(this, arguments); + this.container = context.findKey(key)[1]; + renderer.on('rendered', __bind(function() { + Batman.data(node, 'updateBinding', this.updateSelectBinding); + return context.bind(node, key, this.dataChange, this.nodeChange, only); + }, this)); + } + Select.prototype.dataChange = function(newValue) { + var child, match, matches, value, valueToChild, _i, _j, _k, _len, _len2, _len3, _ref, _ref2; + if (newValue instanceof Array) { + valueToChild = {}; + _ref = this.node.children; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + child.selected = false; + matches = valueToChild[child.value]; + if (matches) { + matches.push(child); + } else { + matches = [child]; + } + valueToChild[child.value] = matches; + } + for (_j = 0, _len2 = newValue.length; _j < _len2; _j++) { + value = newValue[_j]; + _ref2 = valueToChild[value]; + for (_k = 0, _len3 = _ref2.length; _k < _len3; _k++) { + match = _ref2[_k]; + match.selected = true; + } + } + } else { + this.node.value = newValue; + } + return this.updateOptionBindings(); + }; + Select.prototype.nodeChange = function() { + this.updateSelectBinding(); + return this.updateOptionBindings(); + }; + Select.prototype.updateSelectBinding = function() { + var c, selections; + selections = this.node.multiple ? (function() { + var _i, _len, _ref, _results; + _ref = this.node.children; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + c = _ref[_i]; + if (c.selected) { + _results.push(c.value); + } + } + return _results; + }).call(this) : this.node.value; + if (selections.length === 1) { + selections = selections[0]; + } + return this.container.set(this.key, selections); + }; + Select.prototype.updateOptionBindings = function() { + var child, data, subBoundValue, subContainer, subContext, subKey, _i, _len, _ref, _ref2, _results; + _ref = this.node.children; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + _results.push((data = Batman.data(child, 'selected')) ? (subContext = data.context) && (subKey = data.key) ? ((_ref2 = subContext.findKey(subKey), subBoundValue = _ref2[0], subContainer = _ref2[1], _ref2), child.selected !== subBoundValue ? subContainer.set(subKey, child.selected) : void 0) : void 0 : void 0); + } + return _results; + }; + return Select; + })(); Batman.DOM.Style = (function() { + __extends(Style, Batman.DOM.AbstractBinding); function Style(node, key, context) { this.node = node; this.key = key; this.context = context; + this.unbindCurrentHash = __bind(this.unbindCurrentHash, this); this.reapplyOldStyles = __bind(this.reapplyOldStyles, this); this.setStyle = __bind(this.setStyle, this); this.bindSingleAttribute = __bind(this.bindSingleAttribute, this); this.onItemsRemoved = __bind(this.onItemsRemoved, this); this.onItemsAdded = __bind(this.onItemsAdded, this); this.dataChange = __bind(this.dataChange, this); + this.destroy = __bind(this.destroy, this); + Style.__super__.constructor.apply(this, arguments); this.oldStyles = {}; context.bind(node, key, this.dataChange, function() {}); } + Style.prototype.destroy = function() { + var k, _results; + this.unbindCurrentHash(); + _results = []; + for (k in this) { + if (!__hasProp.call(this, k)) continue; + _results.push(delete this[k]); + } + return _results; + }; Style.prototype.dataChange = function(value) { var cssName, cssValue, key, keyValue, keypathContext, keypathValue, style, _i, _len, _ref, _ref2, _ref3, _results; if (!value) { this.reapplyOldStyles(); return; } + this.unbindCurrentHash(); if (typeof value === 'string' && (this.boundValueType = 'string')) { this.reapplyOldStyles(); _ref = value.split(';'); @@ -4684,10 +4983,6 @@ return; } if (value instanceof Batman.Hash && (this.boundValueType = 'batman.hash')) { - if (this.styleHash) { - this.styleHash.event('itemsWereRemoved').removeHandler(this.onItemsRemoved); - this.styleHash.event('itemsWereAdded').removeHandler(this.onItemsAdded); - } this.styleHash = value; value.on('itemsWereAdded', this.onItemsAdded); value.on('itemsWereRemoved', this.onItemsRemoved); @@ -4738,11 +5033,19 @@ } return _results; }; + Style.prototype.unbindCurrentHash = function() { + if (this.styleHash) { + this.styleHash.event('itemsWereRemoved').removeHandler(this.onItemsRemoved); + return this.styleHash.event('itemsWereAdded').removeHandler(this.onItemsAdded); + } + }; return Style; })(); Batman.DOM.Iterator = (function() { - Iterator.prototype.currentAddNumber = 0; - Iterator.prototype.queuedAddNumber = 0; + __extends(Iterator, Batman.DOM.AbstractBinding); + Iterator.prototype.deferEvery = 50; + Iterator.prototype.currentActionNumber = 0; + Iterator.prototype.queuedActionNumber = 0; function Iterator(sourceNode, iteratorName, key, context, parentRenderer) { this.iteratorName = iteratorName; this.key = key; @@ -4751,24 +5054,35 @@ this.arrayChanged = __bind(this.arrayChanged, this); this.collectionChange = __bind(this.collectionChange, this); this.nodeMap = new Batman.SimpleHash; + this.actionMap = new Batman.SimpleHash; this.rendererMap = new Batman.SimpleHash; + this.actions = []; this.prototypeNode = sourceNode.cloneNode(true); this.prototypeNode.removeAttribute("data-foreach-" + iteratorName); this.parentNode = sourceNode.parentNode; this.siblingNode = sourceNode.nextSibling; - this.parentRenderer.on('parsed', function() { + this.parentRenderer.on('parsed', __bind(function() { + this.prototypeNode[Batman.expando] = sourceNode[Batman.expando]; + delete sourceNode[Batman.expando]; return $removeNode(sourceNode); - }); - this.addFunctions = []; + }, this)); + this.parentRenderer.prevent('rendered'); + Iterator.__super__.constructor.call(this, this.parentNode); this.fragment = document.createDocumentFragment(); context.bind(sourceNode, key, this.collectionChange, function() {}); } - Iterator.prototype.collectionChange = function(newCollection) { - var key, value, _ref, _results; + Iterator.prototype.destroy = function() { + var k; + $unbindNode(this.prototypeNode); + this.unbindCollection(); + for (k in this) { + if (!__hasProp.call(this, k)) continue; + delete this[k]; + } + return this.destroyed = true; + }; + Iterator.prototype.unbindCollection = function() { if (this.collection) { - if (newCollection === this.collection) { - return; - } this.nodeMap.forEach(function(item, node) { return $removeNode(node); }); @@ -4777,13 +5091,19 @@ return renderer.stop(); }); this.rendererMap.clear(); - if (this.collection.isObservble && this.collection.toArray) { - this.collection.forget(this.arrayChanged); + if (this.collection.isObservable && this.collection.toArray) { + return this.collection.forget('toArray', this.arrayChanged); } else if (this.collection.isEventEmitter) { - this.collection.event('itemsWereAdded').removeHandler(this.currentAddNumber); - this.collection.event('itemsWereRemoved').removeHandler(this.currentRemovedHandler); + this.collection.event('itemsWereAdded').removeHandler(this.currentAddedHandler); + return this.collection.event('itemsWereRemoved').removeHandler(this.currentRemovedHandler); } } + }; + Iterator.prototype.collectionChange = function(newCollection) { + var key, value, _ref; + if (newCollection !== this.collection) { + this.unbindCollection(); + } this.collection = newCollection; if (this.collection) { if (this.collection.isObservable && this.collection.toArray) { @@ -4796,8 +5116,7 @@ for (i = 0, _len = items.length; i < _len; i++) { item = items[i]; _results.push(this.addItem(item, { - fragment: true, - addNumber: this.currentAddFunction + i + fragment: true })); } return _results; @@ -4814,24 +5133,49 @@ }, this)); } if (this.collection.toArray) { - return this.arrayChanged(); + this.arrayChanged(); } else if (this.collection.forEach) { - return this.collection.forEach(__bind(function(item) { + this.collection.forEach(__bind(function(item) { return this.addItem(item); }, this)); } else { _ref = this.collection; - _results = []; for (key in _ref) { if (!__hasProp.call(_ref, key)) continue; value = _ref[key]; - _results.push(this.addItem(key)); + this.addItem(key); } - return _results; } } else { - return developer.warn("Warning! data-foreach-" + this.iteratorName + " called with an undefined binding. Key was: " + this.key + "."); + developer.warn("Warning! data-foreach-" + this.iteratorName + " called with an undefined binding. Key was: " + this.key + "."); + } + return this.processActionQueue(); + }; + Iterator.prototype.arrayChanged = function() { + var existingNode, item, newItemsInOrder, trackingNodeMap, _i, _len; + newItemsInOrder = this.collection.toArray(); + trackingNodeMap = new Batman.SimpleHash; + for (_i = 0, _len = newItemsInOrder.length; _i < _len; _i++) { + item = newItemsInOrder[_i]; + existingNode = this.nodeMap.get(item); + trackingNodeMap.set(item, true); + if (existingNode) { + this.insertItem(item, existingNode, { + fragment: false, + actionNumber: this.queuedActionNumber++, + sync: true + }); + } else { + this.addItem(item, { + fragment: false + }); + } } + return this.nodeMap.forEach(__bind(function(item, node) { + if (!trackingNodeMap.hasKey(item)) { + return this.removeItem(item); + } + }, this)); }; Iterator.prototype.addItem = function(item, options) { var childRenderer, finish, self; @@ -4840,26 +5184,37 @@ fragment: true }; } - options.addNumber = this.queuedAddNumber++; this.parentRenderer.prevent('rendered'); - finish = __bind(function() { - this.parentRenderer.allow('rendered'); - return this.parentRenderer.fire('rendered'); - }, this); self = this; + options.actionNumber = this.queuedActionNumber++; childRenderer = new Batman.Renderer(this._nodeForItem(item), (function() { return self.insertItem(item, this.node, options); }), this.context.descend(item, this.iteratorName)); this.rendererMap.set(item, childRenderer); + finish = __bind(function() { + if (this.destroyed) { + return; + } + this.parentRenderer.allow('rendered'); + return this.parentRenderer.fire('rendered'); + }, this); childRenderer.on('rendered', finish); - return childRenderer.on('stopped', __bind(function() { - this.addFunctions[options.addNumber] = function() {}; - this._processAddQueue(); - return finish(); + childRenderer.on('stopped', __bind(function() { + if (this.destroyed) { + return; + } + this.actions[options.actionNumber] = function() {}; + finish(); + return this.processActionQueue(); }, this)); + return item; }; Iterator.prototype.removeItem = function(item) { var hideFunction, oldNode; + if (this.destroyed) { + return; + } + this._removeOldAction(item); oldNode = this.nodeMap.unset(item); if (oldNode) { if (hideFunction = Batman.data(oldNode, 'hide')) { @@ -4869,57 +5224,43 @@ } } }; - Iterator.prototype.arrayChanged = function() { - var existingNode, item, newItemsInOrder, trackingNodeMap, _i, _len; - newItemsInOrder = this.collection.toArray(); - trackingNodeMap = new Batman.SimpleHash; - for (_i = 0, _len = newItemsInOrder.length; _i < _len; _i++) { - item = newItemsInOrder[_i]; - existingNode = this.nodeMap.get(item); - trackingNodeMap.set(item, true); - if (existingNode) { - this.insertItem(item, existingNode, { - fragment: false, - addNumber: this.queuedAddNumber++, - sync: true - }); - } else { - this.addItem(item, { - fragment: false - }); - } - } - return this.nodeMap.forEach(__bind(function(item, node) { - if (!trackingNodeMap.hasKey(item)) { - return this.removeItem(item); - } - }, this)); - }; Iterator.prototype.insertItem = function(item, node, options) { + var futureActionNumber, _base, _base2, _name, _name2; if (options == null) { options = {}; } - if (this.nodeMap.get(item) !== node) { - this.addFunctions[options.addNumber] = function() {}; + if (this.destroyed) { + return; + } + futureActionNumber = this.actionMap.get(item); + if ((futureActionNumber != null) && futureActionNumber > options.actionNumber) { + this.actions[options.actionNumber] = function() {}; } else { - this.rendererMap.unset(item); - this.addFunctions[options.addNumber] = function() { - var show; - show = Batman.data(node, 'show'); - if (typeof show === 'function') { - return show.call(node, { - before: this.siblingNode - }); - } else { - if (options.fragment) { - return this.fragment.appendChild(node); + this._removeOldAction(item); + this.actionMap.set(item, options.actionNumber); + if (this.nodeMap.get(item) !== node) { + (_base = this.actions)[_name = options.actionNumber] || (_base[_name] = function() {}); + } else { + this.rendererMap.unset(item); + (_base2 = this.actions)[_name2 = options.actionNumber] || (_base2[_name2] = function() { + var show; + show = Batman.data(node, 'show'); + if (typeof show === 'function') { + return show.call(node, { + before: this.siblingNode + }); } else { - return this.parentNode.insertBefore(node, this.siblingNode); + if (options.fragment) { + return this.fragment.appendChild(node); + } else { + return this.parentNode.insertBefore(node, this.siblingNode); + } } - } - }; + }); + } } - return this._processAddQueue(); + this.actions[options.actionNumber].item = item; + return this.processActionQueue(); }; Iterator.prototype._nodeForItem = function(item) { var newNode; @@ -4927,16 +5268,43 @@ this.nodeMap.set(item, newNode); return newNode; }; - Iterator.prototype._processAddQueue = function() { - var f; - while (!!(f = this.addFunctions[this.currentAddNumber])) { - this.addFunctions[this.currentAddNumber] = void 0; - f.call(this); - this.currentAddNumber++; + Iterator.prototype._removeOldAction = function(item) { + var oldActionNumber; + oldActionNumber = this.actionMap.get(item); + if ((oldActionNumber != null) && oldActionNumber > this.currentActionNumber) { + this.actionMap.unset(item); + return this.actions[oldActionNumber] = function() {}; + } + }; + Iterator.prototype.processActionQueue = function() { + if (this.destroyed) { + return; } - if (this.fragment && this.rendererMap.length === 0 && this.fragment.hasChildNodes()) { - this.parentNode.insertBefore(this.fragment, this.siblingNode); - this.fragment = document.createDocumentFragment(); + if (!this.actionQueueTimeout) { + return this.actionQueueTimeout = $setImmediate(__bind(function() { + var f, startTime; + if (this.destroyed) { + return; + } + delete this.actionQueueTimeout; + startTime = new Date; + while (!!(f = this.actions[this.currentActionNumber])) { + this.actions[this.currentActionNumber] = true; + f.call(this); + this.currentActionNumber++; + if (this.deferEvery && (new Date - startTime) > this.deferEvery) { + return this.processActionQueue(); + } + } + if (this.fragment && this.rendererMap.length === 0 && this.fragment.hasChildNodes()) { + this.parentNode.insertBefore(this.fragment, this.siblingNode); + this.fragment = document.createDocumentFragment(); + } + if (this.currentActionNumber === this.queuedActionNumber) { + this.parentRenderer.allow('rendered'); + return this.parentRenderer.fire('rendered'); + } + }, this)); } }; return Iterator; @@ -5011,7 +5379,23 @@ meta: buntUndefined(function(value, keypath) { developer.assert(value.meta, "Error, value doesn't have a meta to filter on!"); return value.meta.get(keypath); - }) + }), + interpolate: function(string, interpolationKeypaths) { + var k, v, values; + if (string == null) { + return; + } + values = {}; + for (k in interpolationKeypaths) { + v = interpolationKeypaths[k]; + values[k] = this.findKey(v)[0]; + if (!(values[k] != null)) { + Batman.developer.warn("Warning! Undefined interpolation key " + k + " for interpolation", string); + values[k] = ''; + } + } + return Batman.helpers.interpolate(string, values); + } }; _ref = ['capitalize', 'singularize', 'underscore', 'camelize']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -5176,11 +5560,137 @@ } } }; + Batman.Paginator = (function() { + __extends(Paginator, Batman.Object); + function Paginator() { + Paginator.__super__.constructor.apply(this, arguments); + } + Paginator.Cache = (function() { + function Cache(offset, limit, items) { + this.offset = offset; + this.limit = limit; + this.items = items; + this.length = items.length; + this.reach = offset + limit; + } + Cache.prototype.containsItemsForOffsetAndLimit = function(offset, limit) { + return offset >= this.offset && (offset + limit) <= this.reach; + }; + Cache.prototype.itemsForOffsetAndLimit = function(offset, limit) { + var begin, end; + if (!this.containsItemsForOffsetAndLimit(offset, limit)) { + return; + } + begin = offset - this.offset; + end = begin + limit; + return this.items.slice(begin, end); + }; + return Cache; + })(); + Paginator.prototype.offset = 0; + Paginator.prototype.limit = 10; + Paginator.prototype.totalCount = 0; + Paginator.prototype.offsetFromPageAndLimit = function(page, limit) { + return Math.round((+page - 1) * limit); + }; + Paginator.prototype.pageFromOffsetAndLimit = function(offset, limit) { + return offset / limit + 1; + }; + Paginator.prototype.toArray = function() { + var cache, items, limit, offset; + cache = this.get('cache'); + offset = this.get('offset'); + limit = this.get('limit'); + items = cache != null ? cache.itemsForOffsetAndLimit(offset, limit) : void 0; + if (!items) { + this.loadItemsForOffsetAndLimit(offset, limit); + } + return items || []; + }; + Paginator.prototype.page = function() { + return this.pageFromOffsetAndLimit(this.get('offset'), this.get('limit')); + }; + Paginator.prototype.pageCount = function() { + return Math.ceil(this.get('totalCount') / this.get('limit')); + }; + Paginator.prototype.previousPage = function() { + return this.set('page', this.get('page') - 1); + }; + Paginator.prototype.nextPage = function() { + return this.set('page', this.get('page') + 1); + }; + Paginator.prototype.loadItemsForOffsetAndLimit = function(offset, limit) {}; + Paginator.prototype.updateCache = function(offset, limit, items) { + return this.set('cache', new Batman.Paginator.Cache(offset, limit, items)); + }; + Paginator.accessor('toArray', Paginator.prototype.toArray); + Paginator.accessor('offset', 'limit', 'totalCount', { + get: Batman.Property.defaultAccessor.get, + set: function(key, value) { + return Batman.Property.defaultAccessor.set.call(this, key, +value); + } + }); + Paginator.accessor('page', { + get: Paginator.prototype.page, + set: function(_, value) { + value = +value; + this.set('offset', this.offsetFromPageAndLimit(value, this.get('limit'))); + return value; + } + }); + Paginator.accessor('pageCount', Paginator.prototype.pageCount); + return Paginator; + })(); + Batman.ModelPaginator = (function() { + __extends(ModelPaginator, Batman.Paginator); + function ModelPaginator() { + ModelPaginator.__super__.constructor.apply(this, arguments); + } + ModelPaginator.prototype.cachePadding = 0; + ModelPaginator.prototype.paddedOffset = function(offset) { + offset -= this.cachePadding; + if (offset < 0) { + return 0; + } else { + return offset; + } + }; + ModelPaginator.prototype.paddedLimit = function(limit) { + return limit + this.cachePadding * 2; + }; + ModelPaginator.prototype.loadItemsForOffsetAndLimit = function(offset, limit) { + var k, params, v, _ref2; + params = this.paramsForOffsetAndLimit(offset, limit); + _ref2 = this.params; + for (k in _ref2) { + v = _ref2[k]; + params[k] = v; + } + return this.model.load(params, __bind(function(err, records) { + if (err == null) { + return this.updateCache(this.offsetFromParams(params), this.limitFromParams(params), records); + } + }, this)); + }; + ModelPaginator.prototype.paramsForOffsetAndLimit = function(offset, limit) { + return { + offset: this.paddedOffset(offset), + limit: this.paddedLimit(limit) + }; + }; + ModelPaginator.prototype.offsetFromParams = function(params) { + return params.offset; + }; + ModelPaginator.prototype.limitFromParams = function(params) { + return params.limit; + }; + return ModelPaginator; + })(); container = typeof exports !== "undefined" && exports !== null ? (module.exports = Batman, global) : (window.Batman = Batman, window); $mixin(container, Batman.Observable); Batman.exportHelpers = function(onto) { var k, _j, _len2, _ref2; - _ref2 = ['mixin', 'unmixin', 'route', 'redirect', 'typeOf', 'redirect']; + _ref2 = ['mixin', 'unmixin', 'route', 'redirect', 'typeOf', 'redirect', 'setImmediate']; for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { k = _ref2[_j]; onto["$" + k] = Batman[k];