From 16fe28ff5594039369d9f2124f35a6d7df0c98cd Mon Sep 17 00:00:00 2001 From: Ting-Kuan Date: Mon, 7 Apr 2014 16:27:50 -0400 Subject: [PATCH 1/4] Add ResourceManager. --- app/lib/ResourceManager | 159 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 app/lib/ResourceManager diff --git a/app/lib/ResourceManager b/app/lib/ResourceManager new file mode 100644 index 000000000..75620fb3f --- /dev/null +++ b/app/lib/ResourceManager @@ -0,0 +1,159 @@ +module.exports = class ResourceManager extends Backbone.Model + num: 0 + denom: 0 + showing: false + resources:{} # models and collections + progress: 0 + + constructor: ()-> + + addModelResource: (modelOrCollection, name, fetchOptions, value=1)-> + @checkName(name) + res = new ModelResource(modelOrCollection, name, fetchOptions) + storeResource: (name, res, value) + return res + + addRequestResource: (name, jqxhrOptions, value=1)-> + @checkName(name) + res = new RequestResource(name, jqxhrOptions) + storeResource: (name, res, value) + return res + + addSomethingResource: (name, value=1)-> + @checkName(name) + res = new SomethingResource(name) + storeResource: (name, res, value) + return res + + checkName: (name)-> + if name in resources + throw new Error('Resource name has been used.') + + storeResource: (name, resource, value)-> + @resources[name] = res + @listenToOnce(res, 'resource:loaded', @updateProgress) + @listenToOnce(res, 'resource:failed', @onResourceFailed) + @denom += value + + loadResources: ()-> + for name, res of @resources + res.load() + + updateProgress: (r)-> + @num += r.value if r.isLoaded + @progress = if denom then num / denom else 0 + @trigger('resourceManager:updateProgress', @progress) + + onResourceFailed: (r)-> + + getResource: (name)-> + return @resources[name] + + + +class Resource + dependencies: [] + name: null + isLoading: false + isLoaded: false + model: null + + addDependency: (resource)-> + @dependecies.push(resource) + + markLoaded: ()-> + @isLoaded = true + @trigger('resource:loaded', @) if @isLoading + @isLoading = false + + markFailed: ()-> + @isloaded = false + @trigger('resource:failed', @) if @isLoading + @isLoading = false + + load: ()-> + getModel: ()-> @model + + + +class ModelResource extends Resource + constructor: (modelOrCollection, name, fetchOptions)-> + @model = modelOrCollection + @name = name + @fetchOptions = fetchOptions + @loadDeferred = null + + load: ()-> + return @loadDeferred.promise() if @isLoading + + @isLoading = true + @loadDeferred = $.Deferred() + $.when.apply($, @loadDependencies()) + .then(onLoadDependenciesSuccess, onLoadDependenciesFailed) + .always(()=> @isLoading = false) + + return @loadDeferred.promise() + + loadDependencies: ()-> + promises = [] + for dep in @dependecies + continue if dep.isLoaded + promises.push(dep.load()) + + return promises + + onLoadDependenciesSuccess: ()-> + @model.fetch(@fetchOptions) + + @listenToOnce(@model, 'sync' ()=> + @markLoaded() + @loadDeferred.resolve(@) + ) + + @listenToOnce(@model, 'error', ()=> + @markFailed() + @loadDeferred.reject(@) + ) + + onLoadDependenciesFailed: ()-> + @markFailed() + @loadDeferred.reject(@) + + + +class RequestResource extends Resource + constructor: (name, jqxhrOptions)-> + @model = null + @name = name + @jqxhrOptions = jqxhrOptions + + load: ()-> + return @model.promise() if @isLoading + + @isLoading = true + $.when.apply($, @loadDependencies()) + .then(onLoadDependenciesSuccess, onLoadDependenciesFailed) + .always(()=> @isLoading = false) + + return @model.promise() + + loadDependencies: ()-> + promises = [] + for dep in @dependecies + continue if dep.isLoaded + promises.push(dep.load()) + + return promises + + onLoadDependenciesSuccess: ()-> + @model = $.ajax(@jqxhrOptions) + @model.done(()=> @markLoaded()).failed(()=> @markFailed()) + + onLoadDependenciesFailed: ()-> + @markFailed() + + + +class SomethingResource extends Resource + constructor: (name)-> + @name = name \ No newline at end of file From 6729cfc29279a89e491b5700e29784c3d18a3abb Mon Sep 17 00:00:00 2001 From: Ting-Kuan Date: Tue, 8 Apr 2014 12:03:03 -0400 Subject: [PATCH 2/4] First commit. --- app/models/SuperModel.coffee | 8 +++++++- app/views/editor/thang/edit.coffee | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 9e2bcd347..26d6f4855 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -28,12 +28,18 @@ class SuperModel modelLoaded: (model) -> model.loadSchema() schema = model.schema() + + # Load schema first. unless schema.loaded @schemas[schema.urlRoot] = schema return schema.once('sync', => @modelLoaded(model)) + refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection) refs = [] unless @mustPopulate is model or @shouldPopulate(model) -# console.log 'Loaded', model.get('name') + + console.debug 'gintau', 'superModel-refs', model, refs + + # load references. for ref, i in refs when @shouldLoadReference ref ref.saveBackups = @shouldSaveBackups(ref) refURL = ref.url() diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee index 660280e54..0caece8b5 100644 --- a/app/views/editor/thang/edit.coffee +++ b/app/views/editor/thang/edit.coffee @@ -56,10 +56,14 @@ module.exports = class ThangTypeEditView extends View @insertSubView(new ErrorView()) ) + @addResourceToLoad(@thangType.schema(), 'thang_type_schema') + @addResourceToLoad(@thangType, 'thang_type') + @thangType.fetch() @thangType.loadSchema() @listenToOnce(@thangType.schema(), 'sync', @onThangTypeSync) @listenToOnce(@thangType, 'sync', @onThangTypeSync) + @refreshAnimation = _.debounce @refreshAnimation, 500 onThangTypeSync: -> From 3ea4d1677c0d1f41aa14bc560ea6be0ce7ac9f09 Mon Sep 17 00:00:00 2001 From: Ting-Kuan Date: Wed, 9 Apr 2014 15:11:59 -0400 Subject: [PATCH 3/4] able to load level editor. --- app/lib/ResourceManager | 159 ---------------------- app/models/CocoModel.coffee | 1 + app/models/SuperModel.coffee | 249 ++++++++++++++++++++++++++++++++++- 3 files changed, 243 insertions(+), 166 deletions(-) delete mode 100644 app/lib/ResourceManager diff --git a/app/lib/ResourceManager b/app/lib/ResourceManager deleted file mode 100644 index 75620fb3f..000000000 --- a/app/lib/ResourceManager +++ /dev/null @@ -1,159 +0,0 @@ -module.exports = class ResourceManager extends Backbone.Model - num: 0 - denom: 0 - showing: false - resources:{} # models and collections - progress: 0 - - constructor: ()-> - - addModelResource: (modelOrCollection, name, fetchOptions, value=1)-> - @checkName(name) - res = new ModelResource(modelOrCollection, name, fetchOptions) - storeResource: (name, res, value) - return res - - addRequestResource: (name, jqxhrOptions, value=1)-> - @checkName(name) - res = new RequestResource(name, jqxhrOptions) - storeResource: (name, res, value) - return res - - addSomethingResource: (name, value=1)-> - @checkName(name) - res = new SomethingResource(name) - storeResource: (name, res, value) - return res - - checkName: (name)-> - if name in resources - throw new Error('Resource name has been used.') - - storeResource: (name, resource, value)-> - @resources[name] = res - @listenToOnce(res, 'resource:loaded', @updateProgress) - @listenToOnce(res, 'resource:failed', @onResourceFailed) - @denom += value - - loadResources: ()-> - for name, res of @resources - res.load() - - updateProgress: (r)-> - @num += r.value if r.isLoaded - @progress = if denom then num / denom else 0 - @trigger('resourceManager:updateProgress', @progress) - - onResourceFailed: (r)-> - - getResource: (name)-> - return @resources[name] - - - -class Resource - dependencies: [] - name: null - isLoading: false - isLoaded: false - model: null - - addDependency: (resource)-> - @dependecies.push(resource) - - markLoaded: ()-> - @isLoaded = true - @trigger('resource:loaded', @) if @isLoading - @isLoading = false - - markFailed: ()-> - @isloaded = false - @trigger('resource:failed', @) if @isLoading - @isLoading = false - - load: ()-> - getModel: ()-> @model - - - -class ModelResource extends Resource - constructor: (modelOrCollection, name, fetchOptions)-> - @model = modelOrCollection - @name = name - @fetchOptions = fetchOptions - @loadDeferred = null - - load: ()-> - return @loadDeferred.promise() if @isLoading - - @isLoading = true - @loadDeferred = $.Deferred() - $.when.apply($, @loadDependencies()) - .then(onLoadDependenciesSuccess, onLoadDependenciesFailed) - .always(()=> @isLoading = false) - - return @loadDeferred.promise() - - loadDependencies: ()-> - promises = [] - for dep in @dependecies - continue if dep.isLoaded - promises.push(dep.load()) - - return promises - - onLoadDependenciesSuccess: ()-> - @model.fetch(@fetchOptions) - - @listenToOnce(@model, 'sync' ()=> - @markLoaded() - @loadDeferred.resolve(@) - ) - - @listenToOnce(@model, 'error', ()=> - @markFailed() - @loadDeferred.reject(@) - ) - - onLoadDependenciesFailed: ()-> - @markFailed() - @loadDeferred.reject(@) - - - -class RequestResource extends Resource - constructor: (name, jqxhrOptions)-> - @model = null - @name = name - @jqxhrOptions = jqxhrOptions - - load: ()-> - return @model.promise() if @isLoading - - @isLoading = true - $.when.apply($, @loadDependencies()) - .then(onLoadDependenciesSuccess, onLoadDependenciesFailed) - .always(()=> @isLoading = false) - - return @model.promise() - - loadDependencies: ()-> - promises = [] - for dep in @dependecies - continue if dep.isLoaded - promises.push(dep.load()) - - return promises - - onLoadDependenciesSuccess: ()-> - @model = $.ajax(@jqxhrOptions) - @model.done(()=> @markLoaded()).failed(()=> @markFailed()) - - onLoadDependenciesFailed: ()-> - @markFailed() - - - -class SomethingResource extends Resource - constructor: (name)-> - @name = name \ No newline at end of file diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 862ba72fd..0bb6b831b 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -4,6 +4,7 @@ class CocoSchema extends Backbone.Model constructor: (path, args...) -> super(args...) @urlRoot = path + '/schema' + console.debug 'gintau', 'schema-urlRoot', @urlRoot window.CocoSchema = CocoSchema diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 26d6f4855..a251ce545 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -1,20 +1,33 @@ -class SuperModel +module.exports = class SuperModel extends Backbone.Model constructor: -> @models = {} @collections = {} @schemas = {} - _.extend(@, Backbone.Events) + @num = 0 + @denom = 0 + @showing = false + @progress = 0 populateModel: (model) -> + console.debug 'gintau', 'SuperModel-populateModel', model + @mustPopulate = model model.saveBackups = @shouldSaveBackups(model) - model.fetch() unless model.loaded or model.loading + # model.fetch() unless model.loaded or model.loading @listenToOnce(model, 'sync', @modelLoaded) unless model.loaded @listenToOnce(model, 'error', @modelErrored) unless model.loaded url = model.url() @models[url] = model unless @models[url]? @modelLoaded(model) if model.loaded + modelRes = @addModelResource(model, url) + schema = model.schema() + schemaRes = @addModelResource(schema, schema.urlRoot) + @schemas[schema.urlRoot] = schema + modelRes.addDependency(schemaRes.name) + + modelRes.load() + # replace or overwrite shouldLoadReference: (model) -> true shouldLoadProjection: (model) -> false @@ -26,6 +39,8 @@ class SuperModel @removeEventsFromModel(model) modelLoaded: (model) -> + ### + model.loadSchema() schema = model.schema() @@ -37,7 +52,7 @@ class SuperModel refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection) refs = [] unless @mustPopulate is model or @shouldPopulate(model) - console.debug 'gintau', 'superModel-refs', model, refs + # console.debug 'gintau', 'superModel-refs', model, refs # load references. for ref, i in refs when @shouldLoadReference ref @@ -48,8 +63,10 @@ class SuperModel ref.fetch() @listenToOnce(ref, 'sync', @modelLoaded) + ### + @trigger 'loaded-one', model: model - @trigger 'loaded-all' if @finished() + # @trigger 'loaded-all' if @finished() @removeEventsFromModel(model) removeEventsFromModel: (model) -> @@ -102,6 +119,7 @@ class SuperModel @addModel(model) collection + ### progress: -> total = 0 loaded = 0 @@ -115,8 +133,225 @@ class SuperModel return 1.0 unless total return loaded / total +### finished: -> - return @progress() == 1.0 + console.debug 'gintau', 'trigger-loaded-all' if @progress == 1.0 + return @progress == 1.0 -module.exports = SuperModel + + + addModelResource: (modelOrCollection, name, fetchOptions, value=1)-> + @checkName(name) + res = new ModelResource(modelOrCollection, name, fetchOptions, value) + @storeResource(name, res, value) + return res + + addRequestResource: (name, jqxhrOptions, value=1)-> + @checkName(name) + res = new RequestResource(name, jqxhrOptions, value) + @storeResource(name, res, value) + return res + + addSomethingResource: (name, value=1)-> + @checkName(name) + res = new SomethingResource(name, value) + @storeResource(name, res, value) + return res + + checkName: (name)-> + if not name + throw new Error('Resource name should not be empty.') + if name in ResourceManager.resources + throw new Error('Resource name has been used.') + + storeResource: (name, resource, value)-> + ResourceManager.resources[name] = resource + @listenToOnce(resource, 'resource:loaded', @onResourceLoaded) + @listenToOnce(resource, 'resource:failed', @onResourceFailed) + @denom += value + + loadResources: ()-> + for name, res of ResourceManager.resources + res.load() + + onResourceLoaded: (r)=> + # Check if the model has references + console.debug 'gintau', 'updateProgress', r.constructor.name + if r.constructor.name == 'ModelResource' + model = r.model + @addModelRefencesToLoad(model) + @updateProgress(r) + else + console.debug 'gintau', 'updateProgressWithoutAddReferences' + @updateProgress(r) + + onResourceFailed: (r)=> + + addModelRefencesToLoad: (model)-> + schema = model.schema?() + return unless schema + + refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection) + refs = [] unless @mustPopulate is model or @shouldPopulate(model) + + console.debug 'gintau', 'superModel-refs', model, refs + for ref, i in refs when @shouldLoadReference ref + ref.saveBackups = @shouldSaveBackups(ref) + refURL = ref.url() + + continue if @models[refURL] + + @models[refURL] = ref + res = @addModelResource(ref, refURL) + res.load() + + updateProgress: (r)=> + @num += r.value + @progress = @num / @denom + + console.debug 'gintau', 'updateProgress', @num, @denom, @progress + @trigger('superModel:updateProgress', @progress) + @trigger 'loaded-all' if @finished() + + getResource: (name)-> + return ResourceManager.resources[name] + +# Both SuperModel and Resource access this class. +class ResourceManager + @resources: {} + +class Resource extends Backbone.Model + constructor: (name, value=1)-> + @name = name + @value = value + @dependencies = [] + @isLoading = false + @isLoaded = false + @model = null + @loadDeferred = null + @value = 1 + + addDependency: (name)-> + depRes = ResourceManager.resources[name] + throw new Error('Resource not found') unless depRes + return if (depRes.isLoaded or name == @name) + @dependencies.push(name) + + markLoaded: ()-> + console.debug 'gintau', 'markLoaded', @model + @trigger('resource:loaded', @) if not @isLoaded + @isLoaded = true + @isLoading = false + + markFailed: ()-> + console.debug 'gintau', 'markLoaded', @model + @trigger('resource:failed', @) if not @isLoaded + @isLoaded = false + @isLoading = false + + load: ()-> + isReadyForLoad: ()-> return not (@isloaded and @isLoading) + getModel: ()-> @model + +class ModelResource extends Resource + constructor: (modelOrCollection, name, fetchOptions, value)-> + super(name, value) + @model = modelOrCollection + @fetchOptions = fetchOptions + + load: ()-> + return @loadDeferred.promise() if @isLoading or @isLoaded + console.debug 'gintau', 'startLoading', @model + + @isLoading = true + @loadDeferred = $.Deferred() + $.when.apply($, @loadDependencies()) + .then(@onLoadDependenciesSuccess, @onLoadDependenciesFailed) + .always(()=> @isLoading = false) + + return @loadDeferred.promise() + + loadDependencies: ()-> + promises = [] + console.debug 'gintau', 'loadDependencies-Dependencies', @name, @dependencies + + for resName in @dependencies + dep = ResourceManager.resources[resName] + console.debug 'gintau', 'loadDependencies-name', resName, @name + continue if not dep.isReadyForLoad() + promises.push(dep.load()) + + console.debug 'gintau', 'loadDependencies-promises', promises, @name + return promises + + onLoadDependenciesSuccess: ()=> + console.debug 'gintau', 'onLoadDependenciesSuccess', 'startFetch', @model + @model.fetch(@fetchOptions) + + @listenToOnce(@model, 'sync', ()=> + @markLoaded() + @loadDeferred.resolve(@) + ) + + @listenToOnce(@model, 'error', ()=> + @markFailed() + @loadDeferred.reject(@) + ) + + onLoadDependenciesFailed: ()=> + console.debug 'gintau', 'onLoadDependenciesFailed', model + @markFailed() + @loadDeferred.reject(@) + + +class RequestResource extends Resource + constructor: (name, jqxhrOptions, value)-> + super(name, value) + @model = $.ajax(jqxhrOptions) + @jqxhrOptions = jqxhrOptions + @loadDeferred = @model + + load: ()-> + return @loadDeferred.promise() if @isLoading or @isLoaded + + @isLoading = true + $.when.apply($, @loadDependencies()) + .then(@onLoadDependenciesSuccess, @onLoadDependenciesFailed) + .always(()=> @isLoading = false) + + return @loadDeferred.promise() + + loadDependencies: ()-> + promises = [] + for depName in @dependecies + dep = ResourceManager.resources[depName] + continue if not dep.isReadyForLoad() + promises.push(dep.load()) + + return promises + + onLoadDependenciesSuccess: ()-> + @model = $.ajax(@jqxhrOptions) + @model.done(()=> @markLoaded()).failed(()=> @markFailed()) + + onLoadDependenciesFailed: ()-> + @markFailed() + + +class SomethingResource extends Resource + constructor: (name, value)-> + super(value) + @name = name + @loadDeferred = $.Deferred() + + load: ()-> + return @loadDeferred.promise() + + markLoaded: ()-> + @loadDeferred.resolve() + super() + + markFailed: ()-> + @loadDeferred.reject() + super() \ No newline at end of file From a5e6496a7c8d7d7c9c51518eee42a0a03d15f82a Mon Sep 17 00:00:00 2001 From: Ting-Kuan Date: Thu, 10 Apr 2014 16:59:08 -0400 Subject: [PATCH 4/4] Make resources as static class to avoid loading multiple times. Clean debug message. --- app/lib/LevelLoader.coffee | 9 +- app/models/CocoModel.coffee | 1 - app/models/SuperModel.coffee | 88 ++++-------------- app/views/editor/level/edit.coffee | 2 +- app/views/editor/thang/edit.coffee | 10 +- app/views/kinds/CocoView.coffee | 92 ++++--------------- .../play/level/level_loading_view.coffee | 3 +- app/views/play/level_view.coffee | 5 +- 8 files changed, 50 insertions(+), 160 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 0cc871d54..df648c290 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -59,14 +59,16 @@ module.exports = class LevelLoader extends CocoClass # Unless you specify cache:false, sometimes the browser will use a cached session # and players will 'lose' code - @session.fetch({cache:false}) @listenToOnce(@session, 'sync', @onSessionLoaded) + session_res = @supermodel.addModelResource(@session, @session.url, {cache:false}) + session_res.load() if @opponentSessionID @opponentSession = new LevelSession() @opponentSession.url = "/db/level_session/#{@opponentSessionID}" - @opponentSession.fetch() @listenToOnce(@opponentSession, 'sync', @onSessionLoaded) + opponentSession_res = @supermodel.addModelResource(@opponentSession, @opponentSession.url) + opponentSession_res.load() sessionsLoaded: -> return true if @headless @@ -217,7 +219,7 @@ module.exports = class LevelLoader extends CocoClass progress: -> return 0 unless @level.loaded overallProgress = 0 - supermodelProgress = @supermodel.progress() + supermodelProgress = @supermodel.getProgress() overallProgress += supermodelProgress * 0.7 overallProgress += 0.1 if @sessionsLoaded() if @headless @@ -226,6 +228,7 @@ module.exports = class LevelLoader extends CocoClass spriteMapProgress = if supermodelProgress is 1 then 0.2 else 0 spriteMapProgress *= @spriteSheetsBuilt / @spriteSheetsToBuild if @spriteSheetsToBuild overallProgress += spriteMapProgress + return overallProgress notifyProgress: -> diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 0bb6b831b..862ba72fd 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -4,7 +4,6 @@ class CocoSchema extends Backbone.Model constructor: (path, args...) -> super(args...) @urlRoot = path + '/schema' - console.debug 'gintau', 'schema-urlRoot', @urlRoot window.CocoSchema = CocoSchema diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index a251ce545..a626f9d3e 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -3,19 +3,13 @@ module.exports = class SuperModel extends Backbone.Model @models = {} @collections = {} @schemas = {} - @num = 0 - @denom = 0 - @showing = false - @progress = 0 populateModel: (model) -> - console.debug 'gintau', 'SuperModel-populateModel', model - @mustPopulate = model model.saveBackups = @shouldSaveBackups(model) # model.fetch() unless model.loaded or model.loading - @listenToOnce(model, 'sync', @modelLoaded) unless model.loaded - @listenToOnce(model, 'error', @modelErrored) unless model.loaded + # @listenToOnce(model, 'sync', @modelLoaded) unless model.loaded + # @listenToOnce(model, 'error', @modelErrored) unless model.loaded url = model.url() @models[url] = model unless @models[url]? @modelLoaded(model) if model.loaded @@ -38,35 +32,8 @@ module.exports = class SuperModel extends Backbone.Model @trigger 'error' @removeEventsFromModel(model) - modelLoaded: (model) -> - ### - - model.loadSchema() - schema = model.schema() - - # Load schema first. - unless schema.loaded - @schemas[schema.urlRoot] = schema - return schema.once('sync', => @modelLoaded(model)) - - refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection) - refs = [] unless @mustPopulate is model or @shouldPopulate(model) - - # console.debug 'gintau', 'superModel-refs', model, refs - - # load references. - for ref, i in refs when @shouldLoadReference ref - ref.saveBackups = @shouldSaveBackups(ref) - refURL = ref.url() - continue if @models[refURL] - @models[refURL] = ref - ref.fetch() - @listenToOnce(ref, 'sync', @modelLoaded) - - ### - + modelLoaded: (model) -> @trigger 'loaded-one', model: model - # @trigger 'loaded-all' if @finished() @removeEventsFromModel(model) removeEventsFromModel: (model) -> @@ -119,26 +86,8 @@ module.exports = class SuperModel extends Backbone.Model @addModel(model) collection - ### - progress: -> - total = 0 - loaded = 0 - - for model in _.values @models - total += 1 - loaded += 1 if model.loaded - for schema in _.values @schemas - total += 1 - loaded += 1 if schema.loaded - - return 1.0 unless total - return loaded / total -### - finished: -> - console.debug 'gintau', 'trigger-loaded-all' if @progress == 1.0 - return @progress == 1.0 - + return ResourceManager.progress == 1.0 or Object.keys(ResourceManager.resources).length == 0 addModelResource: (modelOrCollection, name, fetchOptions, value=1)-> @@ -169,24 +118,24 @@ module.exports = class SuperModel extends Backbone.Model ResourceManager.resources[name] = resource @listenToOnce(resource, 'resource:loaded', @onResourceLoaded) @listenToOnce(resource, 'resource:failed', @onResourceFailed) - @denom += value + ResourceManager.denom += value loadResources: ()-> for name, res of ResourceManager.resources res.load() onResourceLoaded: (r)=> + @modelLoaded(r.model) # Check if the model has references - console.debug 'gintau', 'updateProgress', r.constructor.name if r.constructor.name == 'ModelResource' model = r.model @addModelRefencesToLoad(model) @updateProgress(r) else - console.debug 'gintau', 'updateProgressWithoutAddReferences' @updateProgress(r) onResourceFailed: (r)=> + @modelErrored(r.model) addModelRefencesToLoad: (model)-> schema = model.schema?() @@ -195,7 +144,6 @@ module.exports = class SuperModel extends Backbone.Model refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection) refs = [] unless @mustPopulate is model or @shouldPopulate(model) - console.debug 'gintau', 'superModel-refs', model, refs for ref, i in refs when @shouldLoadReference ref ref.saveBackups = @shouldSaveBackups(ref) refURL = ref.url() @@ -207,18 +155,24 @@ module.exports = class SuperModel extends Backbone.Model res.load() updateProgress: (r)=> - @num += r.value - @progress = @num / @denom + ResourceManager.num += r.value + ResourceManager.progress = ResourceManager.num / ResourceManager.denom - console.debug 'gintau', 'updateProgress', @num, @denom, @progress - @trigger('superModel:updateProgress', @progress) + @trigger('superModel:updateProgress', ResourceManager.progress) @trigger 'loaded-all' if @finished() getResource: (name)-> return ResourceManager.resources[name] + getProgress: ()-> return ResourceManager.progress + # Both SuperModel and Resource access this class. +# Set resources as static so no need to load resources multiple times when more than one view is used. class ResourceManager + @num = 0 + @denom = 0 + @showing = false + @progress = 0 @resources: {} class Resource extends Backbone.Model @@ -239,13 +193,11 @@ class Resource extends Backbone.Model @dependencies.push(name) markLoaded: ()-> - console.debug 'gintau', 'markLoaded', @model @trigger('resource:loaded', @) if not @isLoaded @isLoaded = true @isLoading = false markFailed: ()-> - console.debug 'gintau', 'markLoaded', @model @trigger('resource:failed', @) if not @isLoaded @isLoaded = false @isLoading = false @@ -262,7 +214,6 @@ class ModelResource extends Resource load: ()-> return @loadDeferred.promise() if @isLoading or @isLoaded - console.debug 'gintau', 'startLoading', @model @isLoading = true @loadDeferred = $.Deferred() @@ -274,19 +225,15 @@ class ModelResource extends Resource loadDependencies: ()-> promises = [] - console.debug 'gintau', 'loadDependencies-Dependencies', @name, @dependencies for resName in @dependencies dep = ResourceManager.resources[resName] - console.debug 'gintau', 'loadDependencies-name', resName, @name continue if not dep.isReadyForLoad() promises.push(dep.load()) - console.debug 'gintau', 'loadDependencies-promises', promises, @name return promises onLoadDependenciesSuccess: ()=> - console.debug 'gintau', 'onLoadDependenciesSuccess', 'startFetch', @model @model.fetch(@fetchOptions) @listenToOnce(@model, 'sync', ()=> @@ -300,7 +247,6 @@ class ModelResource extends Resource ) onLoadDependenciesFailed: ()=> - console.debug 'gintau', 'onLoadDependenciesFailed', model @markFailed() @loadDeferred.reject(@) diff --git a/app/views/editor/level/edit.coffee b/app/views/editor/level/edit.coffee index b685d457f..57a233902 100644 --- a/app/views/editor/level/edit.coffee +++ b/app/views/editor/level/edit.coffee @@ -58,7 +58,7 @@ module.exports = class EditorLevelView extends View onLevelLoaded: -> @files = new DocumentFiles(@level) - @files.fetch() + @supermodel.addModelResource(@files, @files.url).load() onAllLoaded: -> @level.unset('nextLevel') if _.isString(@level.get('nextLevel')) diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee index 0caece8b5..7e468741f 100644 --- a/app/views/editor/thang/edit.coffee +++ b/app/views/editor/thang/edit.coffee @@ -56,11 +56,11 @@ module.exports = class ThangTypeEditView extends View @insertSubView(new ErrorView()) ) - @addResourceToLoad(@thangType.schema(), 'thang_type_schema') - @addResourceToLoad(@thangType, 'thang_type') + thang_res = @supermodel.addModelResource(@thangType, 'thang_type') + thang_schema_res = @supermodel.addModelResource(@thangType.schema(), 'thang_type_schema') + thang_res.addDependency('thang_type_schema') - @thangType.fetch() - @thangType.loadSchema() + thang_res.load() @listenToOnce(@thangType.schema(), 'sync', @onThangTypeSync) @listenToOnce(@thangType, 'sync', @onThangTypeSync) @@ -70,7 +70,7 @@ module.exports = class ThangTypeEditView extends View return unless @thangType.loaded and ThangType.hasSchema() @startsLoading = false @files = new DocumentFiles(@thangType) - @files.fetch() + @supermodel.addModelResource(@files, @files.url).load() @render() getRenderData: (context={}) -> diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index a6355ec22..c92fc7c39 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -26,12 +26,6 @@ module.exports = class CocoView extends Backbone.View # load progress properties loadProgress: - num: 0 - denom: 0 - showing: false - resources: [] # models and collections - requests: [] # jqxhr's - somethings: [] # everything else progress: 0 # Setup, Teardown @@ -48,6 +42,10 @@ module.exports = class CocoView extends Backbone.View @listenToShortcuts() @updateProgressBar = _.debounce @updateProgressBar, 100 # Backbone.Mediator handles subscription setup/teardown automatically + + @listenToOnce(@supermodel, 'loaded-all', ()=>@onLoaded) + @listenToOnce(@supermodel, 'superModel:updateProgress', @updateProgress) + super options destroy: -> @@ -87,8 +85,13 @@ module.exports = class CocoView extends Backbone.View super() return @template if _.isString(@template) @$el.html @template(@getRenderData()) + + if not @supermodel.finished() + @showLoading() + else + @hideLoading() + @afterRender() - @showLoading() if @loading() @$el.i18n() @ @@ -103,81 +106,18 @@ module.exports = class CocoView extends Backbone.View context afterRender: -> - - # Resource and request loading management for any given view - - addResourceToLoad: (modelOrCollection, name, value=1) -> - res = {resource:modelOrCollection, value:value, name:name, loaded: modelOrCollection.loaded} + @hideLoading() - @loadProgress.resources.push res - @loadProgress.denom += value - - @listenToOnce modelOrCollection, 'sync', ()=> - # Sprite builder only works after rendering, if callback creates sprite, we need to update progress first. - res.loaded = true - @updateProgress(res) - - @listenTo modelOrCollection, 'error', @onResourceLoadFailed - @updateProgress(res) - - addRequestToLoad: (jqxhr, name, retryFunc, value=1) -> - res = {request:jqxhr, value:value, name: name, retryFunc: retryFunc, loaded:false} - - @loadProgress.requests.push res - @loadProgress.denom += value - - jqxhr.done ()=> - res.loaded = true - @updateProgress(res) - - jqxhr.fail ()=> - @onRequestLoadFailed(jqxhr) - - addSomethingToLoad: (name, value=1) -> - res = {name: name, value: value, loaded: false} - - @loadProgress.somethings.push res - @loadProgress.denom += value - - @updateProgress(res) - - somethingLoaded: (name) -> - r = _.find @loadProgress.somethings, {name: name} - return console.error 'Could not find something called', name if not r - r.loaded = true - @updateProgress(r) - - loading: -> - return false if @loaded - for r in @loadProgress.resources - return true if not r.resource.loaded - for r in @loadProgress.requests - return true if not r.request.status - for r in @loadProgress.somethings - return true if not r.loaded - return false - - updateProgress: (r)=> - console.debug 'Loaded', r.name, r.loaded - - denom = @loadProgress.denom - @loadProgress.num += r.value if r.loaded - num = @loadProgress.num - - progress = if denom then num / denom else 0 - # sometimes the denominator isn't known from the outset, so make sure the overall progress only goes up + updateProgress: (progress)=> @loadProgress.progress = progress if progress > @loadProgress.progress - @updateProgressBar() - - if num is denom - @onLoaded() + @updateProgressBar(progress) - updateProgressBar: => - prog = "#{parseInt(@loadProgress.progress*100)}%" + updateProgressBar: (progress) => + prog = "#{parseInt(progress*100)}%" @$el.find('.loading-screen .progress-bar').css('width', prog) onLoaded: -> - @render() + #@render() # Error handling for loading diff --git a/app/views/play/level/level_loading_view.coffee b/app/views/play/level/level_loading_view.coffee index 32da3377e..679e78592 100644 --- a/app/views/play/level/level_loading_view.coffee +++ b/app/views/play/level/level_loading_view.coffee @@ -15,6 +15,7 @@ module.exports = class LevelLoadingView extends View tip = _.sample(tips) $(tip).removeClass('to-remove') @$el.find('.to-remove').remove() + @hideLoading() onLevelLoaderProgressChanged: (e) -> @progress = e.progress @@ -33,7 +34,7 @@ module.exports = class LevelLoadingView extends View _.delay @reallyUnveil, 1000 reallyUnveil: => - return if @destroyed + return if @destroyed or @progress < 1 loadingDetails = @$el.find('.loading-details') duration = parseFloat loadingDetails.css 'transition-duration' loadingDetails.css 'top', -loadingDetails.outerHeight(true) diff --git a/app/views/play/level_view.coffee b/app/views/play/level_view.coffee index bd60b867f..a50231a86 100644 --- a/app/views/play/level_view.coffee +++ b/app/views/play/level_view.coffee @@ -86,6 +86,7 @@ module.exports = class PlayLevelView extends View @listenToOnce(@supermodel, 'error', @onLevelLoadError) @saveScreenshot = _.throttle @saveScreenshot, 30000 + @insertSubView @loadingView = new LoadingView {} if @isEditorPreview f = => @supermodel.shouldSaveBackups = (model) -> @@ -361,11 +362,11 @@ module.exports = class PlayLevelView extends View .css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)") .css('top', target_top - 50) .css('left', target_left - 50) - setTimeout((=> + setTimeout(()=> @animatePointer() clearInterval(@pointerInterval) @pointerInterval = setInterval(@animatePointer, 1200) - ), 1) + , 1) animatePointer: ->