diff --git a/app/assets/javascripts/app/lib/spine/ajax.coffee b/app/assets/javascripts/app/lib/spine/ajax.coffee index 2007f0ae6..a69670c42 100755 --- a/app/assets/javascripts/app/lib/spine/ajax.coffee +++ b/app/assets/javascripts/app/lib/spine/ajax.coffee @@ -9,13 +9,13 @@ Ajax = @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' @@ -60,6 +60,12 @@ Ajax = clearQueue: -> @queue [] + config: + loadMethod: 'GET' + updateMethod: 'PUT' + createMethod: 'POST' + destroyMethod: 'DELETE' + class Base defaults: dataType: 'json' @@ -104,7 +110,7 @@ class Base [promise, statusText, ''] ) promise - + @queue request promise @@ -118,7 +124,7 @@ class Collection extends Base record = new @model(id: id) @ajaxQueue( params, { - type: 'GET' + type: options.method or Ajax.config.loadMethod url: options.url or Ajax.getURL(record) parallel: options.parallel } @@ -128,7 +134,7 @@ class Collection extends Base all: (params, options = {}) -> @ajaxQueue( params, { - type: 'GET' + type: options.method or Ajax.config.loadMethod url: options.url or Ajax.getURL(@model) parallel: options.parallel } @@ -159,7 +165,7 @@ class Singleton extends Base reload: (params, options = {}) -> @ajaxQueue( params, { - type: 'GET' + type: options.method or Ajax.config.loadMethod url: options.url parallel: options.parallel }, @record @@ -169,7 +175,7 @@ class Singleton extends Base create: (params, options = {}) -> @ajaxQueue( params, { - type: 'POST' + type: options.method or Ajax.config.createMethod contentType: 'application/json' data: @record.toJSON() url: options.url or Ajax.getCollectionURL(@record) @@ -181,7 +187,7 @@ class Singleton extends Base update: (params, options = {}) -> @ajaxQueue( params, { - type: 'PUT' + type: options.method or Ajax.config.updateMethod contentType: 'application/json' data: @record.toJSON() url: options.url @@ -193,7 +199,7 @@ class Singleton extends Base destroy: (params, options = {}) -> @ajaxQueue( params, { - type: 'DELETE' + type: options.method or Ajax.config.destroyMethod url: options.url parallel: options.parallel }, @record @@ -233,16 +239,16 @@ GenerateURL = Include = ajax: -> new Singleton(this) - + generateURL: GenerateURL.include - + url: GenerateURL.include - + Extend = ajax: -> new Collection(this) - + generateURL: GenerateURL.extend - + url: GenerateURL.extend Model.Ajax = diff --git a/app/assets/javascripts/app/lib/spine/manager.coffee b/app/assets/javascripts/app/lib/spine/manager.coffee index 3d44e39b0..4206f40c3 100755 --- a/app/assets/javascripts/app/lib/spine/manager.coffee +++ b/app/assets/javascripts/app/lib/spine/manager.coffee @@ -1,5 +1,6 @@ -Spine = @Spine or require('spine') -$ = Spine.$ +Spine = @Spine or require('spine') +$ = Spine.$ + class Spine.Manager extends Spine.Module @include Spine.Events @@ -31,6 +32,7 @@ class Spine.Manager extends Spine.Module current.activate(args...) if current + Spine.Controller.include active: (args...) -> if typeof args[0] is 'function' @@ -45,11 +47,12 @@ Spine.Controller.include activate: -> @el.addClass('active') - @ + this deactivate: -> @el.removeClass('active') - @ + this + class Spine.Stack extends Spine.Controller controllers: {} @@ -61,10 +64,11 @@ class Spine.Stack extends Spine.Controller super @manager = new Spine.Manager + @router = Spine.Route?.create() for key, value of @controllers - throw Error "'@#{ key }' already assigned - choose a different name" if @[key]? - @[key] = new value(stack: @) + throw Error "'@#{ key }' already assigned" if @[key]? + @[key] = new value(stack: this) @add(@[key]) for key, value of @routes @@ -79,5 +83,10 @@ class Spine.Stack extends Spine.Controller @manager.add(controller) @append(controller) -module?.exports = Spine.Manager + release: => + @router?.destroy() + super + + +module?.exports = Spine.Manager module?.exports.Stack = Spine.Stack diff --git a/app/assets/javascripts/app/lib/spine/route.coffee b/app/assets/javascripts/app/lib/spine/route.coffee index 0ecb63275..faef442fa 100755 --- a/app/assets/javascripts/app/lib/spine/route.coffee +++ b/app/assets/javascripts/app/lib/spine/route.coffee @@ -6,13 +6,47 @@ namedParam = /:([\w\d]+)/g splatParam = /\*([\w\d]+)/g escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g -class Spine.Route extends Spine.Module + +class Path extends Spine.Module + + 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 = {}) -> + return false unless match = @route.exec(path) + options.match = match + params = match.slice(1) + + if @names.length + for param, i in params + options[@names[i]] = param + + Route.trigger('before', this) + @callback.call(null, options) isnt false + + +class Route extends Spine.Module @extend Spine.Events @historySupport: window.history?.pushState? - @routes: [] - @options: trigger: true history: false @@ -20,16 +54,12 @@ class Spine.Route extends Spine.Module 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)) + @routers: [] @setup: (options = {}) -> @options = $.extend({}, @options, options) - if (@options.history) + if @options.history @history = @historySupport and @options.history return if @options.shim @@ -65,11 +95,11 @@ class Spine.Route extends Spine.Module @trigger('navigate', @path) - route = @matchRoute(@path, options) if options.trigger + routes = @matchRoutes(@path, options) if options.trigger return if options.shim - if !route + unless routes.length if typeof options.redirect is 'function' return options.redirect.apply this, [@path, options] else @@ -83,6 +113,25 @@ class Spine.Route extends Spine.Module else window.location.hash = @path + @create: -> + router = new this + @routers.push router + router + + @add: (path, callback) -> + #@router ?= new this + @router.add path, callback + + 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(path, callback)) + + destroy: -> + @routes.length = 0 + @constructor.routers = (r for r in @constructor.routers when r isnt this) + # Private @getPath: -> @@ -97,58 +146,43 @@ class Spine.Route extends Spine.Module @getHost: -> "#{window.location.protocol}//#{window.location.host}" - @change: -> + @change: => path = @getPath() return if path is @path @path = path - @matchRoute(@path) + @matchRoutes(@path) - @matchRoute: (path, options) -> - for route in @routes when route.match(path, options) - @trigger('change', route, path) - return route + @matchRoutes: (path, options)-> + matches = [] + for router in @routers.concat [@router] + match = router.matchRoute path, options + matches.push match if match + @trigger('change', matches, path) if matches.length + matches @redirect: (path) -> window.location = path - constructor: (@path, @callback) -> - @names = [] + constructor: -> + @routes = [] - if typeof path is 'string' - namedParam.lastIndex = 0 - while (match = namedParam.exec(path)) != null - @names.push(match[1]) + matchRoute: (path, options) -> + for route in @routes when route.match(path, options) + return route - splatParam.lastIndex = 0 - while (match = splatParam.exec(path)) != null - @names.push(match[1]) + trigger: (args...) -> + args.splice(1, 0, this) + @constructor.trigger(args...) - path = path.replace(escapeRegExp, '\\$&') - .replace(namedParam, '([^\/]*)') - .replace(splatParam, '(.*?)') +Route.router = new Route - @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)) + if @router instanceof Spine.Route + @router.add(path, @proxy(callback)) + else + Spine.Route.add(path, @proxy(callback)) routes: (routes) -> @route(key, value) for key, value of routes @@ -156,4 +190,6 @@ Spine.Controller.include navigate: -> Spine.Route.navigate.apply(Spine.Route, arguments) -module?.exports = Spine.Route +Route.Path = Path +Spine.Route = Route +module?.exports = Route diff --git a/app/assets/javascripts/app/lib/spine/spine.coffee b/app/assets/javascripts/app/lib/spine/spine.coffee index 7c898f181..a6a0b8c1a 100755 --- a/app/assets/javascripts/app/lib/spine/spine.coffee +++ b/app/assets/javascripts/app/lib/spine/spine.coffee @@ -157,21 +157,21 @@ class Model extends Module @exists: (id) -> return if @irecords[id] then true else false - @addRecord: (record) -> + @addRecord: (record, options = {}) -> if record.id and @irecords[record.id] - @irecords[record.id].remove() - + @irecords[record.id].remove(options) + record = @irecords[record.id].load(record) unless options.clear record.id or= record.cid + @irecords[record.id] ?= record + @irecords[record.cid] ?= record @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 + @addRecord(record, options) for record in records @sort() result = @cloneArray(records) @@ -252,8 +252,13 @@ class Model extends Module if typeof objects is 'string' objects = JSON.parse(objects) if isArray(objects) - (new @(value) for value in objects) + for value in objects + if value instanceof this + value + else + new @(value) else + return objects if objects instanceof this new @(objects) @fromForm: -> @@ -298,7 +303,8 @@ class Model extends Module load: (atts) -> if atts.id then @id = atts.id for key, value of atts - if atts.hasOwnProperty(key) and typeof @[key] is 'function' + if typeof @[key] is 'function' + continue if typeof value is 'function' @[key](value) else @[key] = value @@ -354,20 +360,22 @@ class Model extends Module @id = id @save() - remove: -> + remove: (options = {}) -> # 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] + if options.clear + # Remove the ID and CID indexes + delete @constructor.irecords[@id] + delete @constructor.irecords[@cid] destroy: (options = {}) -> + options.clear ?= true @trigger('beforeDestroy', options) - @remove() + @remove(options) @destroyed = true # handle events @trigger('destroy', options) @@ -626,7 +634,7 @@ makeArray = (args) -> Spine = @Spine = {} module?.exports = Spine -Spine.version = '1.3.0' +Spine.version = '1.3.1' Spine.isArray = isArray Spine.isBlank = isBlank Spine.$ = $