Updated to spine 1.3.0.
This commit is contained in:
parent
200952f691
commit
0f9aeb62df
37 changed files with 1509 additions and 2515 deletions
|
@ -40,13 +40,13 @@ class App.ControllerGenericNew extends App.ControllerModal
|
|||
# save object
|
||||
ui = @
|
||||
object.save(
|
||||
success: ->
|
||||
done: ->
|
||||
if ui.callback
|
||||
item = App[ ui.genericObject ].retrieve(@id)
|
||||
ui.callback( item )
|
||||
ui.modalHide()
|
||||
|
||||
error: ->
|
||||
fail: ->
|
||||
ui.log 'errors'
|
||||
ui.modalHide()
|
||||
)
|
||||
|
@ -87,13 +87,13 @@ class App.ControllerGenericEdit extends App.ControllerModal
|
|||
# save object
|
||||
ui = @
|
||||
@item.save(
|
||||
success: ->
|
||||
done: ->
|
||||
if ui.callback
|
||||
item = App[ ui.genericObject ].retrieve(@id)
|
||||
ui.callback( item )
|
||||
ui.modalHide()
|
||||
|
||||
error: =>
|
||||
fail: =>
|
||||
ui.log 'errors'
|
||||
ui.modalHide()
|
||||
)
|
||||
|
@ -170,6 +170,7 @@ class App.ControllerGenericIndex extends App.Controller
|
|||
objects: objects
|
||||
overview: overview
|
||||
attributes: attributes
|
||||
groupBy: 'state'
|
||||
)
|
||||
|
||||
binds = {}
|
||||
|
|
|
@ -125,9 +125,9 @@ class App.ChannelEmailFilterEdit extends App.ControllerModal
|
|||
|
||||
# save object
|
||||
object.save(
|
||||
success: =>
|
||||
done: =>
|
||||
@modalHide()
|
||||
error: =>
|
||||
fail: =>
|
||||
@modalHide()
|
||||
)
|
||||
|
||||
|
@ -214,9 +214,9 @@ class App.ChannelEmailAddressEdit extends App.ControllerModal
|
|||
|
||||
# save object
|
||||
object.save(
|
||||
success: =>
|
||||
done: =>
|
||||
@modalHide()
|
||||
error: =>
|
||||
fail: =>
|
||||
@modalHide()
|
||||
)
|
||||
|
||||
|
@ -301,9 +301,9 @@ class App.ChannelEmailSignatureEdit extends App.ControllerModal
|
|||
|
||||
# save object
|
||||
object.save(
|
||||
success: =>
|
||||
done: =>
|
||||
@modalHide()
|
||||
error: =>
|
||||
fail: =>
|
||||
@modalHide()
|
||||
)
|
||||
|
||||
|
@ -425,9 +425,9 @@ class App.ChannelEmailInboundEdit extends App.ControllerModal
|
|||
|
||||
# save object
|
||||
object.save(
|
||||
success: =>
|
||||
done: =>
|
||||
@modalHide()
|
||||
error: =>
|
||||
fail: =>
|
||||
@modalHide()
|
||||
)
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ class Settings extends App.ControllerModal
|
|||
@overview.view['d'] = params['attributes']
|
||||
|
||||
@overview.save(
|
||||
success: =>
|
||||
done: =>
|
||||
if @reload_needed
|
||||
@overview.trigger('local:refetch')
|
||||
else
|
||||
|
|
|
@ -75,7 +75,7 @@ class App.SettingsAreaItem extends App.Controller
|
|||
@setting['state'] = state
|
||||
ui = @
|
||||
@setting.save(
|
||||
success: =>
|
||||
done: =>
|
||||
|
||||
App.Event.trigger 'notify', {
|
||||
type: 'success'
|
||||
|
|
|
@ -282,7 +282,7 @@ class App.TicketCreate extends App.Controller
|
|||
@formDisable(e)
|
||||
ui = @
|
||||
object.save(
|
||||
success: ->
|
||||
done: ->
|
||||
|
||||
# notify UI
|
||||
ui.notify
|
||||
|
@ -306,8 +306,7 @@ class App.TicketCreate extends App.Controller
|
|||
# if not, show start screen
|
||||
ui.navigate "#"
|
||||
|
||||
|
||||
error: ->
|
||||
fail: ->
|
||||
ui.log 'save failed!'
|
||||
ui.formEnable(e)
|
||||
)
|
||||
|
@ -357,7 +356,7 @@ class UserNew extends App.ControllerModal
|
|||
# save user
|
||||
ui = @
|
||||
user.save(
|
||||
success: ->
|
||||
done: ->
|
||||
|
||||
# force to reload object
|
||||
callbackReload = (user) ->
|
||||
|
@ -370,7 +369,7 @@ class UserNew extends App.ControllerModal
|
|||
ui.modalHide()
|
||||
App.User.retrieve( @id, callbackReload , true )
|
||||
|
||||
error: ->
|
||||
fail: ->
|
||||
ui.modalHide()
|
||||
)
|
||||
|
||||
|
|
|
@ -178,12 +178,12 @@ class Index extends App.ControllerContent
|
|||
@formDisable(e)
|
||||
ui = @
|
||||
object.save(
|
||||
success: ->
|
||||
done: ->
|
||||
|
||||
# redirect to zoom
|
||||
ui.navigate '#ticket/zoom/' + this.id
|
||||
|
||||
error: ->
|
||||
fail: ->
|
||||
ui.log 'CustomerTicketCreate', 'error', 'can not create'
|
||||
ui.formEnable(e)
|
||||
)
|
||||
|
|
|
@ -94,7 +94,7 @@ class Index extends App.ControllerContent
|
|||
|
||||
# save user
|
||||
user.save(
|
||||
success: (r) =>
|
||||
done: (r) =>
|
||||
|
||||
if @master_user
|
||||
@master_user = false
|
||||
|
@ -123,7 +123,7 @@ class Index extends App.ControllerContent
|
|||
|
||||
# rerender page
|
||||
@render()
|
||||
error: (data) ->
|
||||
fail: (data) ->
|
||||
|
||||
App.Event.trigger 'notify', {
|
||||
type: 'error'
|
||||
|
|
|
@ -62,7 +62,7 @@ class Index extends App.ControllerContent
|
|||
|
||||
# save user
|
||||
user.save(
|
||||
success: (r) =>
|
||||
done: (r) =>
|
||||
App.Auth.login(
|
||||
data:
|
||||
username: @params.login
|
||||
|
@ -70,7 +70,7 @@ class Index extends App.ControllerContent
|
|||
success: @success
|
||||
error: @error
|
||||
)
|
||||
# error: =>
|
||||
# fail: =>
|
||||
# @modalHide()
|
||||
)
|
||||
|
||||
|
|
|
@ -311,7 +311,7 @@ class Table extends App.ControllerContent
|
|||
|
||||
ticket.load(ticket_update)
|
||||
ticket.save(
|
||||
success: (r) =>
|
||||
done: (r) =>
|
||||
@bulk_count_index++
|
||||
|
||||
# refresh view after all tickets are proceeded
|
||||
|
@ -501,7 +501,7 @@ class Settings extends App.ControllerModal
|
|||
@overview.view[@view_mode] = params['attributes']
|
||||
|
||||
@overview.save(
|
||||
success: =>
|
||||
done: =>
|
||||
if @reload_needed
|
||||
@overview.trigger('local:refetch')
|
||||
else
|
||||
|
|
|
@ -530,17 +530,17 @@ class Edit extends App.Controller
|
|||
return
|
||||
|
||||
ticket.save(
|
||||
success: (r) =>
|
||||
done: (r) =>
|
||||
|
||||
# reset form after save
|
||||
if article
|
||||
article.save(
|
||||
success: (r) =>
|
||||
done: (r) =>
|
||||
@ui.fetch( ticket.id, true )
|
||||
|
||||
# reset form after save
|
||||
App.TaskManager.update( @task_key, { 'state': {} })
|
||||
error: (r) =>
|
||||
fail: (r) =>
|
||||
@log 'error', 'update article', r
|
||||
)
|
||||
else
|
||||
|
|
|
@ -80,10 +80,10 @@ class App.WidgetTemplate extends App.ControllerDrox
|
|||
else
|
||||
ui = @
|
||||
template.save(
|
||||
success: ->
|
||||
done: ->
|
||||
ui.template_id = @.id
|
||||
ui.render()
|
||||
|
||||
error: =>
|
||||
fail: =>
|
||||
@log 'error', 'save failed!'
|
||||
)
|
||||
|
|
|
@ -295,12 +295,12 @@ class App.WidgetTextModuleOld extends App.Controller
|
|||
else
|
||||
ui = @
|
||||
text_module.save(
|
||||
success: ->
|
||||
done: ->
|
||||
ui.el.find('#text_module_name').val('')
|
||||
ui.renderTable()
|
||||
ui.log 'save success!'
|
||||
|
||||
error: ->
|
||||
fail: ->
|
||||
ui.log 'save failed!'
|
||||
)
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ class _taskManagerSingleton extends App.Controller
|
|||
# save new task and update task collection
|
||||
ui = @
|
||||
task.save(
|
||||
success: ->
|
||||
done: ->
|
||||
for taskPosition of ui.allTasks
|
||||
if ui.allTasks[taskPosition] && ui.allTasks[taskPosition]['key'] is @key
|
||||
task = @attributes()
|
||||
|
@ -338,10 +338,10 @@ class _taskManagerSingleton extends App.Controller
|
|||
if taskUpdate.isOnline()
|
||||
ui = @
|
||||
taskUpdate.save(
|
||||
success: ->
|
||||
done: ->
|
||||
if ui.tasksToUpdate[ @key ] is 'inProgress'
|
||||
delete ui.tasksToUpdate[ @key ]
|
||||
error: ->
|
||||
fail: ->
|
||||
ui.log 'error', "can't update task", @
|
||||
if ui.tasksToUpdate[ @key ] is 'inProgress'
|
||||
delete ui.tasksToUpdate[ @key ]
|
||||
|
|
275
app/assets/javascripts/app/lib/spine/ajax.coffee
Executable file
275
app/assets/javascripts/app/lib/spine/ajax.coffee
Executable file
|
@ -0,0 +1,275 @@
|
|||
Spine = @Spine or require('spine')
|
||||
$ = Spine.$
|
||||
Model = Spine.Model
|
||||
Queue = $({})
|
||||
|
||||
Ajax =
|
||||
getURL: (object) ->
|
||||
if object.className?
|
||||
@generateURL(object)
|
||||
else
|
||||
@generateURL(object, encodeURIComponent(object.id))
|
||||
|
||||
getCollectionURL: (object) ->
|
||||
@generateURL(object)
|
||||
|
||||
getScope: (object) ->
|
||||
object.scope?() or object.scope
|
||||
|
||||
getCollection: (object) ->
|
||||
if object.url isnt object.generateURL
|
||||
if typeof object.url is 'function'
|
||||
object.url()
|
||||
else
|
||||
object.url
|
||||
else if object.className?
|
||||
object.className.toLowerCase() + 's'
|
||||
|
||||
generateURL: (object, args...) ->
|
||||
collection = Ajax.getCollection(object) or Ajax.getCollection(object.constructor)
|
||||
scope = Ajax.getScope(object) or Ajax.getScope(object.constructor)
|
||||
args.unshift(collection)
|
||||
args.unshift(scope)
|
||||
# construct and clean url
|
||||
path = args.join('/')
|
||||
path = path.replace /(\/\/)/g, "/"
|
||||
path = path.replace /^\/|\/$/g, ""
|
||||
# handle relative urls vs those that use a host
|
||||
if path.indexOf("../") isnt 0
|
||||
Model.host + "/" + path
|
||||
else
|
||||
path
|
||||
|
||||
enabled: true
|
||||
|
||||
disable: (callback) ->
|
||||
if @enabled
|
||||
@enabled = false
|
||||
try
|
||||
do callback
|
||||
catch e
|
||||
throw e
|
||||
finally
|
||||
@enabled = true
|
||||
else
|
||||
do callback
|
||||
|
||||
queue: (request) ->
|
||||
if request then Queue.queue(request) else Queue.queue()
|
||||
|
||||
clearQueue: ->
|
||||
@queue []
|
||||
|
||||
class Base
|
||||
defaults:
|
||||
dataType: 'json'
|
||||
processData: false
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'}
|
||||
|
||||
queue: Ajax.queue
|
||||
|
||||
ajax: (params, defaults) ->
|
||||
$.ajax @ajaxSettings(params, defaults)
|
||||
|
||||
ajaxQueue: (params, defaults, record) ->
|
||||
jqXHR = null
|
||||
deferred = $.Deferred()
|
||||
promise = deferred.promise()
|
||||
return promise unless Ajax.enabled
|
||||
settings = @ajaxSettings(params, defaults)
|
||||
# prefer setting if exists else default is to parallelize 'GET' requests
|
||||
parallel = if settings.parallel isnt undefined then settings.parallel else (settings.type is 'GET')
|
||||
request = (next) ->
|
||||
if record?.id?
|
||||
# for existing singleton, model id may have been updated
|
||||
# after request has been queued
|
||||
settings.url ?= Ajax.getURL(record)
|
||||
settings.data?.id = record.id
|
||||
# 2 reasons not to stringify: if already a string, or if intend to have ajax processData
|
||||
if typeof settings.data isnt 'string' and settings.processData isnt true
|
||||
settings.data = JSON.stringify(settings.data)
|
||||
jqXHR = $.ajax(settings)
|
||||
.done(deferred.resolve)
|
||||
.fail(deferred.reject)
|
||||
.then(next, next)
|
||||
if parallel
|
||||
Queue.dequeue()
|
||||
|
||||
promise.abort = (statusText) ->
|
||||
return jqXHR.abort(statusText) if jqXHR
|
||||
index = $.inArray(request, @queue())
|
||||
@queue().splice(index, 1) if index > -1
|
||||
deferred.rejectWith(
|
||||
settings.context or settings,
|
||||
[promise, statusText, '']
|
||||
)
|
||||
promise
|
||||
|
||||
@queue request
|
||||
promise
|
||||
|
||||
ajaxSettings: (params, defaults) ->
|
||||
$.extend({}, @defaults, defaults, params)
|
||||
|
||||
class Collection extends Base
|
||||
constructor: (@model) ->
|
||||
|
||||
find: (id, params, options = {}) ->
|
||||
record = new @model(id: id)
|
||||
@ajaxQueue(
|
||||
params, {
|
||||
type: 'GET'
|
||||
url: options.url or Ajax.getURL(record)
|
||||
parallel: options.parallel
|
||||
}
|
||||
).done(@recordsResponse)
|
||||
.fail(@failResponse)
|
||||
|
||||
all: (params, options = {}) ->
|
||||
@ajaxQueue(
|
||||
params, {
|
||||
type: 'GET'
|
||||
url: options.url or Ajax.getURL(@model)
|
||||
parallel: options.parallel
|
||||
}
|
||||
).done(@recordsResponse)
|
||||
.fail(@failResponse)
|
||||
|
||||
fetch: (params = {}, options = {}) ->
|
||||
if id = params.id
|
||||
delete params.id
|
||||
@find(id, params, options).done (record) =>
|
||||
@model.refresh(record, options)
|
||||
else
|
||||
@all(params, options).done (records) =>
|
||||
@model.refresh(records, options)
|
||||
|
||||
# Private
|
||||
|
||||
recordsResponse: (data, status, xhr) =>
|
||||
@model.trigger('ajaxSuccess', null, status, xhr)
|
||||
|
||||
failResponse: (xhr, statusText, error) =>
|
||||
@model.trigger('ajaxError', null, xhr, statusText, error)
|
||||
|
||||
class Singleton extends Base
|
||||
constructor: (@record) ->
|
||||
@model = @record.constructor
|
||||
|
||||
reload: (params, options = {}) ->
|
||||
@ajaxQueue(
|
||||
params, {
|
||||
type: 'GET'
|
||||
url: options.url
|
||||
parallel: options.parallel
|
||||
}, @record
|
||||
).done(@recordResponse(options))
|
||||
.fail(@failResponse(options))
|
||||
|
||||
create: (params, options = {}) ->
|
||||
@ajaxQueue(
|
||||
params, {
|
||||
type: 'POST'
|
||||
contentType: 'application/json'
|
||||
data: @record.toJSON()
|
||||
url: options.url or Ajax.getCollectionURL(@record)
|
||||
parallel: options.parallel
|
||||
}
|
||||
).done(@recordResponse(options))
|
||||
.fail(@failResponse(options))
|
||||
|
||||
update: (params, options = {}) ->
|
||||
@ajaxQueue(
|
||||
params, {
|
||||
type: 'PUT'
|
||||
contentType: 'application/json'
|
||||
data: @record.toJSON()
|
||||
url: options.url
|
||||
parallel: options.parallel
|
||||
}, @record
|
||||
).done(@recordResponse(options))
|
||||
.fail(@failResponse(options))
|
||||
|
||||
destroy: (params, options = {}) ->
|
||||
@ajaxQueue(
|
||||
params, {
|
||||
type: 'DELETE'
|
||||
url: options.url
|
||||
parallel: options.parallel
|
||||
}, @record
|
||||
).done(@recordResponse(options))
|
||||
.fail(@failResponse(options))
|
||||
|
||||
# Private
|
||||
|
||||
recordResponse: (options = {}) =>
|
||||
(data, status, xhr) =>
|
||||
|
||||
Ajax.disable =>
|
||||
unless Spine.isBlank(data) or @record.destroyed
|
||||
# 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.refresh(data)
|
||||
|
||||
@record.trigger('ajaxSuccess', data, status, xhr)
|
||||
options.done?.apply(@record)
|
||||
|
||||
failResponse: (options = {}) =>
|
||||
(xhr, statusText, error) =>
|
||||
@record.trigger('ajaxError', xhr, statusText, error)
|
||||
options.fail?.apply(@record)
|
||||
|
||||
# Ajax endpoint
|
||||
Model.host = ''
|
||||
|
||||
GenerateURL =
|
||||
include: (args...) ->
|
||||
args.unshift(encodeURIComponent(@id))
|
||||
Ajax.generateURL(@, args...)
|
||||
extend: (args...) ->
|
||||
Ajax.generateURL(@, args...)
|
||||
|
||||
Include =
|
||||
ajax: -> new Singleton(this)
|
||||
|
||||
generateURL: GenerateURL.include
|
||||
|
||||
url: GenerateURL.include
|
||||
|
||||
Extend =
|
||||
ajax: -> new Collection(this)
|
||||
|
||||
generateURL: GenerateURL.extend
|
||||
|
||||
url: GenerateURL.extend
|
||||
|
||||
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
|
||||
Ajax.Base = Base
|
||||
Ajax.Singleton = Singleton
|
||||
Ajax.Collection = Collection
|
||||
Spine.Ajax = Ajax
|
||||
module?.exports = Ajax
|
|
@ -1,386 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var $, Ajax, Base, Collection, Extend, Include, Model, Queue, Singleton, Spine,
|
||||
__slice = [].slice,
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
Spine = this.Spine || require('spine');
|
||||
|
||||
$ = Spine.$;
|
||||
|
||||
Model = Spine.Model;
|
||||
|
||||
Queue = $({});
|
||||
|
||||
Ajax = {
|
||||
getURL: function(object) {
|
||||
return (typeof object.url === "function" ? object.url() : void 0) || object.url;
|
||||
},
|
||||
getCollectionURL: function(object) {
|
||||
if (object) {
|
||||
if (typeof object.url === "function") {
|
||||
return this.generateURL(object);
|
||||
} else {
|
||||
return object.url;
|
||||
}
|
||||
}
|
||||
},
|
||||
getScope: function(object) {
|
||||
return (typeof object.scope === "function" ? object.scope() : void 0) || object.scope;
|
||||
},
|
||||
generateURL: function() {
|
||||
var args, collection, object, path, scope;
|
||||
object = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
if (object.className) {
|
||||
collection = object.className.toLowerCase() + 's';
|
||||
scope = Ajax.getScope(object);
|
||||
} else {
|
||||
if (typeof object.constructor.url === 'string') {
|
||||
collection = object.constructor.url;
|
||||
} else {
|
||||
collection = object.constructor.className.toLowerCase() + 's';
|
||||
}
|
||||
scope = Ajax.getScope(object) || Ajax.getScope(object.constructor);
|
||||
}
|
||||
args.unshift(collection);
|
||||
args.unshift(scope);
|
||||
path = args.join('/');
|
||||
path = path.replace(/(\/\/)/g, "/");
|
||||
path = path.replace(/^\/|\/$/g, "");
|
||||
if (path.indexOf("../") !== 0) {
|
||||
return Model.host + "/" + path;
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
},
|
||||
enabled: true,
|
||||
disable: function(callback) {
|
||||
var e;
|
||||
if (this.enabled) {
|
||||
this.enabled = false;
|
||||
try {
|
||||
return callback();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
throw e;
|
||||
} finally {
|
||||
this.enabled = true;
|
||||
}
|
||||
} else {
|
||||
return callback();
|
||||
}
|
||||
},
|
||||
queue: function(request) {
|
||||
if (request) {
|
||||
return Queue.queue(request);
|
||||
} else {
|
||||
return Queue.queue();
|
||||
}
|
||||
},
|
||||
clearQueue: function() {
|
||||
return this.queue([]);
|
||||
}
|
||||
};
|
||||
|
||||
Base = (function() {
|
||||
function Base() {}
|
||||
|
||||
Base.prototype.defaults = {
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
};
|
||||
|
||||
Base.prototype.queue = Ajax.queue;
|
||||
|
||||
Base.prototype.ajax = function(params, defaults) {
|
||||
return $.ajax(this.ajaxSettings(params, defaults));
|
||||
};
|
||||
|
||||
Base.prototype.ajaxQueue = function(params, defaults, record) {
|
||||
var deferred, jqXHR, promise, request, settings;
|
||||
jqXHR = null;
|
||||
deferred = $.Deferred();
|
||||
promise = deferred.promise();
|
||||
if (!Ajax.enabled) {
|
||||
return promise;
|
||||
}
|
||||
settings = this.ajaxSettings(params, defaults);
|
||||
request = function(next) {
|
||||
var _ref;
|
||||
if ((record != null ? record.id : void 0) != null) {
|
||||
if (settings.url == null) {
|
||||
settings.url = Ajax.getURL(record);
|
||||
}
|
||||
if ((_ref = settings.data) != null) {
|
||||
_ref.id = record.id;
|
||||
}
|
||||
}
|
||||
if (typeof settings.data !== 'string' && settings.processData !== true) {
|
||||
settings.data = JSON.stringify(settings.data);
|
||||
}
|
||||
return jqXHR = $.ajax(settings).done(deferred.resolve).fail(deferred.reject).then(next, next);
|
||||
};
|
||||
promise.abort = function(statusText) {
|
||||
var index;
|
||||
if (jqXHR) {
|
||||
return jqXHR.abort(statusText);
|
||||
}
|
||||
index = $.inArray(request, this.queue());
|
||||
if (index > -1) {
|
||||
this.queue().splice(index, 1);
|
||||
}
|
||||
deferred.rejectWith(settings.context || settings, [promise, statusText, '']);
|
||||
return promise;
|
||||
};
|
||||
this.queue(request);
|
||||
return promise;
|
||||
};
|
||||
|
||||
Base.prototype.ajaxSettings = function(params, defaults) {
|
||||
return $.extend({}, this.defaults, defaults, params);
|
||||
};
|
||||
|
||||
return Base;
|
||||
|
||||
})();
|
||||
|
||||
Collection = (function(_super) {
|
||||
__extends(Collection, _super);
|
||||
|
||||
function Collection(model) {
|
||||
this.model = model;
|
||||
this.failResponse = __bind(this.failResponse, this);
|
||||
this.recordsResponse = __bind(this.recordsResponse, this);
|
||||
}
|
||||
|
||||
Collection.prototype.find = function(id, params, options) {
|
||||
var record;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
record = new this.model({
|
||||
id: id
|
||||
});
|
||||
return this.ajaxQueue(params, {
|
||||
type: 'GET',
|
||||
url: options.url || Ajax.getURL(record)
|
||||
}).done(this.recordsResponse).fail(this.failResponse);
|
||||
};
|
||||
|
||||
Collection.prototype.all = function(params, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return this.ajaxQueue(params, {
|
||||
type: 'GET',
|
||||
url: options.url || Ajax.getURL(this.model)
|
||||
}).done(this.recordsResponse).fail(this.failResponse);
|
||||
};
|
||||
|
||||
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, options).done(function(record) {
|
||||
return _this.model.refresh(record, options);
|
||||
});
|
||||
} else {
|
||||
return this.all(params, options).done(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.failResponse = 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.failResponse = __bind(this.failResponse, this);
|
||||
this.recordResponse = __bind(this.recordResponse, this);
|
||||
this.model = this.record.constructor;
|
||||
}
|
||||
|
||||
Singleton.prototype.reload = function(params, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return this.ajaxQueue(params, {
|
||||
type: 'GET',
|
||||
url: options.url
|
||||
}, this.record).done(this.recordResponse(options)).fail(this.failResponse(options));
|
||||
};
|
||||
|
||||
Singleton.prototype.create = function(params, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return this.ajaxQueue(params, {
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: this.record.toJSON(),
|
||||
url: options.url || Ajax.getCollectionURL(this.record)
|
||||
}).done(this.recordResponse(options)).fail(this.failResponse(options));
|
||||
};
|
||||
|
||||
Singleton.prototype.update = function(params, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return this.ajaxQueue(params, {
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: this.record.toJSON(),
|
||||
url: options.url
|
||||
}, this.record).done(this.recordResponse(options)).fail(this.failResponse(options));
|
||||
};
|
||||
|
||||
Singleton.prototype.destroy = function(params, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return this.ajaxQueue(params, {
|
||||
type: 'DELETE',
|
||||
url: options.url
|
||||
}, this.record).done(this.recordResponse(options)).fail(this.failResponse(options));
|
||||
};
|
||||
|
||||
Singleton.prototype.recordResponse = function(options) {
|
||||
var _this = this;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return function(data, status, xhr) {
|
||||
var _ref, _ref1;
|
||||
Ajax.disable(function() {
|
||||
if (!(Spine.isBlank(data) || _this.record.destroyed)) {
|
||||
if (data.id && _this.record.id !== data.id) {
|
||||
_this.record.changeID(data.id);
|
||||
}
|
||||
return _this.record.refresh(data);
|
||||
}
|
||||
});
|
||||
_this.record.trigger('ajaxSuccess', data, status, xhr);
|
||||
if ((_ref = options.success) != null) {
|
||||
_ref.apply(_this.record);
|
||||
}
|
||||
return (_ref1 = options.done) != null ? _ref1.apply(_this.record) : void 0;
|
||||
};
|
||||
};
|
||||
|
||||
Singleton.prototype.failResponse = function(options) {
|
||||
var _this = this;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return function(xhr, statusText, error) {
|
||||
var _ref, _ref1;
|
||||
_this.record.trigger('ajaxError', xhr, statusText, error);
|
||||
if ((_ref = options.error) != null) {
|
||||
_ref.apply(_this.record);
|
||||
}
|
||||
return (_ref1 = options.fail) != null ? _ref1.apply(_this.record) : void 0;
|
||||
};
|
||||
};
|
||||
|
||||
return Singleton;
|
||||
|
||||
})(Base);
|
||||
|
||||
Model.host = '';
|
||||
|
||||
Include = {
|
||||
ajax: function() {
|
||||
return new Singleton(this);
|
||||
},
|
||||
url: function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||||
args.unshift(encodeURIComponent(this.id));
|
||||
return Ajax.generateURL.apply(Ajax, [this].concat(__slice.call(args)));
|
||||
}
|
||||
};
|
||||
|
||||
Extend = {
|
||||
ajax: function() {
|
||||
return new Collection(this);
|
||||
},
|
||||
url: function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||||
return Ajax.generateURL.apply(Ajax, [this].concat(__slice.call(args)));
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
Ajax.Base = Base;
|
||||
|
||||
Ajax.Singleton = Singleton;
|
||||
|
||||
Ajax.Collection = Collection;
|
||||
|
||||
Spine.Ajax = Ajax;
|
||||
|
||||
if (typeof module !== "undefined" && module !== null) {
|
||||
module.exports = Ajax;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
//@ sourceMappingURL=ajax.map
|
||||
*/
|
File diff suppressed because one or more lines are too long
95
app/assets/javascripts/app/lib/spine/bindings.coffee
Executable file
95
app/assets/javascripts/app/lib/spine/bindings.coffee
Executable file
|
@ -0,0 +1,95 @@
|
|||
BindingsClass =
|
||||
|
||||
model: 'model'
|
||||
|
||||
bindings: {}
|
||||
|
||||
|
||||
class ValueSetter
|
||||
|
||||
constructor: (@context) ->
|
||||
|
||||
setValue: (element, value, setter) ->
|
||||
if typeof setter is 'string'
|
||||
setter = @context.proxy(@context[setter])
|
||||
setter = setter || (e, v) => @_standardSetter e, v
|
||||
setter element, value
|
||||
|
||||
getValue: (element, getter) ->
|
||||
if typeof getter is 'string'
|
||||
getter = @context.proxy(@context[getter])
|
||||
getter = getter || (e, v) => @_standardGetter e, v
|
||||
getter element
|
||||
|
||||
_standardGetter: (element) ->
|
||||
self = @
|
||||
self["_#{element.attr("type")}Get"]?(element) || element.val()
|
||||
|
||||
_standardSetter: (element, value) ->
|
||||
self = @
|
||||
element.each ->
|
||||
el = $(this)
|
||||
self["_#{el.attr("type")}Set"]?(el, value) || el.val(value)
|
||||
|
||||
_checkboxSet: (element, value) ->
|
||||
if value
|
||||
element.prop("checked", "checked")
|
||||
else
|
||||
element.prop("checked", "")
|
||||
|
||||
_checkboxGet: (element) ->
|
||||
element.is(":checked")
|
||||
|
||||
BindingsInstance =
|
||||
|
||||
getModel: ->
|
||||
@[@modelVar]
|
||||
|
||||
setModel: (model) ->
|
||||
@[@modelVar] = model
|
||||
|
||||
walkBindings: (fn) ->
|
||||
for selector, field of @bindings
|
||||
fn selector, field
|
||||
|
||||
applyBindings: ->
|
||||
@valueSetter = new ValueSetter @
|
||||
@walkBindings (selector, field) =>
|
||||
if not field.direction or field.direction is 'model'
|
||||
@_bindModelToEl @getModel(), field, selector
|
||||
if not field.direction or field.direction is 'element'
|
||||
@_bindElToModel @getModel(), field, selector
|
||||
|
||||
_getField: (value) ->
|
||||
if typeof value is 'string'
|
||||
value
|
||||
else
|
||||
value.field
|
||||
|
||||
_forceModelBindings: (model) ->
|
||||
@walkBindings (selector, field) =>
|
||||
@valueSetter.setValue @$(selector), model[@_getField(field)], field.setter
|
||||
|
||||
changeBindingSource: (model) ->
|
||||
@getModel().unbind 'change'
|
||||
@walkBindings (selector) =>
|
||||
selector = false if selector is 'self'
|
||||
@el.off 'change', selector
|
||||
@setModel model
|
||||
@_forceModelBindings model
|
||||
do @applyBindings
|
||||
|
||||
_bindModelToEl: (model, field, selector) ->
|
||||
self = @
|
||||
selector = false if selector is 'self'
|
||||
@el.on 'change', selector, ->
|
||||
model[self._getField(field)] = self.valueSetter.getValue $(this), field.getter
|
||||
|
||||
_bindElToModel: (model, field, selector) ->
|
||||
model.bind 'change', =>
|
||||
@valueSetter.setValue @$(selector), model[@_getField(field)], field.setter
|
||||
|
||||
Spine.Bindings =
|
||||
extended: ->
|
||||
@extend BindingsClass
|
||||
@include BindingsInstance
|
47
app/assets/javascripts/app/lib/spine/list.coffee
Executable file
47
app/assets/javascripts/app/lib/spine/list.coffee
Executable file
|
@ -0,0 +1,47 @@
|
|||
Spine = @Spine or require('spine')
|
||||
$ = Spine.$
|
||||
|
||||
class Spine.List extends Spine.Controller
|
||||
events:
|
||||
'click .item': 'click'
|
||||
|
||||
selectFirst: false
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
@bind 'change', @change
|
||||
|
||||
template: ->
|
||||
throw Error 'Override template'
|
||||
|
||||
change: (item) =>
|
||||
@current = item
|
||||
|
||||
unless @current
|
||||
@children().removeClass('active')
|
||||
return
|
||||
|
||||
@children().removeClass('active')
|
||||
for item, idx in @items when item is @current
|
||||
index = idx
|
||||
break
|
||||
|
||||
$(@children().get(index)).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 = @items[$(e.currentTarget).index()]
|
||||
@trigger('change', item)
|
||||
true
|
||||
|
||||
module?.exports = Spine.List
|
|
@ -1,77 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var $, Spine,
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
Spine = this.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() {
|
||||
throw 'Override template';
|
||||
};
|
||||
|
||||
List.prototype.change = function(item) {
|
||||
this.current = item;
|
||||
if (!this.current) {
|
||||
this.children().removeClass('active');
|
||||
return;
|
||||
}
|
||||
this.children().removeClass('active');
|
||||
return $(this.children().get(this.items.indexOf(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 = this.items[$(e.currentTarget).index()];
|
||||
this.trigger('change', item);
|
||||
return true;
|
||||
};
|
||||
|
||||
return List;
|
||||
|
||||
})(Spine.Controller);
|
||||
|
||||
if (typeof module !== "undefined" && module !== null) {
|
||||
module.exports = Spine.List;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
//@ sourceMappingURL=list.map
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "list.js",
|
||||
"sourceRoot": "..",
|
||||
"sources": [
|
||||
"src/list.coffee"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": ";AAAA;CAAA,KAAA,EAAA;KAAA;;oSAAA;;CAAA,CAAA,CAAS,CAAC,CAAV,EAAmB;;CAAnB,CACA,CAAS,EAAK;;CADd,CAGM,GAAK;CACT;;CAAA,EACE,GADF;CACE,CAAe,IAAf,CAAA,MAAA;CADF,KAAA;;CAAA,EAGa,EAHb,MAGA;;CAEa,EAAA,CAAA,UAAA;CACX,sCAAA;CAAA,KAAA,GAAA,8BAAA;CAAA,CACgB,EAAf,EAAD,EAAA;CAPF,IAKa;;CALb,EASU,KAAV,CAAU;CACR,WAAM,OAAN;CAVF,IASU;;CATV,EAYQ,CAAA,EAAR,GAAS;CACP,EAAW,CAAV,EAAD,CAAA;AAEO,CAAP,GAAA,EAAA,CAAA;CACE,GAAC,IAAD,GAAA;CACA,aAAA;QAJF;CAAA,GAMC,EAAD,EAAA,GAAA;CACA,EAAE,CAAC,CAAqB,EAAN,CAAhB,KAAF;CApBF,IAYQ;;CAZR,EAsBQ,EAAA,CAAR,GAAS;CACP,GAAkB,CAAlB,CAAA;CAAA,EAAS,CAAR,CAAD,GAAA;QAAA;CAAA,GACC,CAAK,CAAN,EAAM;CADN,GAEC,EAAD,CAAA;CACA,GAAG,EAAH,KAAA;AACS,CAAP,GAAA,EAAA,EAAA,CAAO;CACJ,GAAA,CAAD,GAAA,SAAA;UAFJ;QAJM;CAtBR,IAsBQ;;CAtBR,EA8BU,KAAV,CAAW;CACR,CAAE,CAAH,CAAC,IAAD,KAAA;CA/BF,IA8BU;;CA9BV,EAiCO,EAAP,IAAQ;CACN,GAAA,MAAA;CAAA,EAAO,CAAP,CAAc,CAAd,OAAc;CAAd,CACmB,EAAlB,EAAD,CAAA,CAAA;CAFK,YAGL;CApCF,IAiCO;;CAjCP;;CADuB,IAAK;;;CAuCtB,EAAU,CAAlB,CAAuB,CAAjB,CAAN;IA1CA;CAAA"
|
||||
}
|
17
app/assets/javascripts/app/lib/spine/local.coffee
Executable file
17
app/assets/javascripts/app/lib/spine/local.coffee
Executable file
|
@ -0,0 +1,17 @@
|
|||
Spine = @Spine or require('spine')
|
||||
|
||||
Spine.Model.Local =
|
||||
extended: ->
|
||||
@change @saveLocal
|
||||
@fetch @loadLocal
|
||||
|
||||
saveLocal: ->
|
||||
result = JSON.stringify(@)
|
||||
localStorage[@className] = result
|
||||
|
||||
loadLocal: (options = {})->
|
||||
options.clear = true unless options.hasOwnProperty('clear')
|
||||
result = localStorage[@className]
|
||||
@refresh(result or [], options)
|
||||
|
||||
module?.exports = Spine.Model.Local
|
|
@ -1,38 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var Spine;
|
||||
|
||||
Spine = this.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(options) {
|
||||
var result;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.hasOwnProperty('clear')) {
|
||||
options.clear = true;
|
||||
}
|
||||
result = localStorage[this.className];
|
||||
return this.refresh(result || [], options);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof module !== "undefined" && module !== null) {
|
||||
module.exports = Spine.Model.Local;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
//@ sourceMappingURL=local.map
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "local.js",
|
||||
"sourceRoot": "..",
|
||||
"sources": [
|
||||
"src/local.coffee"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": ";AAAA;CAAA,IAAA,CAAA;;CAAA,CAAA,CAAQ,CAAC,CAAT,EAAkB;;CAAlB,CAEA,CACE,EADG;CACH,CAAU,CAAA,CAAV,IAAA,CAAU;CACR,GAAC,EAAD,GAAA;CACC,GAAA,CAAD,IAAA,IAAA;CAFF,IAAU;CAAV,CAIW,CAAA,CAAX,KAAA;CACE,KAAA,IAAA;CAAA,EAAS,CAAI,EAAb,GAAS;CACI,EAAc,CAAb,KAAD,GAAA,CAAb;CANF,IAIW;CAJX,CAQW,CAAA,CAAX,GAAW,EAAX;CACE,KAAA,IAAA;;GADoB,KAAV;QACV;AAA4B,CAA5B,GAAA,EAAA,CAAmC,OAAP;CAA5B,EAAgB,CAAhB,CAAA,EAAO,CAAP;QAAA;CAAA,EACS,CAAc,EAAvB,GAAsB,GAAA;CACrB,CAAD,EAAC,EAAQ,CAAT,MAAA;CAXF,IAQW;CAXb,GAAA;;;CAgBQ,EAAU,CAAlB,CAAuB,CAAjB,CAAN;IAhBA;CAAA"
|
||||
}
|
83
app/assets/javascripts/app/lib/spine/manager.coffee
Executable file
83
app/assets/javascripts/app/lib/spine/manager.coffee
Executable file
|
@ -0,0 +1,83 @@
|
|||
Spine = @Spine or 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 = (c for c in @controllers when c isnt controller)
|
||||
|
||||
@controllers.push(controller)
|
||||
|
||||
deactivate: ->
|
||||
@trigger('change', false, arguments...)
|
||||
|
||||
# Private
|
||||
|
||||
change: (current, args...) ->
|
||||
for cont in @controllers when cont isnt current
|
||||
cont.deactivate(args...)
|
||||
|
||||
current.activate(args...) if current
|
||||
|
||||
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
|
||||
|
||||
for key, value of @controllers
|
||||
throw Error "'@#{ key }' already assigned - choose a different name" if @[key]?
|
||||
@[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
|
||||
module?.exports.Stack = Spine.Stack
|
|
@ -1,162 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var $, Spine,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__slice = [].slice;
|
||||
|
||||
Spine = this.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;
|
||||
current = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
_ref = this.controllers;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
cont = _ref[_i];
|
||||
if (cont !== current) {
|
||||
cont.deactivate.apply(cont, args);
|
||||
}
|
||||
}
|
||||
if (current) {
|
||||
return current.activate.apply(current, args);
|
||||
}
|
||||
};
|
||||
|
||||
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, _ref1,
|
||||
_this = this;
|
||||
Stack.__super__.constructor.apply(this, arguments);
|
||||
this.manager = new Spine.Manager;
|
||||
_ref = this.controllers;
|
||||
for (key in _ref) {
|
||||
value = _ref[key];
|
||||
if (this[key] != null) {
|
||||
throw Error("'@" + key + "' already assigned - choose a different name");
|
||||
}
|
||||
this[key] = new value({
|
||||
stack: this
|
||||
});
|
||||
this.add(this[key]);
|
||||
}
|
||||
_ref1 = this.routes;
|
||||
_fn = function(key, value) {
|
||||
var callback;
|
||||
if (typeof value === 'function') {
|
||||
callback = value;
|
||||
}
|
||||
callback || (callback = function() {
|
||||
var _ref2;
|
||||
return (_ref2 = _this[value]).active.apply(_ref2, arguments);
|
||||
});
|
||||
return _this.route(key, callback);
|
||||
};
|
||||
for (key in _ref1) {
|
||||
value = _ref1[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;
|
||||
}
|
||||
|
||||
if (typeof module !== "undefined" && module !== null) {
|
||||
module.exports.Stack = Spine.Stack;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
//@ sourceMappingURL=manager.map
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "manager.js",
|
||||
"sourceRoot": "..",
|
||||
"sources": [
|
||||
"src/manager.coffee"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "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"
|
||||
}
|
137
app/assets/javascripts/app/lib/spine/relation.coffee
Executable file
137
app/assets/javascripts/app/lib/spine/relation.coffee
Executable file
|
@ -0,0 +1,137 @@
|
|||
Spine = @Spine or require('spine')
|
||||
isArray = Spine.isArray
|
||||
require = @require or ((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]
|
||||
|
||||
count: ->
|
||||
@all().length
|
||||
|
||||
find: (id, notFound = @model.notFound) ->
|
||||
records = @select (rec) =>
|
||||
"#{rec.id}" is "#{id}"
|
||||
return records[0] or notFound?(id)
|
||||
|
||||
findAllByAttribute: (name, value) ->
|
||||
@model.select (rec) =>
|
||||
@associated(rec) and rec[name] is value
|
||||
|
||||
findByAttribute: (name, value) ->
|
||||
@findAllByAttribute(name, value)[0]
|
||||
|
||||
select: (cb) ->
|
||||
@model.select (rec) =>
|
||||
@associated(rec) and cb(rec)
|
||||
|
||||
refresh: (values) ->
|
||||
return this unless values?
|
||||
for record in @all()
|
||||
delete @model.irecords[record.id]
|
||||
for match, i in @model.records when match.id is record.id
|
||||
@model.records.splice(i, 1)
|
||||
break
|
||||
values = [values] unless isArray(values)
|
||||
for record in values
|
||||
record.newRecord = false
|
||||
record[@fkey] = @record.id
|
||||
@model.refresh values
|
||||
this
|
||||
|
||||
create: (record, options) ->
|
||||
record[@fkey] = @record.id
|
||||
@model.create(record, options)
|
||||
|
||||
add: (record, options) ->
|
||||
record.updateAttribute @fkey, @record.id, options
|
||||
|
||||
remove: (record, options) ->
|
||||
record.updateAttribute @fkey, null, options
|
||||
|
||||
# Private
|
||||
|
||||
associated: (record) ->
|
||||
record[@fkey] is @record.id
|
||||
|
||||
class Instance extends Spine.Module
|
||||
constructor: (options = {}) ->
|
||||
for key, value of options
|
||||
@[key] = value
|
||||
|
||||
exists: ->
|
||||
return if @record[@fkey] then @model.exists(@record[@fkey]) else false
|
||||
|
||||
find: ->
|
||||
return @model.find(@record[@fkey])
|
||||
|
||||
update: (value) ->
|
||||
return this unless value?
|
||||
unless value instanceof @model
|
||||
value = new @model(value)
|
||||
value.save() if value.isNew()
|
||||
@record[@fkey] = value and value.id
|
||||
this
|
||||
|
||||
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) ->
|
||||
return this unless value?
|
||||
unless value instanceof @model
|
||||
value = @model.fromJSON(value)
|
||||
|
||||
value[@fkey] = @record.id
|
||||
value.save()
|
||||
this
|
||||
|
||||
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()
|
||||
|
||||
association = (name, model, record, fkey, Ctor) ->
|
||||
model = require(model) if typeof model is 'string'
|
||||
new Ctor(name: name, model: model, record: record, fkey: fkey)
|
||||
|
||||
Spine.Model.extend
|
||||
hasMany: (name, model, fkey) ->
|
||||
fkey ?= "#{underscore(this.className)}_id"
|
||||
@::[name] = (value) ->
|
||||
association(name, model, @, fkey, Collection).refresh(value)
|
||||
|
||||
belongsTo: (name, model, fkey) ->
|
||||
fkey ?= "#{underscore(singularize(name))}_id"
|
||||
@::[name] = (value) ->
|
||||
association(name, model, @, fkey, Instance).update(value).find()
|
||||
@attributes.push(fkey)
|
||||
|
||||
hasOne: (name, model, fkey) ->
|
||||
fkey ?= "#{underscore(@className)}_id"
|
||||
@::[name] = (value) ->
|
||||
association(name, model, @, fkey, Singleton).update(value).find()
|
||||
|
||||
Spine.Collection = Collection
|
||||
Spine.Singleton = Singleton
|
||||
Spine.Instance = Instance
|
|
@ -1,264 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var Collection, Instance, Singleton, Spine, association, isArray, require, singularize, underscore,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
Spine = this.Spine || require('spine');
|
||||
|
||||
isArray = Spine.isArray;
|
||||
|
||||
require = this.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.count = function() {
|
||||
return this.all().length;
|
||||
};
|
||||
|
||||
Collection.prototype.find = function(id) {
|
||||
var records,
|
||||
_this = this;
|
||||
records = this.select(function(rec) {
|
||||
return ("" + rec.id) === ("" + id);
|
||||
});
|
||||
if (!records[0]) {
|
||||
throw new Error("\"" + this.model.className + "\" model could not find a record for the ID \"" + id + "\"");
|
||||
}
|
||||
return records[0];
|
||||
};
|
||||
|
||||
Collection.prototype.findAllByAttribute = function(name, value) {
|
||||
var _this = this;
|
||||
return this.model.select(function(rec) {
|
||||
return _this.associated(rec) && 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 i, match, record, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
|
||||
if (values == null) {
|
||||
return this;
|
||||
}
|
||||
_ref = this.all();
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
record = _ref[_i];
|
||||
delete this.model.irecords[record.id];
|
||||
_ref1 = this.model.records;
|
||||
for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) {
|
||||
match = _ref1[i];
|
||||
if (!(match.id === record.id)) {
|
||||
continue;
|
||||
}
|
||||
this.model.records.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isArray(values)) {
|
||||
values = [values];
|
||||
}
|
||||
for (_k = 0, _len2 = values.length; _k < _len2; _k++) {
|
||||
record = values[_k];
|
||||
record.newRecord = false;
|
||||
record[this.fkey] = this.record.id;
|
||||
}
|
||||
this.model.refresh(values);
|
||||
return this;
|
||||
};
|
||||
|
||||
Collection.prototype.create = function(record, options) {
|
||||
record[this.fkey] = this.record.id;
|
||||
return this.model.create(record, options);
|
||||
};
|
||||
|
||||
Collection.prototype.add = function(record, options) {
|
||||
return record.updateAttribute(this.fkey, this.record.id, options);
|
||||
};
|
||||
|
||||
Collection.prototype.remove = function(record, options) {
|
||||
return record.updateAttribute(this.fkey, null, options);
|
||||
};
|
||||
|
||||
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() {
|
||||
if (this.record[this.fkey]) {
|
||||
return this.model.exists(this.record[this.fkey]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Instance.prototype.update = function(value) {
|
||||
if (value == null) {
|
||||
return this;
|
||||
}
|
||||
if (!(value instanceof this.model)) {
|
||||
value = new this.model(value);
|
||||
}
|
||||
if (value.isNew()) {
|
||||
value.save();
|
||||
}
|
||||
this.record[this.fkey] = value && value.id;
|
||||
return this;
|
||||
};
|
||||
|
||||
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 == null) {
|
||||
return this;
|
||||
}
|
||||
if (!(value instanceof this.model)) {
|
||||
value = this.model.fromJSON(value);
|
||||
}
|
||||
value[this.fkey] = this.record.id;
|
||||
value.save();
|
||||
return this;
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
association = function(name, model, record, fkey, Ctor) {
|
||||
if (typeof model === 'string') {
|
||||
model = require(model);
|
||||
}
|
||||
return new Ctor({
|
||||
name: name,
|
||||
model: model,
|
||||
record: record,
|
||||
fkey: fkey
|
||||
});
|
||||
};
|
||||
|
||||
Spine.Model.extend({
|
||||
hasMany: function(name, model, fkey) {
|
||||
if (fkey == null) {
|
||||
fkey = "" + (underscore(this.className)) + "_id";
|
||||
}
|
||||
return this.prototype[name] = function(value) {
|
||||
return association(name, model, this, fkey, Collection).refresh(value);
|
||||
};
|
||||
},
|
||||
belongsTo: function(name, model, fkey) {
|
||||
if (fkey == null) {
|
||||
fkey = "" + (underscore(singularize(name))) + "_id";
|
||||
}
|
||||
this.prototype[name] = function(value) {
|
||||
return association(name, model, this, fkey, Instance).update(value).exists();
|
||||
};
|
||||
return this.attributes.push(fkey);
|
||||
},
|
||||
hasOne: function(name, model, fkey) {
|
||||
if (fkey == null) {
|
||||
fkey = "" + (underscore(this.className)) + "_id";
|
||||
}
|
||||
return this.prototype[name] = function(value) {
|
||||
return association(name, model, this, fkey, Singleton).update(value).find();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Spine.Collection = Collection;
|
||||
|
||||
Spine.Singleton = Singleton;
|
||||
|
||||
Spine.Instance = Instance;
|
||||
|
||||
}).call(this);
|
||||
|
||||
/*
|
||||
//@ sourceMappingURL=relation.map
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "relation.js",
|
||||
"sourceRoot": "..",
|
||||
"sources": [
|
||||
"src/relation.coffee"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "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"
|
||||
}
|
159
app/assets/javascripts/app/lib/spine/route.coffee
Executable file
159
app/assets/javascripts/app/lib/spine/route.coffee
Executable file
|
@ -0,0 +1,159 @@
|
|||
Spine = @Spine or 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
|
||||
replace: false
|
||||
redirect: 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 and @options.history
|
||||
|
||||
return if @options.shim
|
||||
|
||||
if @history
|
||||
$(window).bind('popstate', @change)
|
||||
else
|
||||
$(window).bind('hashchange', @change)
|
||||
@change()
|
||||
|
||||
@unbind: ->
|
||||
return if @options.shim
|
||||
|
||||
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)
|
||||
|
||||
route = @matchRoute(@path, options) if options.trigger
|
||||
|
||||
return if options.shim
|
||||
|
||||
if !route
|
||||
if typeof options.redirect is 'function'
|
||||
return options.redirect.apply this, [@path, options]
|
||||
else
|
||||
if options.redirect is true
|
||||
@redirect(@path)
|
||||
|
||||
if @history and options.replace
|
||||
history.replaceState({}, document.title, @path)
|
||||
else if @history
|
||||
history.pushState({}, document.title, @path)
|
||||
else
|
||||
window.location.hash = @path
|
||||
|
||||
# Private
|
||||
|
||||
@getPath: ->
|
||||
if @history
|
||||
path = window.location.pathname
|
||||
path = '/' + path if path.substr(0,1) isnt '/'
|
||||
else
|
||||
path = window.location.hash
|
||||
path = path.replace(hashStrip, '')
|
||||
path
|
||||
|
||||
@getHost: ->
|
||||
"#{window.location.protocol}//#{window.location.host}"
|
||||
|
||||
@change: ->
|
||||
path = @getPath()
|
||||
return if path is @path
|
||||
@path = path
|
||||
@matchRoute(@path)
|
||||
|
||||
@matchRoute: (path, options) ->
|
||||
for route in @routes when route.match(path, options)
|
||||
@trigger('change', route, path)
|
||||
return route
|
||||
|
||||
@redirect: (path) ->
|
||||
window.location = path
|
||||
|
||||
constructor: (@path, @callback) ->
|
||||
@names = []
|
||||
|
||||
if typeof path is 'string'
|
||||
namedParam.lastIndex = 0
|
||||
while (match = namedParam.exec(path)) != null
|
||||
@names.push(match[1])
|
||||
|
||||
splatParam.lastIndex = 0
|
||||
while (match = splatParam.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
|
||||
@.constructor.trigger('before', @)
|
||||
@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
|
|
@ -1,242 +0,0 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var $, Spine, escapeRegExp, hashStrip, namedParam, splatParam,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__slice = [].slice;
|
||||
|
||||
Spine = this.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,
|
||||
replace: false,
|
||||
redirect: 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.options.shim) {
|
||||
return;
|
||||
}
|
||||
if (this.history) {
|
||||
return $(window).unbind('popstate', this.change);
|
||||
} else {
|
||||
return $(window).unbind('hashchange', this.change);
|
||||
}
|
||||
};
|
||||
|
||||
Route.navigate = function() {
|
||||
var args, lastArg, options, path, route;
|
||||
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) {
|
||||
route = this.matchRoute(this.path, options);
|
||||
}
|
||||
if (options.shim) {
|
||||
return;
|
||||
}
|
||||
if (!route) {
|
||||
if (typeof options.redirect === 'function') {
|
||||
return options.redirect.apply(this, [this.path, options]);
|
||||
} else {
|
||||
if (options.redirect === true) {
|
||||
this.redirect(this.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.history && options.replace) {
|
||||
return history.replaceState({}, document.title, this.path);
|
||||
} else if (this.history) {
|
||||
return history.pushState({}, document.title, this.path);
|
||||
} else {
|
||||
return window.location.hash = this.path;
|
||||
}
|
||||
};
|
||||
|
||||
Route.getPath = function() {
|
||||
var path;
|
||||
if (this.history) {
|
||||
path = window.location.pathname;
|
||||
if (path.substr(0, 1) !== '/') {
|
||||
path = '/' + path;
|
||||
}
|
||||
} else {
|
||||
path = window.location.hash;
|
||||
path = path.replace(hashStrip, '');
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
Route.getHost = function() {
|
||||
return "" + window.location.protocol + "//" + window.location.host;
|
||||
};
|
||||
|
||||
Route.change = function() {
|
||||
var path;
|
||||
path = this.getPath();
|
||||
if (path === this.path) {
|
||||
return;
|
||||
}
|
||||
this.path = path;
|
||||
return this.matchRoute(this.path);
|
||||
};
|
||||
|
||||
Route.matchRoute = function(path, options) {
|
||||
var route, _i, _len, _ref1;
|
||||
_ref1 = this.routes;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
route = _ref1[_i];
|
||||
if (!(route.match(path, options))) {
|
||||
continue;
|
||||
}
|
||||
this.trigger('change', route, path);
|
||||
return route;
|
||||
}
|
||||
};
|
||||
|
||||
Route.redirect = function(path) {
|
||||
return window.location = path;
|
||||
};
|
||||
|
||||
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]);
|
||||
}
|
||||
splatParam.lastIndex = 0;
|
||||
while ((match = splatParam.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, _i, _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 = _i = 0, _len = params.length; _i < _len; i = ++_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);
|
||||
|
||||
/*
|
||||
//@ sourceMappingURL=route.map
|
||||
*/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "route.js",
|
||||
"sourceRoot": "..",
|
||||
"sources": [
|
||||
"src/route.coffee"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "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"
|
||||
}
|
659
app/assets/javascripts/app/lib/spine/spine.coffee
Executable file
659
app/assets/javascripts/app/lib/spine/spine.coffee
Executable file
|
@ -0,0 +1,659 @@
|
|||
###
|
||||
Spine.js MVC library
|
||||
Released under the MIT License
|
||||
###
|
||||
|
||||
Events =
|
||||
bind: (ev, callback) ->
|
||||
evs = ev.split(' ')
|
||||
@_callbacks = {} unless @hasOwnProperty('_callbacks') and @_callbacks
|
||||
for name in evs
|
||||
@_callbacks[name] or= []
|
||||
@_callbacks[name].push(callback)
|
||||
this
|
||||
|
||||
one: (ev, callback) ->
|
||||
@bind ev, handler = ->
|
||||
@unbind(ev, handler)
|
||||
callback.apply(this, arguments)
|
||||
|
||||
trigger: (args...) ->
|
||||
ev = args.shift()
|
||||
list = @hasOwnProperty('_callbacks') and @_callbacks?[ev]
|
||||
return unless list
|
||||
for callback in list
|
||||
if callback.apply(this, args) is false
|
||||
break
|
||||
true
|
||||
|
||||
listenTo: (obj, ev, callback) ->
|
||||
obj.bind(ev, callback)
|
||||
@listeningTo or= []
|
||||
@listeningTo.push {obj, ev, callback}
|
||||
this
|
||||
|
||||
listenToOnce: (obj, ev, callback) ->
|
||||
listeningToOnce = @listeningToOnce or= []
|
||||
obj.bind ev, handler = ->
|
||||
idx = -1
|
||||
for lt, i in listeningToOnce when lt.obj is obj
|
||||
idx = i if lt.ev is ev and lt.callback is callback
|
||||
obj.unbind(ev, handler)
|
||||
listeningToOnce.splice(idx, 1) unless idx is -1
|
||||
callback.apply(this, arguments)
|
||||
listeningToOnce.push {obj, ev, callback, handler}
|
||||
this
|
||||
|
||||
stopListening: (obj, events, callback) ->
|
||||
if arguments.length is 0
|
||||
for listeningTo in [@listeningTo, @listeningToOnce]
|
||||
continue unless listeningTo
|
||||
for lt in listeningTo
|
||||
lt.obj.unbind(lt.ev, lt.handler or lt.callback)
|
||||
@listeningTo = undefined
|
||||
@listeningToOnce = undefined
|
||||
|
||||
else if obj
|
||||
for listeningTo in [@listeningTo, @listeningToOnce]
|
||||
continue unless listeningTo
|
||||
events = if events then events.split(' ') else [undefined]
|
||||
for ev in events
|
||||
for idx in [listeningTo.length-1..0]
|
||||
lt = listeningTo[idx]
|
||||
continue if callback and (lt.handler or lt.callback) isnt callback
|
||||
if (not ev) or (ev is lt.ev)
|
||||
lt.obj.unbind(lt.ev, lt.handler or lt.callback)
|
||||
listeningTo.splice(idx, 1) unless idx is -1
|
||||
else if ev
|
||||
evts = lt.ev.split(' ')
|
||||
if ev in evts
|
||||
evts = (e for e in evts when e isnt ev)
|
||||
lt.ev = $.trim(evts.join(' '))
|
||||
lt.obj.unbind(ev, lt.handler or lt.callback)
|
||||
|
||||
unbind: (ev, callback) ->
|
||||
if arguments.length is 0
|
||||
@_callbacks = {}
|
||||
return this
|
||||
return this unless ev
|
||||
evs = ev.split(' ')
|
||||
for name in evs
|
||||
list = @_callbacks?[name]
|
||||
continue unless list
|
||||
unless callback
|
||||
delete @_callbacks[name]
|
||||
continue
|
||||
for cb, i in list when (cb is callback)
|
||||
list = list.slice()
|
||||
list.splice(i, 1)
|
||||
@_callbacks[name] = list
|
||||
break
|
||||
this
|
||||
|
||||
Events.on = Events.bind
|
||||
Events.off = Events.unbind
|
||||
|
||||
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 new Error('include(obj) requires obj') unless obj
|
||||
for key, value of obj when key not in moduleKeywords
|
||||
@::[key] = value
|
||||
obj.included?.apply(this)
|
||||
this
|
||||
|
||||
@extend: (obj) ->
|
||||
throw new Error('extend(obj) requires obj') unless obj
|
||||
for key, value of obj when key not in moduleKeywords
|
||||
@[key] = value
|
||||
obj.extended?.apply(this)
|
||||
this
|
||||
|
||||
@proxy: (func) ->
|
||||
=> func.apply(this, arguments)
|
||||
|
||||
proxy: (func) ->
|
||||
=> func.apply(this, arguments)
|
||||
|
||||
constructor: ->
|
||||
@init?(arguments...)
|
||||
|
||||
class Model extends Module
|
||||
@extend Events
|
||||
|
||||
@records : []
|
||||
@irecords : {}
|
||||
@attributes : []
|
||||
|
||||
@configure: (name, attributes...) ->
|
||||
@className = name
|
||||
@deleteAll()
|
||||
@attributes = attributes if attributes.length
|
||||
@attributes and= makeArray(@attributes)
|
||||
@attributes or= []
|
||||
@unbind()
|
||||
this
|
||||
|
||||
@toString: -> "#{@className}(#{@attributes.join(", ")})"
|
||||
|
||||
@find: (id, notFound = @notFound) ->
|
||||
record = @irecords[id]?.clone()
|
||||
return record or notFound?(id)
|
||||
|
||||
@notFound: (id) -> return null
|
||||
|
||||
@exists: (id) ->
|
||||
return if @irecords[id] then true else false
|
||||
|
||||
@addRecord: (record) ->
|
||||
if record.id and @irecords[record.id]
|
||||
@irecords[record.id].remove()
|
||||
|
||||
record.id or= record.cid
|
||||
@records.push(record)
|
||||
@irecords[record.id] = record
|
||||
@irecords[record.cid] = record
|
||||
|
||||
@refresh: (values, options = {}) ->
|
||||
@deleteAll() if options.clear
|
||||
|
||||
records = @fromJSON(values)
|
||||
records = [records] unless isArray(records)
|
||||
@addRecord(record) for record in records
|
||||
@sort()
|
||||
|
||||
result = @cloneArray(records)
|
||||
@trigger('refresh', result, options)
|
||||
result
|
||||
|
||||
@select: (callback) ->
|
||||
(record.clone() for record in @records when callback(record))
|
||||
|
||||
@findByAttribute: (name, value) ->
|
||||
for record in @records
|
||||
if record[name] is value
|
||||
return record.clone()
|
||||
null
|
||||
|
||||
@findAllByAttribute: (name, value) ->
|
||||
@select (item) ->
|
||||
item[name] is value
|
||||
|
||||
@each: (callback) ->
|
||||
callback(record.clone()) for record in @records
|
||||
|
||||
@all: ->
|
||||
@cloneArray(@records)
|
||||
|
||||
@slice: (begin = 0, end)->
|
||||
@cloneArray(@records.slice(begin, end))
|
||||
|
||||
@first: (end = 1)->
|
||||
if end > 1
|
||||
@cloneArray(@records.slice(0, end))
|
||||
else
|
||||
@records[0]?.clone()
|
||||
|
||||
@last: (begin)->
|
||||
if typeof begin is 'number'
|
||||
@cloneArray(@records.slice(-begin))
|
||||
else
|
||||
@records[@records.length - 1]?.clone()
|
||||
|
||||
@count: ->
|
||||
@records.length
|
||||
|
||||
@deleteAll: ->
|
||||
@records = []
|
||||
@irecords = {}
|
||||
|
||||
@destroyAll: (options) ->
|
||||
record.destroy(options) for record in @records
|
||||
|
||||
@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', arguments...)
|
||||
|
||||
@fetch: (callbackOrParams) ->
|
||||
if typeof callbackOrParams is 'function'
|
||||
@bind('fetch', callbackOrParams)
|
||||
else
|
||||
@trigger('fetch', arguments...)
|
||||
|
||||
@toJSON: ->
|
||||
@records
|
||||
|
||||
@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...)
|
||||
|
||||
@sort: ->
|
||||
if @comparator
|
||||
@records.sort @comparator
|
||||
this
|
||||
|
||||
# Private
|
||||
|
||||
@cloneArray: (array) ->
|
||||
(value.clone() for value in array)
|
||||
|
||||
@idCounter: 0
|
||||
|
||||
@uid: (prefix = '') ->
|
||||
uid = prefix + @idCounter++
|
||||
uid = @uid(prefix) if @exists(uid)
|
||||
uid
|
||||
|
||||
# Instance
|
||||
|
||||
constructor: (atts) ->
|
||||
super
|
||||
if @constructor.uuid? and typeof @constructor.uuid is 'function'
|
||||
@cid = @constructor.uuid()
|
||||
@id = @cid unless @id
|
||||
else
|
||||
@cid = atts?.cid or @constructor.uid('c-')
|
||||
@load atts if atts
|
||||
|
||||
isNew: ->
|
||||
not @exists()
|
||||
|
||||
isValid: ->
|
||||
not @validate()
|
||||
|
||||
validate: ->
|
||||
|
||||
load: (atts) ->
|
||||
if atts.id then @id = atts.id
|
||||
for key, value of atts
|
||||
if atts.hasOwnProperty(key) and 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.cid is @cid) or (rec.id and rec.id is @id)))
|
||||
|
||||
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)
|
||||
@stripCloneAttrs()
|
||||
@trigger('save', options)
|
||||
record
|
||||
|
||||
stripCloneAttrs: ->
|
||||
return if @hasOwnProperty 'cid' # Make sure it's not the raw object
|
||||
for own key, value of this
|
||||
delete @[key] if key in @constructor.attributes
|
||||
this
|
||||
|
||||
updateAttribute: (name, value, options) ->
|
||||
atts = {}
|
||||
atts[name] = value
|
||||
@updateAttributes(atts, options)
|
||||
|
||||
updateAttributes: (atts, options) ->
|
||||
@load(atts)
|
||||
@save(options)
|
||||
|
||||
changeID: (id) ->
|
||||
return if id is @id
|
||||
records = @constructor.irecords
|
||||
records[id] = records[@id]
|
||||
delete records[@id] unless @cid is @id
|
||||
@id = id
|
||||
@save()
|
||||
|
||||
remove: ->
|
||||
# Remove record from model
|
||||
records = @constructor.records.slice(0)
|
||||
for record, i in records when @eql(record)
|
||||
records.splice(i, 1)
|
||||
break
|
||||
@constructor.records = records
|
||||
# Remove the ID and CID
|
||||
delete @constructor.irecords[@id]
|
||||
delete @constructor.irecords[@cid]
|
||||
|
||||
destroy: (options = {}) ->
|
||||
@trigger('beforeDestroy', options)
|
||||
@remove()
|
||||
@destroyed = true
|
||||
# handle events
|
||||
@trigger('destroy', options)
|
||||
@trigger('change', 'destroy', options)
|
||||
if @listeningTo
|
||||
@stopListening()
|
||||
@unbind()
|
||||
this
|
||||
|
||||
dup: (newRecord = true) ->
|
||||
atts = @attributes()
|
||||
if newRecord
|
||||
delete atts.id
|
||||
else
|
||||
atts.cid = @cid
|
||||
new @constructor(atts)
|
||||
|
||||
clone: ->
|
||||
createObject(this)
|
||||
|
||||
reload: ->
|
||||
return this if @isNew()
|
||||
original = @constructor.find(@id)
|
||||
@load(original.attributes())
|
||||
original
|
||||
|
||||
refresh: (data) ->
|
||||
# go to the source and load attributes
|
||||
root = @constructor.irecords[@id]
|
||||
root.load(data)
|
||||
@trigger('refresh')
|
||||
@
|
||||
|
||||
toJSON: ->
|
||||
@attributes()
|
||||
|
||||
toString: ->
|
||||
"<#{@constructor.className} (#{JSON.stringify(this)})>"
|
||||
|
||||
fromForm: (form) ->
|
||||
result = {}
|
||||
|
||||
for checkbox in $(form).find('[type=checkbox]:not([value])')
|
||||
result[checkbox.name] = $(checkbox).prop('checked')
|
||||
|
||||
for checkbox in $(form).find('[type=checkbox][name$="[]"]')
|
||||
name = checkbox.name.replace(/\[\]$/, '')
|
||||
result[name] or= []
|
||||
result[name].push checkbox.value if $(checkbox).prop('checked')
|
||||
|
||||
for key in $(form).serializeArray()
|
||||
result[key.name] or= key.value
|
||||
|
||||
@load(result)
|
||||
|
||||
exists: ->
|
||||
@constructor.exists(@id)
|
||||
|
||||
# Private
|
||||
|
||||
update: (options) ->
|
||||
@trigger('beforeUpdate', options)
|
||||
|
||||
records = @constructor.irecords
|
||||
records[@id].load @attributes()
|
||||
|
||||
@constructor.sort()
|
||||
|
||||
clone = records[@id].clone()
|
||||
clone.trigger('update', options)
|
||||
clone.trigger('change', 'update', options)
|
||||
clone
|
||||
|
||||
create: (options) ->
|
||||
@trigger('beforeCreate', options)
|
||||
@id or= @cid
|
||||
|
||||
record = @dup(false)
|
||||
@constructor.addRecord(record)
|
||||
@constructor.sort()
|
||||
|
||||
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(this, arguments)
|
||||
# create a wrapper function to be called with 'unbind' for each event
|
||||
for singleEvent in events.split(' ')
|
||||
do (singleEvent) =>
|
||||
@constructor.bind "unbind", unbinder = (record, event, cb) =>
|
||||
if record && @eql(record)
|
||||
return if event and event isnt singleEvent
|
||||
return if cb and cb isnt callback
|
||||
@constructor.unbind(singleEvent, binder)
|
||||
@constructor.unbind("unbind", unbinder)
|
||||
this
|
||||
|
||||
one: (events, callback) ->
|
||||
@bind events, handler = =>
|
||||
@unbind(events, handler)
|
||||
callback.apply(this, arguments)
|
||||
|
||||
trigger: (args...) ->
|
||||
args.splice(1, 0, this)
|
||||
@constructor.trigger(args...)
|
||||
|
||||
listenTo: -> Events.listenTo.apply @, arguments
|
||||
listenToOnce: -> Events.listenToOnce.apply @, arguments
|
||||
stopListening: -> Events.stopListening.apply @, arguments
|
||||
|
||||
unbind: (events, callback) ->
|
||||
if arguments.length is 0
|
||||
@trigger('unbind')
|
||||
else if events
|
||||
for event in events.split(' ')
|
||||
@trigger('unbind', event, callback)
|
||||
|
||||
Model::on = Model::bind
|
||||
Model::off = Model::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 = @el
|
||||
|
||||
@el.addClass(@className) if @className
|
||||
@el.attr(@attributes) if @attributes
|
||||
|
||||
@events = @constructor.events unless @events
|
||||
@elements = @constructor.elements unless @elements
|
||||
|
||||
context = @
|
||||
while parent_prototype = context.constructor.__super__
|
||||
@events = $.extend({}, parent_prototype.events, @events) if parent_prototype.events
|
||||
@elements = $.extend({}, parent_prototype.elements, @elements) if parent_prototype.elements
|
||||
context = parent_prototype
|
||||
|
||||
@delegateEvents(@events) if @events
|
||||
@refreshElements() if @elements
|
||||
|
||||
super
|
||||
|
||||
release: =>
|
||||
@trigger 'release', this
|
||||
# no need to unDelegateEvents since remove will end up handling that
|
||||
@el.remove()
|
||||
@unbind()
|
||||
@stopListening()
|
||||
|
||||
$: (selector) -> $(selector, @el)
|
||||
|
||||
delegateEvents: (events) ->
|
||||
for key, method of events
|
||||
|
||||
if typeof(method) is 'function'
|
||||
# Always return true from event handlers
|
||||
method = do (method) => =>
|
||||
method.apply(this, arguments)
|
||||
true
|
||||
else
|
||||
unless @[method]
|
||||
throw new Error("#{method} doesn't exist")
|
||||
|
||||
method = do (method) => =>
|
||||
@[method].apply(this, arguments)
|
||||
true
|
||||
|
||||
match = key.match(@eventSplitter)
|
||||
eventName = match[1]
|
||||
selector = match[2]
|
||||
|
||||
if selector is ''
|
||||
@el.bind(eventName, method)
|
||||
else
|
||||
@el.on(eventName, selector, method)
|
||||
|
||||
refreshElements: ->
|
||||
for key, value of @elements
|
||||
@[value] = @$(key)
|
||||
|
||||
delay: (func, timeout) ->
|
||||
setTimeout(@proxy(func), timeout || 0)
|
||||
|
||||
# keep controllers elements obj in sync with it contents
|
||||
|
||||
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) ->
|
||||
element = element.el or element
|
||||
element = $.trim(element) if typeof element is "string"
|
||||
# parseHTML is incompatible with Zepto
|
||||
[previous, @el] = [@el, $($.parseHTML(element)?[0] or element)]
|
||||
previous.replaceWith(@el)
|
||||
@delegateEvents(@events)
|
||||
@refreshElements()
|
||||
@el
|
||||
|
||||
# Utilities & Shims
|
||||
|
||||
$ = window?.jQuery or window?.Zepto or (element) -> element
|
||||
|
||||
createObject = Object.create or (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::slice.call(args, 0)
|
||||
|
||||
# Globals
|
||||
|
||||
Spine = @Spine = {}
|
||||
module?.exports = Spine
|
||||
|
||||
Spine.version = '1.3.0'
|
||||
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
|
||||
|
||||
Spine.Class = Module
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
@ -1,66 +0,0 @@
|
|||
(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);
|
|
@ -1,22 +0,0 @@
|
|||
(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);
|
Loading…
Reference in a new issue