codecombat/app/models/SuperModel.coffee

186 lines
4.9 KiB
CoffeeScript
Raw Normal View History

2014-04-09 15:11:59 -04:00
module.exports = class SuperModel extends Backbone.Model
2014-01-03 13:32:13 -05:00
constructor: ->
@num = 0
@denom = 0
@progress = 0
@resources = {}
@rid = 0
2014-01-03 13:32:13 -05:00
@models = {}
@collections = {}
# replace or overwrite
shouldSaveBackups: (model) -> false
# Caching logic
2014-01-03 13:32:13 -05:00
getModel: (ModelClass_or_url, id) ->
return @getModelByURL(ModelClass_or_url) if _.isString(ModelClass_or_url)
m = new ModelClass_or_url(_id: id)
return @getModelByURL(m.url())
getModelByURL: (modelURL) ->
modelURL = modelURL() if _.isFunction(modelURL)
2014-01-03 13:32:13 -05:00
return @models[modelURL] or null
getModelByOriginalAndMajorVersion: (ModelClass, original, majorVersion=0) ->
_.find @models, (m) ->
m.get('original') is original and m.get('version').major is majorVersion and m.constructor.className is ModelClass.className
getModels: (ModelClass) ->
# can't use instanceof. SuperModel gets passed between windows, and one window
# will have different class objects than another window.
# So compare className instead.
return (m for key, m of @models when m.constructor.className is ModelClass.className) if ModelClass
return _.values @models
registerModel: (model) ->
2014-04-16 02:28:59 -04:00
url = model.url
url = model.url() if _.isFunction(model.url)
2014-01-03 13:32:13 -05:00
@models[url] = model
getCollection: (collection) ->
url = collection.url
url = url() if _.isFunction(url)
return @collections[url] or collection
addCollection: (collection) ->
url = collection.url
url = url() if _.isFunction(url)
if @collections[url]?
return console.warn "Tried to add Collection '#{url}' to SuperModel when we already had it."
@collections[url] = collection
# consolidate models
for model, i in collection.models
cachedModel = @getModelByURL(model.url())
if cachedModel
collection.models[i] = cachedModel
else
@registerModel(model)
2014-01-03 13:32:13 -05:00
collection
# New, loading tracking stuff
2014-01-03 13:32:13 -05:00
finished: ->
return @progress is 1.0 or Object.keys(@resources).length is 0
2014-04-09 15:11:59 -04:00
addModelResource: (modelOrCollection, name, fetchOptions, value=1) ->
modelOrCollection.saveBackups = @shouldSaveBackups(modelOrCollection)
2014-04-09 15:11:59 -04:00
@checkName(name)
@registerModel(modelOrCollection)
2014-04-09 15:11:59 -04:00
res = new ModelResource(modelOrCollection, name, fetchOptions, value)
@storeResource(res, value)
2014-04-09 15:11:59 -04:00
return res
addRequestResource: (name, jqxhrOptions, value=1) ->
2014-04-09 15:11:59 -04:00
@checkName(name)
res = new RequestResource(name, jqxhrOptions, value)
@storeResource(res, value)
2014-04-09 15:11:59 -04:00
return res
addSomethingResource: (name, value=1) ->
2014-04-09 15:11:59 -04:00
@checkName(name)
res = new SomethingResource(name, value)
@storeResource(res, value)
2014-04-09 15:11:59 -04:00
return res
checkName: (name) ->
2014-04-09 15:11:59 -04:00
if not name
throw new Error('Resource name should not be empty.')
storeResource: (resource, value) ->
@rid++
resource.rid = @rid
@resources[@rid] = resource
@listenToOnce(resource, 'loaded', @onResourceLoaded)
@listenTo(resource, 'failed', @onResourceFailed)
@denom += value
2014-04-09 15:11:59 -04:00
onResourceLoaded: (r) ->
@num += r.value
_.defer @updateProgress
2014-04-09 15:11:59 -04:00
onResourceFailed: (source) ->
@trigger('failed', source)
2014-04-09 15:11:59 -04:00
updateProgress: =>
# Because this is _.defer'd, this might end up getting called after
# a bunch of things load all at once.
# So make sure we only emit events if @progress has changed.
return if @progress is @num / @denom
@progress = @num / @denom
@trigger('update-progress', @progress)
@trigger('loaded-all') if @finished()
2014-04-09 15:11:59 -04:00
getProgress: -> return @progress
2014-04-09 15:11:59 -04:00
getResource: (rid) ->
return @resources[rid]
2014-04-09 15:11:59 -04:00
class Resource extends Backbone.Model
constructor: (name, value=1) ->
2014-04-09 15:11:59 -04:00
@name = name
@value = value
@rid = -1 # Used for checking state and reloading
2014-04-09 15:11:59 -04:00
@isLoading = false
@isLoaded = false
@model = null
@jqxhr = null
2014-04-09 15:11:59 -04:00
markLoaded: ->
return if @isLoaded
@trigger('loaded', @)
2014-04-09 15:11:59 -04:00
@isLoaded = true
@isLoading = false
markFailed: ->
return if @isLoaded
@trigger('failed', {resource: @})
@isLoaded = @isLoading = false
@isFailed = true
markLoading: ->
@isLoaded = @isFailed = false
@isLoading = true
load: -> @
2014-04-09 15:11:59 -04:00
class ModelResource extends Resource
constructor: (modelOrCollection, name, fetchOptions, value)->
super(name, value)
@model = modelOrCollection
@fetchOptions = fetchOptions
load: ->
@markLoading()
@fetchModel()
@
2014-04-09 15:11:59 -04:00
fetchModel: ->
@jqxhr = @model.fetch(@fetchOptions) unless @model.loading
@listenToOnce @model, 'sync', -> @markLoaded()
@listenToOnce @model, 'error', -> @markFailed()
2014-04-09 15:11:59 -04:00
class RequestResource extends Resource
constructor: (name, jqxhrOptions, value) ->
2014-04-09 15:11:59 -04:00
super(name, value)
@jqxhrOptions = jqxhrOptions
load: ->
@markLoading()
@jqxhr = $.ajax(jqxhrOptions)
@jqxhr.done @markLoaded()
@jqxhr.fail @markFailed()
@
2014-04-09 15:11:59 -04:00
class SomethingResource extends Resource