diff --git a/app/assets/javascripts/app/index.js.coffee b/app/assets/javascripts/app/index.js.coffee index c24f80b0a..67420c8fb 100644 --- a/app/assets/javascripts/app/index.js.coffee +++ b/app/assets/javascripts/app/index.js.coffee @@ -2,9 +2,9 @@ #= require ./lib/jquery-1.7.2.min.js #= require ./lib/ui/jquery-ui-1.8.18.custom.min.js -#= require ./lib/spine/spine.coffee -#= require ./lib/spine/ajax.coffee -#= require ./lib/spine/route.coffee +#= require ./lib/spine/spine.js +#= require ./lib/spine/ajax.js +#= require ./lib/spine/route.js #= require ./lib/bootstrap-dropdown.js #= require ./lib/bootstrap-tooltip.js diff --git a/app/assets/javascripts/app/lib/spine/ajax.coffee b/app/assets/javascripts/app/lib/spine/ajax.coffee deleted file mode 100644 index ac017d78b..000000000 --- a/app/assets/javascripts/app/lib/spine/ajax.coffee +++ /dev/null @@ -1,206 +0,0 @@ -Spine ?= require('spine') -$ = Spine.$ -Model = Spine.Model - -Ajax = - getURL: (object) -> - object and object.url?() or object.url - - enabled: true - pending: false - requests: [] - - disable: (callback) -> - if @enabled - @enabled = false - do callback - @enabled = true - else - do callback - - requestNext: -> - next = @requests.shift() - if next - @request(next) - else - @pending = false - - request: (callback) -> - (do callback).complete(=> do @requestNext) - - queue: (callback) -> - return unless @enabled - if @pending - @requests.push(callback) - else - @pending = true - @request(callback) - callback - -class Base - defaults: - contentType: 'application/json' - dataType: 'json' - processData: false - headers: {'X-Requested-With': 'XMLHttpRequest'} - - ajax: (params, defaults) -> - $.ajax($.extend({}, @defaults, defaults, params)) - - queue: (callback) -> - Ajax.queue(callback) - -class Collection extends Base - constructor: (@model) -> - - find: (id, params) -> - record = new @model(id: id) - @ajax( - params, - type: 'GET', - url: Ajax.getURL(record) - ).success(@recordsResponse) - .error(@errorResponse) - - all: (params) -> - @ajax( - params, - type: 'GET', - url: Ajax.getURL(@model) - ).success(@recordsResponse) - .error(@errorResponse) - - fetch: (params = {}, options = {}) -> - if id = params.id - delete params.id - @find(id, params).success (record) => - @model.refresh(record, options) - else - @all(params).success (records) => - @model.refresh(records, options) - - # Private - - recordsResponse: (data, status, xhr) => - @model.trigger('ajaxSuccess', null, status, xhr) - - errorResponse: (xhr, statusText, error) => - @model.trigger('ajaxError', null, xhr, statusText, error) - -class Singleton extends Base - constructor: (@record) -> - @model = @record.constructor - - reload: (params, options) -> - @queue => - @ajax( - params, - type: 'GET' - url: Ajax.getURL(@record) - ).success(@recordResponse(options)) - .error(@errorResponse(options)) - - create: (params, options) -> - @queue => - @ajax( - params, - type: 'POST' - data: JSON.stringify(@record) - url: Ajax.getURL(@model) - ).success(@recordResponse(options)) - .error(@errorResponse(options)) - - update: (params, options) -> - @queue => - @ajax( - params, - type: 'PUT' - data: JSON.stringify(@record) - url: Ajax.getURL(@record) - ).success(@recordResponse(options)) - .error(@errorResponse(options)) - - destroy: (params, options) -> - @queue => - @ajax( - params, - type: 'DELETE' - url: Ajax.getURL(@record) - ).success(@recordResponse(options)) - .error(@errorResponse(options)) - - # Private - - recordResponse: (options = {}) => - (data, status, xhr) => - if Spine.isBlank(data) - data = false - else - data = @model.fromJSON(data) - - Ajax.disable => - if data - # ID change, need to do some shifting - if data.id and @record.id isnt data.id - @record.changeID(data.id) - - # Update with latest data - @record.updateAttributes(data.attributes()) - - @record.trigger('ajaxSuccess', data, status, xhr) - # 2012-02-23 me -# options.success?.apply(@record) - options.success?(@record) - - errorResponse: (options = {}) => - (xhr, statusText, error) => - @record.trigger('ajaxError', xhr, statusText, error) - options.error?.apply(@record) - -# Ajax endpoint -Model.host = '' - -Include = - ajax: -> new Singleton(this) - - url: (args...) -> - url = Ajax.getURL(@constructor) - url += '/' unless url.charAt(url.length - 1) is '/' - url += encodeURIComponent(@id) - args.unshift(url) - args.join('/') - -Extend = - ajax: -> new Collection(this) - - url: (args...) -> - args.unshift(@className.toLowerCase() + 's') - args.unshift(Model.host) - args.join('/') - -Model.Ajax = - extended: -> - @fetch @ajaxFetch - @change @ajaxChange - - @extend Extend - @include Include - - # Private - - ajaxFetch: -> - @ajax().fetch(arguments...) - - ajaxChange: (record, type, options = {}) -> - return if options.ajax is false - record.ajax()[type](options.ajax, options) - -Model.Ajax.Methods = - extended: -> - @extend Extend - @include Include - -# Globals -Ajax.defaults = Base::defaults -Spine.Ajax = Ajax -module?.exports = Ajax \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/spine/ajax.js b/app/assets/javascripts/app/lib/spine/ajax.js new file mode 100755 index 000000000..4f31dfa55 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/ajax.js @@ -0,0 +1,290 @@ +(function() { + var $, Ajax, Base, Collection, Extend, Include, Model, Singleton, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = Object.prototype.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 = Array.prototype.slice; + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + $ = Spine.$; + + Model = Spine.Model; + + Ajax = { + getURL: function(object) { + return object && (typeof object.url === "function" ? object.url() : void 0) || object.url; + }, + enabled: true, + pending: false, + requests: [], + disable: function(callback) { + if (this.enabled) { + this.enabled = false; + callback(); + return this.enabled = true; + } else { + return callback(); + } + }, + requestNext: function() { + var next; + next = this.requests.shift(); + if (next) { + return this.request(next); + } else { + return this.pending = false; + } + }, + request: function(callback) { + var _this = this; + return (callback()).complete(function() { + return _this.requestNext(); + }); + }, + queue: function(callback) { + if (!this.enabled) return; + if (this.pending) { + this.requests.push(callback); + } else { + this.pending = true; + this.request(callback); + } + return callback; + } + }; + + Base = (function() { + + function Base() {} + + Base.prototype.defaults = { + contentType: 'application/json', + dataType: 'json', + processData: false, + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } + }; + + Base.prototype.ajax = function(params, defaults) { + return $.ajax($.extend({}, this.defaults, defaults, params)); + }; + + Base.prototype.queue = function(callback) { + return Ajax.queue(callback); + }; + + return Base; + + })(); + + Collection = (function(_super) { + + __extends(Collection, _super); + + function Collection(model) { + this.model = model; + this.errorResponse = __bind(this.errorResponse, this); + this.recordsResponse = __bind(this.recordsResponse, this); + } + + Collection.prototype.find = function(id, params) { + var record; + record = new this.model({ + id: id + }); + return this.ajax(params, { + type: 'GET', + url: Ajax.getURL(record) + }).success(this.recordsResponse).error(this.errorResponse); + }; + + Collection.prototype.all = function(params) { + return this.ajax(params, { + type: 'GET', + url: Ajax.getURL(this.model) + }).success(this.recordsResponse).error(this.errorResponse); + }; + + Collection.prototype.fetch = function(params, options) { + var id, + _this = this; + if (params == null) params = {}; + if (options == null) options = {}; + if (id = params.id) { + delete params.id; + return this.find(id, params).success(function(record) { + return _this.model.refresh(record, options); + }); + } else { + return this.all(params).success(function(records) { + return _this.model.refresh(records, options); + }); + } + }; + + Collection.prototype.recordsResponse = function(data, status, xhr) { + return this.model.trigger('ajaxSuccess', null, status, xhr); + }; + + Collection.prototype.errorResponse = function(xhr, statusText, error) { + return this.model.trigger('ajaxError', null, xhr, statusText, error); + }; + + return Collection; + + })(Base); + + Singleton = (function(_super) { + + __extends(Singleton, _super); + + function Singleton(record) { + this.record = record; + this.errorResponse = __bind(this.errorResponse, this); + this.recordResponse = __bind(this.recordResponse, this); + this.model = this.record.constructor; + } + + Singleton.prototype.reload = function(params, options) { + var _this = this; + return this.queue(function() { + return _this.ajax(params, { + type: 'GET', + url: Ajax.getURL(_this.record) + }).success(_this.recordResponse(options)).error(_this.errorResponse(options)); + }); + }; + + Singleton.prototype.create = function(params, options) { + var _this = this; + return this.queue(function() { + return _this.ajax(params, { + type: 'POST', + data: JSON.stringify(_this.record), + url: Ajax.getURL(_this.model) + }).success(_this.recordResponse(options)).error(_this.errorResponse(options)); + }); + }; + + Singleton.prototype.update = function(params, options) { + var _this = this; + return this.queue(function() { + return _this.ajax(params, { + type: 'PUT', + data: JSON.stringify(_this.record), + url: Ajax.getURL(_this.record) + }).success(_this.recordResponse(options)).error(_this.errorResponse(options)); + }); + }; + + Singleton.prototype.destroy = function(params, options) { + var _this = this; + return this.queue(function() { + return _this.ajax(params, { + type: 'DELETE', + url: Ajax.getURL(_this.record) + }).success(_this.recordResponse(options)).error(_this.errorResponse(options)); + }); + }; + + Singleton.prototype.recordResponse = function(options) { + var _this = this; + if (options == null) options = {}; + return function(data, status, xhr) { + var _ref; + if (Spine.isBlank(data)) { + data = false; + } else { + data = _this.model.fromJSON(data); + } + Ajax.disable(function() { + if (data) { + if (data.id && _this.record.id !== data.id) { + _this.record.changeID(data.id); + } + return _this.record.updateAttributes(data.attributes()); + } + }); + _this.record.trigger('ajaxSuccess', data, status, xhr); + return (_ref = options.success) != null ? _ref.apply(_this.record) : void 0; + }; + }; + + Singleton.prototype.errorResponse = function(options) { + var _this = this; + if (options == null) options = {}; + return function(xhr, statusText, error) { + var _ref; + _this.record.trigger('ajaxError', xhr, statusText, error); + return (_ref = options.error) != null ? _ref.apply(_this.record) : void 0; + }; + }; + + return Singleton; + + })(Base); + + Model.host = ''; + + Include = { + ajax: function() { + return new Singleton(this); + }, + url: function() { + var args, url; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + url = Ajax.getURL(this.constructor); + if (url.charAt(url.length - 1) !== '/') url += '/'; + url += encodeURIComponent(this.id); + args.unshift(url); + return args.join('/'); + } + }; + + Extend = { + ajax: function() { + return new Collection(this); + }, + url: function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + args.unshift(this.className.toLowerCase() + 's'); + args.unshift(Model.host); + return args.join('/'); + } + }; + + Model.Ajax = { + extended: function() { + this.fetch(this.ajaxFetch); + this.change(this.ajaxChange); + this.extend(Extend); + return this.include(Include); + }, + ajaxFetch: function() { + var _ref; + return (_ref = this.ajax()).fetch.apply(_ref, arguments); + }, + ajaxChange: function(record, type, options) { + if (options == null) options = {}; + if (options.ajax === false) return; + return record.ajax()[type](options.ajax, options); + } + }; + + Model.Ajax.Methods = { + extended: function() { + this.extend(Extend); + return this.include(Include); + } + }; + + Ajax.defaults = Base.prototype.defaults; + + Spine.Ajax = Ajax; + + if (typeof module !== "undefined" && module !== null) module.exports = Ajax; + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/list.coffee b/app/assets/javascripts/app/lib/spine/list.coffee deleted file mode 100644 index 76047594a..000000000 --- a/app/assets/javascripts/app/lib/spine/list.coffee +++ /dev/null @@ -1,42 +0,0 @@ -Spine ?= require('spine') -$ = Spine.$ - -class Spine.List extends Spine.Controller - events: - 'click .item': 'click' - - selectFirst: false - - constructor: -> - super - @bind 'change', @change - - template: -> arguments[0] - - change: (item) => - @current = item - - unless @current - @children().removeClass('active') - return - - @children().removeClass('active') - @children().forItem(@current).addClass('active') - - render: (items) -> - @items = items if items - @html @template(@items) - @change @current - if @selectFirst - unless @children('.active').length - @children(':first').click() - - children: (sel) -> - @el.children(sel) - - click: (e) -> - item = $(e.currentTarget).item() - @trigger('change', item) - true - -module?.exports = Spine.List \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/spine/list.js b/app/assets/javascripts/app/lib/spine/list.js new file mode 100755 index 000000000..5a4dc1b3c --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/list.js @@ -0,0 +1,70 @@ +(function() { + var $, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = Object.prototype.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; }; + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + $ = Spine.$; + + Spine.List = (function(_super) { + + __extends(List, _super); + + List.prototype.events = { + 'click .item': 'click' + }; + + List.prototype.selectFirst = false; + + function List() { + this.change = __bind(this.change, this); List.__super__.constructor.apply(this, arguments); + this.bind('change', this.change); + } + + List.prototype.template = function() { + return arguments[0]; + }; + + List.prototype.change = function(item) { + this.current = item; + if (!this.current) { + this.children().removeClass('active'); + return; + } + this.children().removeClass('active'); + return this.children().forItem(this.current).addClass('active'); + }; + + List.prototype.render = function(items) { + if (items) this.items = items; + this.html(this.template(this.items)); + this.change(this.current); + if (this.selectFirst) { + if (!this.children('.active').length) { + return this.children(':first').click(); + } + } + }; + + List.prototype.children = function(sel) { + return this.el.children(sel); + }; + + List.prototype.click = function(e) { + var item; + item = $(e.currentTarget).item(); + this.trigger('change', item); + return true; + }; + + return List; + + })(Spine.Controller); + + if (typeof module !== "undefined" && module !== null) { + module.exports = Spine.List; + } + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/local.coffee b/app/assets/javascripts/app/lib/spine/local.coffee deleted file mode 100644 index 05ef90c74..000000000 --- a/app/assets/javascripts/app/lib/spine/local.coffee +++ /dev/null @@ -1,16 +0,0 @@ -Spine ?= require('spine') - -Spine.Model.Local = - extended: -> - @change @saveLocal - @fetch @loadLocal - - saveLocal: -> - result = JSON.stringify(@) - localStorage[@className] = result - - loadLocal: -> - result = localStorage[@className] - @refresh(result or [], clear: true) - -module?.exports = Spine.Model.Local \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/spine/local.js b/app/assets/javascripts/app/lib/spine/local.js new file mode 100755 index 000000000..3cb511d66 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/local.js @@ -0,0 +1,28 @@ +(function() { + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + Spine.Model.Local = { + extended: function() { + this.change(this.saveLocal); + return this.fetch(this.loadLocal); + }, + saveLocal: function() { + var result; + result = JSON.stringify(this); + return localStorage[this.className] = result; + }, + loadLocal: function() { + var result; + result = localStorage[this.className]; + return this.refresh(result || [], { + clear: true + }); + } + }; + + if (typeof module !== "undefined" && module !== null) { + module.exports = Spine.Model.Local; + } + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/manager.coffee b/app/assets/javascripts/app/lib/spine/manager.coffee deleted file mode 100644 index 16cafe17a..000000000 --- a/app/assets/javascripts/app/lib/spine/manager.coffee +++ /dev/null @@ -1,85 +0,0 @@ -Spine ?= require('spine') -$ = Spine.$ - -class Spine.Manager extends Spine.Module - @include Spine.Events - - constructor: -> - @controllers = [] - @bind 'change', @change - @add(arguments...) - - add: (controllers...) -> - @addOne(cont) for cont in controllers - - addOne: (controller) -> - controller.bind 'active', (args...) => - @trigger('change', controller, args...) - controller.bind 'release', => - @controllers.splice(@controllers.indexOf(controller), 1) - - @controllers.push(controller) - - deactivate: -> - @trigger('change', false, arguments...) - - # Private - - change: (current, args...) -> - for cont in @controllers - if cont is current - cont.activate(args...) - else - cont.deactivate(args...) - -Spine.Controller.include - active: (args...) -> - if typeof args[0] is 'function' - @bind('active', args[0]) - else - args.unshift('active') - @trigger(args...) - @ - - isActive: -> - @el.hasClass('active') - - activate: -> - @el.addClass('active') - @ - - deactivate: -> - @el.removeClass('active') - @ - -class Spine.Stack extends Spine.Controller - controllers: {} - routes: {} - - className: 'spine stack' - - constructor: -> - super - - @manager = new Spine.Manager - - @manager.bind 'change', (controller, args...) => - @active(args...) if controller - - for key, value of @controllers - @[key] = new value(stack: @) - @add(@[key]) - - for key, value of @routes - do (key, value) => - callback = value if typeof value is 'function' - callback or= => @[value].active(arguments...) - @route(key, callback) - - @[@default].active() if @default - - add: (controller) -> - @manager.add(controller) - @append(controller) - -module?.exports = Spine.Manager \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/spine/manager.js b/app/assets/javascripts/app/lib/spine/manager.js new file mode 100755 index 000000000..da41a21da --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/manager.js @@ -0,0 +1,154 @@ +(function() { + var $, + __hasProp = Object.prototype.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 = Array.prototype.slice; + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + $ = Spine.$; + + Spine.Manager = (function(_super) { + + __extends(Manager, _super); + + Manager.include(Spine.Events); + + function Manager() { + this.controllers = []; + this.bind('change', this.change); + this.add.apply(this, arguments); + } + + Manager.prototype.add = function() { + var cont, controllers, _i, _len, _results; + controllers = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _results = []; + for (_i = 0, _len = controllers.length; _i < _len; _i++) { + cont = controllers[_i]; + _results.push(this.addOne(cont)); + } + return _results; + }; + + Manager.prototype.addOne = function(controller) { + var _this = this; + controller.bind('active', function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return _this.trigger.apply(_this, ['change', controller].concat(__slice.call(args))); + }); + controller.bind('release', function() { + return _this.controllers.splice(_this.controllers.indexOf(controller), 1); + }); + return this.controllers.push(controller); + }; + + Manager.prototype.deactivate = function() { + return this.trigger.apply(this, ['change', false].concat(__slice.call(arguments))); + }; + + Manager.prototype.change = function() { + var args, cont, current, _i, _len, _ref, _results; + current = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + _ref = this.controllers; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + cont = _ref[_i]; + if (cont === current) { + _results.push(cont.activate.apply(cont, args)); + } else { + _results.push(cont.deactivate.apply(cont, args)); + } + } + return _results; + }; + + return Manager; + + })(Spine.Module); + + Spine.Controller.include({ + active: function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (typeof args[0] === 'function') { + this.bind('active', args[0]); + } else { + args.unshift('active'); + this.trigger.apply(this, args); + } + return this; + }, + isActive: function() { + return this.el.hasClass('active'); + }, + activate: function() { + this.el.addClass('active'); + return this; + }, + deactivate: function() { + this.el.removeClass('active'); + return this; + } + }); + + Spine.Stack = (function(_super) { + + __extends(Stack, _super); + + Stack.prototype.controllers = {}; + + Stack.prototype.routes = {}; + + Stack.prototype.className = 'spine stack'; + + function Stack() { + var key, value, _fn, _ref, _ref2, + _this = this; + Stack.__super__.constructor.apply(this, arguments); + this.manager = new Spine.Manager; + this.manager.bind('change', function() { + var args, controller; + controller = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (controller) return _this.active.apply(_this, args); + }); + _ref = this.controllers; + for (key in _ref) { + value = _ref[key]; + this[key] = new value({ + stack: this + }); + this.add(this[key]); + } + _ref2 = this.routes; + _fn = function(key, value) { + var callback; + if (typeof value === 'function') callback = value; + callback || (callback = function() { + var _ref3; + return (_ref3 = _this[value]).active.apply(_ref3, arguments); + }); + return _this.route(key, callback); + }; + for (key in _ref2) { + value = _ref2[key]; + _fn(key, value); + } + if (this["default"]) this[this["default"]].active(); + } + + Stack.prototype.add = function(controller) { + this.manager.add(controller); + return this.append(controller); + }; + + return Stack; + + })(Spine.Controller); + + if (typeof module !== "undefined" && module !== null) { + module.exports = Spine.Manager; + } + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/relation.coffee b/app/assets/javascripts/app/lib/spine/relation.coffee deleted file mode 100644 index 5b2956ea2..000000000 --- a/app/assets/javascripts/app/lib/spine/relation.coffee +++ /dev/null @@ -1,144 +0,0 @@ -Spine ?= require('spine') -isArray = Spine.isArray -require ?= ((value) -> eval(value)) - -class Collection extends Spine.Module - constructor: (options = {}) -> - for key, value of options - @[key] = value - - all: -> - @model.select (rec) => @associated(rec) - - first: -> - @all()[0] - - last: -> - values = @all() - values[values.length - 1] - - find: (id) -> - records = @select (rec) => - rec.id + '' is id + '' - throw('Unknown record') unless records[0] - records[0] - - findAllByAttribute: (name, value) -> - @model.select (rec) => - rec[name] is value - - findByAttribute: (name, value) -> - @findAllByAttribute(name, value)[0] - - select: (cb) -> - @model.select (rec) => - @associated(rec) and cb(rec) - - refresh: (values) -> - delete @model.records[record.id] for record in @all() - records = @model.fromJSON(values) - - records = [records] unless isArray(records) - - for record in records - record.newRecord = false - record[@fkey] = @record.id - @model.records[record.id] = record - - @model.trigger('refresh', records) - - create: (record) -> - record[@fkey] = @record.id - @model.create(record) - - # Private - - associated: (record) -> - record[@fkey] is @record.id - -class Instance extends Spine.Module - constructor: (options = {}) -> - for key, value of options - @[key] = value - - exists: -> - @record[@fkey] and @model.exists(@record[@fkey]) - - update: (value) -> - unless value instanceof @model - value = new @model(value) - value.save() if value.isNew() - @record[@fkey] = value and value.id - -class Singleton extends Spine.Module - constructor: (options = {}) -> - for key, value of options - @[key] = value - - find: -> - @record.id and @model.findByAttribute(@fkey, @record.id) - - update: (value) -> - unless value instanceof @model - value = @model.fromJSON(value) - - value[@fkey] = @record.id - value.save() - -singularize = (str) -> - str.replace(/s$/, '') - -underscore = (str) -> - str.replace(/::/g, '/') - .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') - .replace(/([a-z\d])([A-Z])/g, '$1_$2') - .replace(/-/g, '_') - .toLowerCase() - -Spine.Model.extend - hasMany: (name, model, fkey) -> - fkey ?= "#{underscore(this.className)}_id" - - association = (record) -> - model = require(model) if typeof model is 'string' - - new Collection( - name: name, model: model, - record: record, fkey: fkey - ) - - @::[name] = (value) -> - association(@).refresh(value) if value? - association(@) - - belongsTo: (name, model, fkey) -> - fkey ?= "#{singularize(name)}_id" - - association = (record) -> - model = require(model) if typeof model is 'string' - - new Instance( - name: name, model: model, - record: record, fkey: fkey - ) - - @::[name] = (value) -> - association(@).update(value) if value? - association(@).exists() - - @attributes.push(fkey) - - hasOne: (name, model, fkey) -> - fkey ?= "#{underscore(@className)}_id" - - association = (record) -> - model = require(model) if typeof model is 'string' - - new Singleton( - name: name, model: model, - record: record, fkey: fkey - ) - - @::[name] = (value) -> - association(@).update(value) if value? - association(@).find() \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/spine/relation.js b/app/assets/javascripts/app/lib/spine/relation.js new file mode 100755 index 000000000..737cf4c66 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/relation.js @@ -0,0 +1,222 @@ +(function() { + var Collection, Instance, Singleton, isArray, singularize, underscore, + __hasProp = Object.prototype.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; }; + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + isArray = Spine.isArray; + + if (typeof require === "undefined" || require === null) { + require = (function(value) { + return eval(value); + }); + } + + Collection = (function(_super) { + + __extends(Collection, _super); + + function Collection(options) { + var key, value; + if (options == null) options = {}; + for (key in options) { + value = options[key]; + this[key] = value; + } + } + + Collection.prototype.all = function() { + var _this = this; + return this.model.select(function(rec) { + return _this.associated(rec); + }); + }; + + Collection.prototype.first = function() { + return this.all()[0]; + }; + + Collection.prototype.last = function() { + var values; + values = this.all(); + return values[values.length - 1]; + }; + + Collection.prototype.find = function(id) { + var records, + _this = this; + records = this.select(function(rec) { + return rec.id + '' === id + ''; + }); + if (!records[0]) throw 'Unknown record'; + return records[0]; + }; + + Collection.prototype.findAllByAttribute = function(name, value) { + var _this = this; + return this.model.select(function(rec) { + return rec[name] === value; + }); + }; + + Collection.prototype.findByAttribute = function(name, value) { + return this.findAllByAttribute(name, value)[0]; + }; + + Collection.prototype.select = function(cb) { + var _this = this; + return this.model.select(function(rec) { + return _this.associated(rec) && cb(rec); + }); + }; + + Collection.prototype.refresh = function(values) { + var record, records, _i, _j, _len, _len2, _ref; + _ref = this.all(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + record = _ref[_i]; + delete this.model.records[record.id]; + } + records = this.model.fromJSON(values); + if (!isArray(records)) records = [records]; + for (_j = 0, _len2 = records.length; _j < _len2; _j++) { + record = records[_j]; + record.newRecord = false; + record[this.fkey] = this.record.id; + this.model.records[record.id] = record; + } + return this.model.trigger('refresh', records); + }; + + Collection.prototype.create = function(record) { + record[this.fkey] = this.record.id; + return this.model.create(record); + }; + + Collection.prototype.associated = function(record) { + return record[this.fkey] === this.record.id; + }; + + return Collection; + + })(Spine.Module); + + Instance = (function(_super) { + + __extends(Instance, _super); + + function Instance(options) { + var key, value; + if (options == null) options = {}; + for (key in options) { + value = options[key]; + this[key] = value; + } + } + + Instance.prototype.exists = function() { + return this.record[this.fkey] && this.model.exists(this.record[this.fkey]); + }; + + Instance.prototype.update = function(value) { + if (!(value instanceof this.model)) value = new this.model(value); + if (value.isNew()) value.save(); + return this.record[this.fkey] = value && value.id; + }; + + return Instance; + + })(Spine.Module); + + Singleton = (function(_super) { + + __extends(Singleton, _super); + + function Singleton(options) { + var key, value; + if (options == null) options = {}; + for (key in options) { + value = options[key]; + this[key] = value; + } + } + + Singleton.prototype.find = function() { + return this.record.id && this.model.findByAttribute(this.fkey, this.record.id); + }; + + Singleton.prototype.update = function(value) { + if (!(value instanceof this.model)) value = this.model.fromJSON(value); + value[this.fkey] = this.record.id; + return value.save(); + }; + + return Singleton; + + })(Spine.Module); + + singularize = function(str) { + return str.replace(/s$/, ''); + }; + + underscore = function(str) { + return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/-/g, '_').toLowerCase(); + }; + + Spine.Model.extend({ + hasMany: function(name, model, fkey) { + var association; + if (fkey == null) fkey = "" + (underscore(this.className)) + "_id"; + association = function(record) { + if (typeof model === 'string') model = require(model); + return new Collection({ + name: name, + model: model, + record: record, + fkey: fkey + }); + }; + return this.prototype[name] = function(value) { + if (value != null) association(this).refresh(value); + return association(this); + }; + }, + belongsTo: function(name, model, fkey) { + var association; + if (fkey == null) fkey = "" + (singularize(name)) + "_id"; + association = function(record) { + if (typeof model === 'string') model = require(model); + return new Instance({ + name: name, + model: model, + record: record, + fkey: fkey + }); + }; + this.prototype[name] = function(value) { + if (value != null) association(this).update(value); + return association(this).exists(); + }; + return this.attributes.push(fkey); + }, + hasOne: function(name, model, fkey) { + var association; + if (fkey == null) fkey = "" + (underscore(this.className)) + "_id"; + association = function(record) { + if (typeof model === 'string') model = require(model); + return new Singleton({ + name: name, + model: model, + record: record, + fkey: fkey + }); + }; + return this.prototype[name] = function(value) { + if (value != null) association(this).update(value); + return association(this).find(); + }; + } + }); + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/route.coffee b/app/assets/javascripts/app/lib/spine/route.coffee deleted file mode 100644 index b1f17c455..000000000 --- a/app/assets/javascripts/app/lib/spine/route.coffee +++ /dev/null @@ -1,147 +0,0 @@ -Spine ?= require('spine') -$ = Spine.$ - -hashStrip = /^#*/ -namedParam = /:([\w\d]+)/g -splatParam = /\*([\w\d]+)/g -escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g - -class Spine.Route extends Spine.Module - @extend Spine.Events - - @historySupport: window.history?.pushState? - - @routes: [] - - @options: - trigger: true - history: false - shim: false - - @add: (path, callback) -> - if (typeof path is 'object' and path not instanceof RegExp) - @add(key, value) for key, value of path - else - @routes.push(new @(path, callback)) - - @setup: (options = {}) -> - @options = $.extend({}, @options, options) - - if (@options.history) - @history = @historySupport && @options.history - - return if @options.shim - - if @history - $(window).bind('popstate', @change) - else - $(window).bind('hashchange', @change) - @change() - - @unbind: -> - if @history - $(window).unbind('popstate', @change) - else - $(window).unbind('hashchange', @change) - - @navigate: (args...) -> - options = {} - - lastArg = args[args.length - 1] - if typeof lastArg is 'object' - options = args.pop() - else if typeof lastArg is 'boolean' - options.trigger = args.pop() - - options = $.extend({}, @options, options) - - path = args.join('/') - return if @path is path - @path = path - - @trigger('navigate', @path) - - @matchRoute(@path, options) if options.trigger - - return if options.shim - - if @history - history.pushState( - {}, - document.title, - @path - ) - else - window.location.hash = @path - - # Private - - @getPath: -> - path = window.location.pathname - if path.substr(0,1) isnt '/' - path = '/' + path - path - - @getHash: -> window.location.hash - - @getFragment: -> @getHash().replace(hashStrip, '') - - @getHost: -> - (document.location + '').replace(@getPath() + @getHash(), '') - - @change: -> -# change me 2012-02-15 -# path = if @getFragment() isnt '' then @getFragment() else @getPath() - path = if @getFragment() isnt 'workaround' then @getFragment() else @getPath() - return if path is @path - @path = path - @matchRoute(@path) - - @matchRoute: (path, options) -> - for route in @routes - if route.match(path, options) - @trigger('change', route, path) - return route - - constructor: (@path, @callback) -> - @names = [] - - if typeof path is 'string' - namedParam.lastIndex = 0 - while (match = namedParam.exec(path)) != null - @names.push(match[1]) - - path = path.replace(escapeRegExp, '\\$&') - .replace(namedParam, '([^\/]*)') - .replace(splatParam, '(.*?)') - - @route = new RegExp('^' + path + '$') - else - @route = path - - match: (path, options = {}) -> - match = @route.exec(path) - return false unless match - options.match = match - params = match.slice(1) - - if @names.length - for param, i in params - options[@names[i]] = param - - @callback.call(null, options) isnt false - -# Coffee-script bug -Spine.Route.change = Spine.Route.proxy(Spine.Route.change) - -Spine.Controller.include - route: (path, callback) -> - Spine.Route.add(path, @proxy(callback)) - - routes: (routes) -> - @route(key, value) for key, value of routes - - navigate: -> - Spine.Route.navigate.apply(Spine.Route, arguments) - -module?.exports = Spine.Route \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/spine/route.js b/app/assets/javascripts/app/lib/spine/route.js new file mode 100755 index 000000000..6e22eed90 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/route.js @@ -0,0 +1,197 @@ +(function() { + var $, escapeRegExp, hashStrip, namedParam, splatParam, + __hasProp = Object.prototype.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 = Array.prototype.slice; + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + $ = Spine.$; + + hashStrip = /^#*/; + + namedParam = /:([\w\d]+)/g; + + splatParam = /\*([\w\d]+)/g; + + escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g; + + Spine.Route = (function(_super) { + var _ref; + + __extends(Route, _super); + + Route.extend(Spine.Events); + + Route.historySupport = ((_ref = window.history) != null ? _ref.pushState : void 0) != null; + + Route.routes = []; + + Route.options = { + trigger: true, + history: false, + shim: false + }; + + Route.add = function(path, callback) { + var key, value, _results; + if (typeof path === 'object' && !(path instanceof RegExp)) { + _results = []; + for (key in path) { + value = path[key]; + _results.push(this.add(key, value)); + } + return _results; + } else { + return this.routes.push(new this(path, callback)); + } + }; + + Route.setup = function(options) { + if (options == null) options = {}; + this.options = $.extend({}, this.options, options); + if (this.options.history) { + this.history = this.historySupport && this.options.history; + } + if (this.options.shim) return; + if (this.history) { + $(window).bind('popstate', this.change); + } else { + $(window).bind('hashchange', this.change); + } + return this.change(); + }; + + Route.unbind = function() { + if (this.history) { + return $(window).unbind('popstate', this.change); + } else { + return $(window).unbind('hashchange', this.change); + } + }; + + Route.navigate = function() { + var args, lastArg, options, path; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + options = {}; + lastArg = args[args.length - 1]; + if (typeof lastArg === 'object') { + options = args.pop(); + } else if (typeof lastArg === 'boolean') { + options.trigger = args.pop(); + } + options = $.extend({}, this.options, options); + path = args.join('/'); + if (this.path === path) return; + this.path = path; + this.trigger('navigate', this.path); + if (options.trigger) this.matchRoute(this.path, options); + if (options.shim) return; + if (this.history) { + return history.pushState({}, document.title, this.path); + } else { + return window.location.hash = this.path; + } + }; + + Route.getPath = function() { + var path; + path = window.location.pathname; + if (path.substr(0, 1) !== '/') path = '/' + path; + return path; + }; + + Route.getHash = function() { + return window.location.hash; + }; + + Route.getFragment = function() { + return this.getHash().replace(hashStrip, ''); + }; + + Route.getHost = function() { + return (document.location + '').replace(this.getPath() + this.getHash(), ''); + }; + + Route.change = function() { + var path; + path = this.getFragment() !== '' ? this.getFragment() : this.getPath(); + if (path === this.path) return; + this.path = path; + return this.matchRoute(this.path); + }; + + Route.matchRoute = function(path, options) { + var route, _i, _len, _ref2; + _ref2 = this.routes; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + route = _ref2[_i]; + if (route.match(path, options)) { + this.trigger('change', route, path); + return route; + } + } + }; + + function Route(path, callback) { + var match; + this.path = path; + this.callback = callback; + this.names = []; + if (typeof path === 'string') { + namedParam.lastIndex = 0; + while ((match = namedParam.exec(path)) !== null) { + this.names.push(match[1]); + } + path = path.replace(escapeRegExp, '\\$&').replace(namedParam, '([^\/]*)').replace(splatParam, '(.*?)'); + this.route = new RegExp('^' + path + '$'); + } else { + this.route = path; + } + } + + Route.prototype.match = function(path, options) { + var i, match, param, params, _len; + if (options == null) options = {}; + match = this.route.exec(path); + if (!match) return false; + options.match = match; + params = match.slice(1); + if (this.names.length) { + for (i = 0, _len = params.length; i < _len; i++) { + param = params[i]; + options[this.names[i]] = param; + } + } + return this.callback.call(null, options) !== false; + }; + + return Route; + + })(Spine.Module); + + Spine.Route.change = Spine.Route.proxy(Spine.Route.change); + + Spine.Controller.include({ + route: function(path, callback) { + return Spine.Route.add(path, this.proxy(callback)); + }, + routes: function(routes) { + var key, value, _results; + _results = []; + for (key in routes) { + value = routes[key]; + _results.push(this.route(key, value)); + } + return _results; + }, + navigate: function() { + return Spine.Route.navigate.apply(Spine.Route, arguments); + } + }); + + if (typeof module !== "undefined" && module !== null) { + module.exports = Spine.Route; + } + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/spine.coffee b/app/assets/javascripts/app/lib/spine/spine.coffee deleted file mode 100644 index fef91637c..000000000 --- a/app/assets/javascripts/app/lib/spine/spine.coffee +++ /dev/null @@ -1,528 +0,0 @@ -Events = - bind: (ev, callback) -> - evs = ev.split(' ') - calls = @hasOwnProperty('_callbacks') and @_callbacks or= {} - - for name in evs - calls[name] or= [] - calls[name].push(callback) - this - - one: (ev, callback) -> - @bind ev, -> - @unbind(ev, arguments.callee) - callback.apply(@, arguments) - - trigger: (args...) -> - ev = args.shift() - - list = @hasOwnProperty('_callbacks') and @_callbacks?[ev] - return unless list - - for callback in list - if callback.apply(@, args) is false - break - true - - unbind: (ev, callback) -> - unless ev - @_callbacks = {} - return this - - list = @_callbacks?[ev] - return this unless list - - unless callback - delete @_callbacks[ev] - return this - - for cb, i in list when cb is callback - list = list.slice() - list.splice(i, 1) - @_callbacks[ev] = list - break - this - -Log = - trace: true - - logPrefix: '(App)' - - log: (args...) -> - return unless @trace - if @logPrefix then args.unshift(@logPrefix) - console?.log?(args...) - this - -moduleKeywords = ['included', 'extended'] - -class Module - @include: (obj) -> - throw('include(obj) requires obj') unless obj - for key, value of obj when key not in moduleKeywords - @::[key] = value - obj.included?.apply(@) - this - - @extend: (obj) -> - throw('extend(obj) requires obj') unless obj - for key, value of obj when key not in moduleKeywords - @[key] = value - obj.extended?.apply(@) - this - - @proxy: (func) -> - => func.apply(@, arguments) - - proxy: (func) -> - => func.apply(@, arguments) - - constructor: -> - @init?(arguments...) - -class Model extends Module - @extend Events - - @records: {} - @crecords: {} - @attributes: [] - - @configure: (name, attributes...) -> - @className = name - @records = {} - @crecords = {} - @attributes = attributes if attributes.length - @attributes and= makeArray(@attributes) - @attributes or= [] - @unbind() - this - - @toString: -> "#{@className}(#{@attributes.join(", ")})" - - @find: (id) -> - record = @records[id] - if !record and ("#{id}").match(/c-\d+/) - return @findCID(id) - throw('Unknown record'+id+@className) unless record - record.clone() - - @findCID: (cid) -> - record = @crecords[cid] - throw('Unknown record') unless record - record.clone() - - @exists: (id) -> - try - return @find(id) - catch e - return false - - @refresh: (values, options = {}) -> - if options.clear - @records = {} - @crecords = {} - - records = @fromJSON(values) - records = [records] unless isArray(records) - - for record in records - record.id or= record.cid - @records[record.id] = record - @crecords[record.cid] = record - - @trigger('refresh', not options.clear and @cloneArray(records)) - this - - @select: (callback) -> - result = (record for id, record of @records when callback(record)) - @cloneArray(result) - - @findByAttribute: (name, value) -> - for id, record of @records - if record[name] is value - return record.clone() - null - - @findAllByAttribute: (name, value) -> - @select (item) -> - item[name] is value - - @each: (callback) -> - for key, value of @records - callback(value.clone()) - - @all: -> - @cloneArray(@recordsValues()) - - @first: -> - record = @recordsValues()[0] - record?.clone() - - @last: -> - values = @recordsValues() - record = values[values.length - 1] - record?.clone() - - @count: -> - @recordsValues().length - - @deleteAll: -> - for key, value of @records - delete @records[key] - - @destroyAll: -> - for key, value of @records - @records[key].destroy() - - @update: (id, atts, options) -> - @find(id).updateAttributes(atts, options) - - @create: (atts, options) -> - record = new @(atts) - record.save(options) - - @destroy: (id, options) -> - @find(id).destroy(options) - - @change: (callbackOrParams) -> - if typeof callbackOrParams is 'function' - @bind('change', callbackOrParams) - else - @trigger('change', callbackOrParams) - - @fetch: (callbackOrParams) -> - if typeof callbackOrParams is 'function' - @bind('fetch', callbackOrParams) - else - @trigger('fetch', callbackOrParams) - - @toJSON: -> - @recordsValues() - - @fromJSON: (objects) -> - return unless objects - if typeof objects is 'string' - objects = JSON.parse(objects) - if isArray(objects) - (new @(value) for value in objects) - else - new @(objects) - - @fromForm: -> - (new this).fromForm(arguments...) - - # Private - - @recordsValues: -> - result = [] - for key, value of @records - result.push(value) - result - - @cloneArray: (array) -> - (value.clone() for value in array) - - @idCounter: 0 - - @uid: -> - @idCounter++ - - # Instance - - constructor: (atts) -> - super - @load atts if atts - @cid or= 'c-' + @constructor.uid() - - isNew: -> - not @exists() - - isValid: -> - not @validate() - - validate: -> - - load: (atts) -> - for key, value of atts - if typeof @[key] is 'function' - @[key](value) - else - @[key] = value - this - - attributes: -> - result = {} - for key in @constructor.attributes when key of this - if typeof @[key] is 'function' - result[key] = @[key]() - else - result[key] = @[key] - result.id = @id if @id - result - - eql: (rec) -> - !!(rec and rec.constructor is @constructor and - ((rec.id and rec.id is @id) or rec.cid is @cid)) - - save: (options = {}) -> - unless options.validate is false - error = @validate() - if error - @trigger('error', error) - return false - - @trigger('beforeSave', options) - record = if @isNew() then @create(options) else @update(options) - @trigger('save', options) - record - - updateAttribute: (name, value) -> - @[name] = value - @save() - - updateAttributes: (atts, options) -> - @load(atts) - @save(options) - - changeID: (id) -> - records = @constructor.records - records[id] = records[@id] - delete records[@id] - @id = id - @save() - - destroy: (options = {}) -> - @trigger('beforeDestroy', options) - delete @constructor.records[@id] - delete @constructor.crecords[@cid] - @destroyed = true - @trigger('destroy', options) - @trigger('change', 'destroy', options) - @unbind() - this - - dup: (newRecord) -> - result = new @constructor(@attributes()) - if newRecord is false - result.cid = @cid - else - delete result.id - result - - clone: -> - Object.create(@) - - reload: -> - return this if @isNew() - original = @constructor.find(@id) - @load(original.attributes()) - original - - toJSON: -> - @attributes() - - toString: -> - "<#{@constructor.className} (#{JSON.stringify(@)})>" - - fromForm: (form) -> - result = {} - for key in $(form).serializeArray() - result[key.name] = key.value - @load(result) - - exists: -> - @id && @id of @constructor.records - - # Private - - update: (options) -> - @trigger('beforeUpdate', options) - records = @constructor.records - records[@id].load @attributes() - clone = records[@id].clone() - clone.trigger('update', options) - clone.trigger('change', 'update', options) - clone - - create: (options) -> - @trigger('beforeCreate', options) - @id = @cid unless @id - - record = @dup(false) - @constructor.records[@id] = record - @constructor.crecords[@cid] = record - - clone = record.clone() - clone.trigger('create', options) - clone.trigger('change', 'create', options) - clone - - bind: (events, callback) -> - @constructor.bind events, binder = (record) => - if record && @eql(record) - callback.apply(@, arguments) - @constructor.bind 'unbind', unbinder = (record) => - if record && @eql(record) - @constructor.unbind(events, binder) - @constructor.unbind('unbind', unbinder) - binder - - one: (events, callback) -> - binder = @bind events, => - @constructor.unbind(events, binder) - callback.apply(@) - - trigger: (args...) -> - args.splice(1, 0, @) - @constructor.trigger(args...) - - unbind: -> - @trigger('unbind') - -class Controller extends Module - @include Events - @include Log - - eventSplitter: /^(\S+)\s*(.*)$/ - tag: 'div' - - constructor: (options) -> - @options = options - - for key, value of @options - @[key] = value - - @el = document.createElement(@tag) unless @el - @el = $(@el) - - @el.addClass(@className) if @className - @el.attr(@attributes) if @attributes - - @release -> @el.remove() - - @events = @constructor.events unless @events - @elements = @constructor.elements unless @elements - - @delegateEvents() if @events - @refreshElements() if @elements - - super - - release: (callback) => - if typeof callback is 'function' - @bind 'release', callback - else - @trigger 'release' - - $: (selector) -> $(selector, @el) - - delegateEvents: -> - for key, method of @events - unless typeof(method) is 'function' - method = @proxy(@[method]) - - match = key.match(@eventSplitter) - eventName = match[1] - selector = match[2] - - if selector is '' - @el.bind(eventName, method) - else - @el.delegate(selector, eventName, method) - - refreshElements: -> - for key, value of @elements - @[value] = @$(key) - - delay: (func, timeout) -> - setTimeout(@proxy(func), timeout || 0) - - html: (element) -> - @el.html(element.el or element) - @refreshElements() - @el - - append: (elements...) -> - elements = (e.el or e for e in elements) - @el.append(elements...) - @refreshElements() - @el - - appendTo: (element) -> - @el.appendTo(element.el or element) - @refreshElements() - @el - - prepend: (elements...) -> - elements = (e.el or e for e in elements) - @el.prepend(elements...) - @refreshElements() - @el - - replace: (element) -> - [previous, @el] = [@el, $(element.el or element)] - previous.replaceWith(@el) - @delegateEvents() - @refreshElements() - @el - -# Utilities & Shims - -$ = window?.jQuery or window?.Zepto or (element) -> element - -unless typeof Object.create is 'function' - Object.create = (o) -> - Func = -> - Func.prototype = o - new Func() - -isArray = (value) -> - Object::toString.call(value) is '[object Array]' - -isBlank = (value) -> - return true unless value - return false for key of value - true - -makeArray = (args) -> - Array.prototype.slice.call(args, 0) - -# Globals - -Spine = @Spine = {} -module?.exports = Spine - -Spine.version = '1.0.6' -Spine.isArray = isArray -Spine.isBlank = isBlank -Spine.$ = $ -Spine.Events = Events -Spine.Log = Log -Spine.Module = Module -Spine.Controller = Controller -Spine.Model = Model - -# Global events - -Module.extend.call(Spine, Events) - -# JavaScript compatability - -Module.create = Module.sub = - Controller.create = Controller.sub = - Model.sub = (instances, statics) -> - class result extends this - result.include(instances) if instances - result.extend(statics) if statics - result.unbind?() - result - -Model.setup = (name, attributes = []) -> - class Instance extends this - Instance.configure(name, attributes...) - Instance - -Module.init = Controller.init = Model.init = (a1, a2, a3, a4, a5) -> - new this(a1, a2, a3, a4, a5) - -Spine.Class = Module diff --git a/app/assets/javascripts/app/lib/spine/spine.js b/app/assets/javascripts/app/lib/spine/spine.js new file mode 100755 index 000000000..699512599 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/spine.js @@ -0,0 +1,808 @@ +(function() { + var $, Controller, Events, Log, Model, Module, Spine, isArray, isBlank, makeArray, moduleKeywords, + __slice = Array.prototype.slice, + __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, + __hasProp = Object.prototype.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; }, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + Events = { + bind: function(ev, callback) { + var calls, evs, name, _i, _len; + evs = ev.split(' '); + calls = this.hasOwnProperty('_callbacks') && this._callbacks || (this._callbacks = {}); + for (_i = 0, _len = evs.length; _i < _len; _i++) { + name = evs[_i]; + calls[name] || (calls[name] = []); + calls[name].push(callback); + } + return this; + }, + one: function(ev, callback) { + return this.bind(ev, function() { + this.unbind(ev, arguments.callee); + return callback.apply(this, arguments); + }); + }, + trigger: function() { + var args, callback, ev, list, _i, _len, _ref; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + ev = args.shift(); + list = this.hasOwnProperty('_callbacks') && ((_ref = this._callbacks) != null ? _ref[ev] : void 0); + if (!list) return; + for (_i = 0, _len = list.length; _i < _len; _i++) { + callback = list[_i]; + if (callback.apply(this, args) === false) break; + } + return true; + }, + unbind: function(ev, callback) { + var cb, i, list, _len, _ref; + if (!ev) { + this._callbacks = {}; + return this; + } + list = (_ref = this._callbacks) != null ? _ref[ev] : void 0; + if (!list) return this; + if (!callback) { + delete this._callbacks[ev]; + return this; + } + for (i = 0, _len = list.length; i < _len; i++) { + cb = list[i]; + if (!(cb === callback)) continue; + list = list.slice(); + list.splice(i, 1); + this._callbacks[ev] = list; + break; + } + return this; + } + }; + + Log = { + trace: true, + logPrefix: '(App)', + log: function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (!this.trace) return; + if (this.logPrefix) args.unshift(this.logPrefix); + if (typeof console !== "undefined" && console !== null) { + if (typeof console.log === "function") console.log.apply(console, args); + } + return this; + } + }; + + moduleKeywords = ['included', 'extended']; + + Module = (function() { + + Module.include = function(obj) { + var key, value, _ref; + if (!obj) throw 'include(obj) requires obj'; + for (key in obj) { + value = obj[key]; + if (__indexOf.call(moduleKeywords, key) < 0) this.prototype[key] = value; + } + if ((_ref = obj.included) != null) _ref.apply(this); + return this; + }; + + Module.extend = function(obj) { + var key, value, _ref; + if (!obj) throw 'extend(obj) requires obj'; + for (key in obj) { + value = obj[key]; + if (__indexOf.call(moduleKeywords, key) < 0) this[key] = value; + } + if ((_ref = obj.extended) != null) _ref.apply(this); + return this; + }; + + Module.proxy = function(func) { + var _this = this; + return function() { + return func.apply(_this, arguments); + }; + }; + + Module.prototype.proxy = function(func) { + var _this = this; + return function() { + return func.apply(_this, arguments); + }; + }; + + function Module() { + if (typeof this.init === "function") this.init.apply(this, arguments); + } + + return Module; + + })(); + + Model = (function(_super) { + + __extends(Model, _super); + + Model.extend(Events); + + Model.records = {}; + + Model.crecords = {}; + + Model.attributes = []; + + Model.configure = function() { + var attributes, name; + name = arguments[0], attributes = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this.className = name; + this.records = {}; + this.crecords = {}; + if (attributes.length) this.attributes = attributes; + this.attributes && (this.attributes = makeArray(this.attributes)); + this.attributes || (this.attributes = []); + this.unbind(); + return this; + }; + + Model.toString = function() { + return "" + this.className + "(" + (this.attributes.join(", ")) + ")"; + }; + + Model.find = function(id) { + var record; + record = this.records[id]; + if (!record && ("" + id).match(/c-\d+/)) return this.findCID(id); + if (!record) throw 'Unknown record'; + return record.clone(); + }; + + Model.findCID = function(cid) { + var record; + record = this.crecords[cid]; + if (!record) throw 'Unknown record'; + return record.clone(); + }; + + Model.exists = function(id) { + try { + return this.find(id); + } catch (e) { + return false; + } + }; + + Model.refresh = function(values, options) { + var record, records, _i, _len; + if (options == null) options = {}; + if (options.clear) { + this.records = {}; + this.crecords = {}; + } + records = this.fromJSON(values); + if (!isArray(records)) records = [records]; + for (_i = 0, _len = records.length; _i < _len; _i++) { + record = records[_i]; + record.id || (record.id = record.cid); + this.records[record.id] = record; + this.crecords[record.cid] = record; + } + this.trigger('refresh', !options.clear && this.cloneArray(records)); + return this; + }; + + Model.select = function(callback) { + var id, record, result; + result = (function() { + var _ref, _results; + _ref = this.records; + _results = []; + for (id in _ref) { + record = _ref[id]; + if (callback(record)) _results.push(record); + } + return _results; + }).call(this); + return this.cloneArray(result); + }; + + Model.findByAttribute = function(name, value) { + var id, record, _ref; + _ref = this.records; + for (id in _ref) { + record = _ref[id]; + if (record[name] === value) return record.clone(); + } + return null; + }; + + Model.findAllByAttribute = function(name, value) { + return this.select(function(item) { + return item[name] === value; + }); + }; + + Model.each = function(callback) { + var key, value, _ref, _results; + _ref = this.records; + _results = []; + for (key in _ref) { + value = _ref[key]; + _results.push(callback(value.clone())); + } + return _results; + }; + + Model.all = function() { + return this.cloneArray(this.recordsValues()); + }; + + Model.first = function() { + var record; + record = this.recordsValues()[0]; + return record != null ? record.clone() : void 0; + }; + + Model.last = function() { + var record, values; + values = this.recordsValues(); + record = values[values.length - 1]; + return record != null ? record.clone() : void 0; + }; + + Model.count = function() { + return this.recordsValues().length; + }; + + Model.deleteAll = function() { + var key, value, _ref, _results; + _ref = this.records; + _results = []; + for (key in _ref) { + value = _ref[key]; + _results.push(delete this.records[key]); + } + return _results; + }; + + Model.destroyAll = function() { + var key, value, _ref, _results; + _ref = this.records; + _results = []; + for (key in _ref) { + value = _ref[key]; + _results.push(this.records[key].destroy()); + } + return _results; + }; + + Model.update = function(id, atts, options) { + return this.find(id).updateAttributes(atts, options); + }; + + Model.create = function(atts, options) { + var record; + record = new this(atts); + return record.save(options); + }; + + Model.destroy = function(id, options) { + return this.find(id).destroy(options); + }; + + Model.change = function(callbackOrParams) { + if (typeof callbackOrParams === 'function') { + return this.bind('change', callbackOrParams); + } else { + return this.trigger('change', callbackOrParams); + } + }; + + Model.fetch = function(callbackOrParams) { + if (typeof callbackOrParams === 'function') { + return this.bind('fetch', callbackOrParams); + } else { + return this.trigger('fetch', callbackOrParams); + } + }; + + Model.toJSON = function() { + return this.recordsValues(); + }; + + Model.fromJSON = function(objects) { + var value, _i, _len, _results; + if (!objects) return; + if (typeof objects === 'string') objects = JSON.parse(objects); + if (isArray(objects)) { + _results = []; + for (_i = 0, _len = objects.length; _i < _len; _i++) { + value = objects[_i]; + _results.push(new this(value)); + } + return _results; + } else { + return new this(objects); + } + }; + + Model.fromForm = function() { + var _ref; + return (_ref = new this).fromForm.apply(_ref, arguments); + }; + + Model.recordsValues = function() { + var key, result, value, _ref; + result = []; + _ref = this.records; + for (key in _ref) { + value = _ref[key]; + result.push(value); + } + return result; + }; + + Model.cloneArray = function(array) { + var value, _i, _len, _results; + _results = []; + for (_i = 0, _len = array.length; _i < _len; _i++) { + value = array[_i]; + _results.push(value.clone()); + } + return _results; + }; + + Model.idCounter = 0; + + Model.uid = function() { + return this.idCounter++; + }; + + function Model(atts) { + Model.__super__.constructor.apply(this, arguments); + if (atts) this.load(atts); + this.cid || (this.cid = 'c-' + this.constructor.uid()); + } + + Model.prototype.isNew = function() { + return !this.exists(); + }; + + Model.prototype.isValid = function() { + return !this.validate(); + }; + + Model.prototype.validate = function() {}; + + Model.prototype.load = function(atts) { + var key, value; + for (key in atts) { + value = atts[key]; + if (typeof this[key] === 'function') { + this[key](value); + } else { + this[key] = value; + } + } + return this; + }; + + Model.prototype.attributes = function() { + var key, result, _i, _len, _ref; + result = {}; + _ref = this.constructor.attributes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (key in this) { + if (typeof this[key] === 'function') { + result[key] = this[key](); + } else { + result[key] = this[key]; + } + } + } + if (this.id) result.id = this.id; + return result; + }; + + Model.prototype.eql = function(rec) { + return !!(rec && rec.constructor === this.constructor && (rec.id === this.id || rec.cid === this.cid)); + }; + + Model.prototype.save = function(options) { + var error, record; + if (options == null) options = {}; + if (options.validate !== false) { + error = this.validate(); + if (error) { + this.trigger('error', error); + return false; + } + } + this.trigger('beforeSave', options); + record = this.isNew() ? this.create(options) : this.update(options); + this.trigger('save', options); + return record; + }; + + Model.prototype.updateAttribute = function(name, value) { + this[name] = value; + return this.save(); + }; + + Model.prototype.updateAttributes = function(atts, options) { + this.load(atts); + return this.save(options); + }; + + Model.prototype.changeID = function(id) { + var records; + records = this.constructor.records; + records[id] = records[this.id]; + delete records[this.id]; + this.id = id; + return this.save(); + }; + + Model.prototype.destroy = function(options) { + if (options == null) options = {}; + this.trigger('beforeDestroy', options); + delete this.constructor.records[this.id]; + delete this.constructor.crecords[this.cid]; + this.destroyed = true; + this.trigger('destroy', options); + this.trigger('change', 'destroy', options); + this.unbind(); + return this; + }; + + Model.prototype.dup = function(newRecord) { + var result; + result = new this.constructor(this.attributes()); + if (newRecord === false) { + result.cid = this.cid; + } else { + delete result.id; + } + return result; + }; + + Model.prototype.clone = function() { + return Object.create(this); + }; + + Model.prototype.reload = function() { + var original; + if (this.isNew()) return this; + original = this.constructor.find(this.id); + this.load(original.attributes()); + return original; + }; + + Model.prototype.toJSON = function() { + return this.attributes(); + }; + + Model.prototype.toString = function() { + return "<" + this.constructor.className + " (" + (JSON.stringify(this)) + ")>"; + }; + + Model.prototype.fromForm = function(form) { + var key, result, _i, _len, _ref; + result = {}; + _ref = $(form).serializeArray(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + result[key.name] = key.value; + } + return this.load(result); + }; + + Model.prototype.exists = function() { + return this.id && this.id in this.constructor.records; + }; + + Model.prototype.update = function(options) { + var clone, records; + this.trigger('beforeUpdate', options); + records = this.constructor.records; + records[this.id].load(this.attributes()); + clone = records[this.id].clone(); + clone.trigger('update', options); + clone.trigger('change', 'update', options); + return clone; + }; + + Model.prototype.create = function(options) { + var clone, record; + this.trigger('beforeCreate', options); + if (!this.id) this.id = this.cid; + record = this.dup(false); + this.constructor.records[this.id] = record; + this.constructor.crecords[this.cid] = record; + clone = record.clone(); + clone.trigger('create', options); + clone.trigger('change', 'create', options); + return clone; + }; + + Model.prototype.bind = function(events, callback) { + var binder, unbinder, + _this = this; + this.constructor.bind(events, binder = function(record) { + if (record && _this.eql(record)) return callback.apply(_this, arguments); + }); + this.constructor.bind('unbind', unbinder = function(record) { + if (record && _this.eql(record)) { + _this.constructor.unbind(events, binder); + return _this.constructor.unbind('unbind', unbinder); + } + }); + return binder; + }; + + Model.prototype.one = function(events, callback) { + var binder, + _this = this; + return binder = this.bind(events, function() { + _this.constructor.unbind(events, binder); + return callback.apply(_this); + }); + }; + + Model.prototype.trigger = function() { + var args, _ref; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + args.splice(1, 0, this); + return (_ref = this.constructor).trigger.apply(_ref, args); + }; + + Model.prototype.unbind = function() { + return this.trigger('unbind'); + }; + + return Model; + + })(Module); + + Controller = (function(_super) { + + __extends(Controller, _super); + + Controller.include(Events); + + Controller.include(Log); + + Controller.prototype.eventSplitter = /^(\S+)\s*(.*)$/; + + Controller.prototype.tag = 'div'; + + function Controller(options) { + this.release = __bind(this.release, this); + var key, value, _ref; + this.options = options; + _ref = this.options; + for (key in _ref) { + value = _ref[key]; + this[key] = value; + } + if (!this.el) this.el = document.createElement(this.tag); + this.el = $(this.el); + if (this.className) this.el.addClass(this.className); + if (this.attributes) this.el.attr(this.attributes); + this.release(function() { + return this.el.remove(); + }); + if (!this.events) this.events = this.constructor.events; + if (!this.elements) this.elements = this.constructor.elements; + if (this.events) this.delegateEvents(); + if (this.elements) this.refreshElements(); + Controller.__super__.constructor.apply(this, arguments); + } + + Controller.prototype.release = function(callback) { + if (typeof callback === 'function') { + return this.bind('release', callback); + } else { + return this.trigger('release'); + } + }; + + Controller.prototype.$ = function(selector) { + return $(selector, this.el); + }; + + Controller.prototype.delegateEvents = function() { + var eventName, key, match, method, selector, _ref, _results; + _ref = this.events; + _results = []; + for (key in _ref) { + method = _ref[key]; + if (typeof method !== 'function') method = this.proxy(this[method]); + match = key.match(this.eventSplitter); + eventName = match[1]; + selector = match[2]; + if (selector === '') { + _results.push(this.el.bind(eventName, method)); + } else { + _results.push(this.el.delegate(selector, eventName, method)); + } + } + return _results; + }; + + Controller.prototype.refreshElements = function() { + var key, value, _ref, _results; + _ref = this.elements; + _results = []; + for (key in _ref) { + value = _ref[key]; + _results.push(this[value] = this.$(key)); + } + return _results; + }; + + Controller.prototype.delay = function(func, timeout) { + return setTimeout(this.proxy(func), timeout || 0); + }; + + Controller.prototype.html = function(element) { + this.el.html(element.el || element); + this.refreshElements(); + return this.el; + }; + + Controller.prototype.append = function() { + var e, elements, _ref; + elements = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + elements = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + e = elements[_i]; + _results.push(e.el || e); + } + return _results; + })(); + (_ref = this.el).append.apply(_ref, elements); + this.refreshElements(); + return this.el; + }; + + Controller.prototype.appendTo = function(element) { + this.el.appendTo(element.el || element); + this.refreshElements(); + return this.el; + }; + + Controller.prototype.prepend = function() { + var e, elements, _ref; + elements = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + elements = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + e = elements[_i]; + _results.push(e.el || e); + } + return _results; + })(); + (_ref = this.el).prepend.apply(_ref, elements); + this.refreshElements(); + return this.el; + }; + + Controller.prototype.replace = function(element) { + var previous, _ref; + _ref = [this.el, $(element.el || element)], previous = _ref[0], this.el = _ref[1]; + previous.replaceWith(this.el); + this.delegateEvents(); + this.refreshElements(); + return this.el; + }; + + return Controller; + + })(Module); + + $ = (typeof window !== "undefined" && window !== null ? window.jQuery : void 0) || (typeof window !== "undefined" && window !== null ? window.Zepto : void 0) || function(element) { + return element; + }; + + if (typeof Object.create !== 'function') { + Object.create = function(o) { + var Func; + Func = function() {}; + Func.prototype = o; + return new Func(); + }; + } + + isArray = function(value) { + return Object.prototype.toString.call(value) === '[object Array]'; + }; + + isBlank = function(value) { + var key; + if (!value) return true; + for (key in value) { + return false; + } + return true; + }; + + makeArray = function(args) { + return Array.prototype.slice.call(args, 0); + }; + + Spine = this.Spine = {}; + + if (typeof module !== "undefined" && module !== null) module.exports = Spine; + + Spine.version = '1.0.6'; + + Spine.isArray = isArray; + + Spine.isBlank = isBlank; + + Spine.$ = $; + + Spine.Events = Events; + + Spine.Log = Log; + + Spine.Module = Module; + + Spine.Controller = Controller; + + Spine.Model = Model; + + Module.extend.call(Spine, Events); + + Module.create = Module.sub = Controller.create = Controller.sub = Model.sub = function(instances, statics) { + var result; + result = (function(_super) { + + __extends(result, _super); + + function result() { + result.__super__.constructor.apply(this, arguments); + } + + return result; + + })(this); + if (instances) result.include(instances); + if (statics) result.extend(statics); + if (typeof result.unbind === "function") result.unbind(); + return result; + }; + + Model.setup = function(name, attributes) { + var Instance; + if (attributes == null) attributes = []; + Instance = (function(_super) { + + __extends(Instance, _super); + + function Instance() { + Instance.__super__.constructor.apply(this, arguments); + } + + return Instance; + + })(this); + Instance.configure.apply(Instance, [name].concat(__slice.call(attributes))); + return Instance; + }; + + Module.init = Controller.init = Model.init = function(a1, a2, a3, a4, a5) { + return new this(a1, a2, a3, a4, a5); + }; + + Spine.Class = Module; + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/tabs.coffee b/app/assets/javascripts/app/lib/spine/tabs.coffee deleted file mode 100644 index e4210517b..000000000 --- a/app/assets/javascripts/app/lib/spine/tabs.coffee +++ /dev/null @@ -1,36 +0,0 @@ -Spine ?= require('spine') -$ = Spine.$ - -class Spine.Tabs extends Spine.Controller - events: - 'click [data-name]': 'click' - - constructor: -> - super - @bind 'change', @change - - change: (name) => - return unless name - @current = name - @children().removeClass('active') - @children("[data-name=#{@current}]").addClass('active') - - render: -> - @change @current - unless @children('.active').length or @current - @children(':first').click() - - children: (sel) -> - @el.children(sel) - - click: (e) -> - name = $(e.currentTarget).attr('data-name') - @trigger('change', name) - - connect: (tabName, controller) -> - @bind 'change', (name) -> - controller.active() if name is tabName - controller.bind 'active', => - @change tabName - -module?.exports = Spine.Tabs diff --git a/app/assets/javascripts/app/lib/spine/tabs.js b/app/assets/javascripts/app/lib/spine/tabs.js new file mode 100755 index 000000000..13179ba43 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/tabs.js @@ -0,0 +1,66 @@ +(function() { + var $, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = Object.prototype.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; }; + + if (typeof Spine === "undefined" || Spine === null) Spine = require('spine'); + + $ = Spine.$; + + Spine.Tabs = (function(_super) { + + __extends(Tabs, _super); + + Tabs.prototype.events = { + 'click [data-name]': 'click' + }; + + function Tabs() { + this.change = __bind(this.change, this); Tabs.__super__.constructor.apply(this, arguments); + this.bind('change', this.change); + } + + Tabs.prototype.change = function(name) { + if (!name) return; + this.current = name; + this.children().removeClass('active'); + return this.children("[data-name=" + this.current + "]").addClass('active'); + }; + + Tabs.prototype.render = function() { + this.change(this.current); + if (!(this.children('.active').length || this.current)) { + return this.children(':first').click(); + } + }; + + Tabs.prototype.children = function(sel) { + return this.el.children(sel); + }; + + Tabs.prototype.click = function(e) { + var name; + name = $(e.currentTarget).attr('data-name'); + return this.trigger('change', name); + }; + + Tabs.prototype.connect = function(tabName, controller) { + var _this = this; + this.bind('change', function(name) { + if (name === tabName) return controller.active(); + }); + return controller.bind('active', function() { + return _this.change(tabName); + }); + }; + + return Tabs; + + })(Spine.Controller); + + if (typeof module !== "undefined" && module !== null) { + module.exports = Spine.Tabs; + } + +}).call(this); diff --git a/app/assets/javascripts/app/lib/spine/tmpl.coffee b/app/assets/javascripts/app/lib/spine/tmpl.coffee deleted file mode 100644 index 87b746fab..000000000 --- a/app/assets/javascripts/app/lib/spine/tmpl.coffee +++ /dev/null @@ -1,15 +0,0 @@ -# jQuery.tmpl.js utilities - -$ = jQuery ? require("jqueryify") - -$.fn.item = -> - item = $(@) - item = item.data("item") or item.tmplItem?().data - item?.reload?() - item - -$.fn.forItem = (item) -> - @filter -> - compare = $(@).item() - return item.eql?(compare) or item is compare - diff --git a/app/assets/javascripts/app/lib/spine/tmpl.js b/app/assets/javascripts/app/lib/spine/tmpl.js new file mode 100755 index 000000000..cd8253ac6 --- /dev/null +++ b/app/assets/javascripts/app/lib/spine/tmpl.js @@ -0,0 +1,22 @@ +(function() { + var $; + + $ = typeof jQuery !== "undefined" && jQuery !== null ? jQuery : require("jqueryify"); + + $.fn.item = function() { + var item; + item = $(this); + item = item.data("item") || (typeof item.tmplItem === "function" ? item.tmplItem().data : void 0); + if (item != null) if (typeof item.reload === "function") item.reload(); + return item; + }; + + $.fn.forItem = function(item) { + return this.filter(function() { + var compare; + compare = $(this).item(); + return (typeof item.eql === "function" ? item.eql(compare) : void 0) || item === compare; + }); + }; + +}).call(this);