From 565508498103562189654a08480b1073c0f05d10 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 12 Feb 2014 12:41:41 -0800 Subject: [PATCH] Fixed a ton more leaks. --- app/lib/God.coffee | 48 +++++++++++-------- app/lib/LevelLoader.coffee | 15 ++++-- app/lib/surface/Camera.coffee | 9 ++-- app/lib/surface/CocoSprite.coffee | 10 ++-- app/lib/surface/MusicPlayer.coffee | 17 ++++--- app/lib/surface/Surface.coffee | 20 ++++---- app/models/CocoModel.coffee | 14 +++--- app/models/SuperModel.coffee | 8 ++-- app/models/User.coffee | 1 - app/views/kinds/CocoView.coffee | 18 +++---- app/views/play/level/control_bar_view.coffee | 1 + .../play/level/tome/cast_button_view.coffee | 24 ++++------ .../play/level/tome/spell_debug_view.coffee | 3 +- .../play/level/tome/spell_palette_view.coffee | 1 + app/views/play/level/tome/spell_view.coffee | 41 +++++++++++----- .../level/tome/thang_list_entry_view.coffee | 12 +++-- app/views/play/level_view.coffee | 33 ++++++++----- 17 files changed, 162 insertions(+), 113 deletions(-) diff --git a/app/lib/God.coffee b/app/lib/God.coffee index 6a77d3a54..b05e56a43 100644 --- a/app/lib/God.coffee +++ b/app/lib/God.coffee @@ -131,6 +131,8 @@ module.exports = class God @dead = true Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @) @goalManager = null + @fillWorkerPool = null + @simulateWorld = null #### Bad code for running worlds on main thread (profiling / IE9) #### simulateWorld: => @@ -207,6 +209,7 @@ class Angel if @worker worker = @worker _.defer -> worker.terminate + @worker.removeEventListener 'message', @onWorkerMessage @worker = null @ @@ -217,6 +220,7 @@ class Angel terminate: => @worker?.terminate() + @worker?.removeEventListener 'message', @onWorkerMessage @worker = null return if @dead @free() @@ -225,6 +229,10 @@ class Angel destroy: -> @dead = true @abort() + @terminate = null + @testWorker = null + @condemnWorker = null + @onWorkerMessage = null testWorker: => @worker.postMessage {func: 'reportIn'} @@ -235,22 +243,24 @@ class Angel @abort() listen: -> - @worker.addEventListener 'message', (event) => - switch event.data.type - when 'new-world' - @god.beholdWorld @, event.data.serialized, event.data.goalStates - when 'world-load-progress-changed' - Backbone.Mediator.publish 'god:world-load-progress-changed', event.data unless @dead - when 'console-log' - console.log "|" + @god.id + "'s " + @id + "|", event.data.args... - when 'user-code-problem' - @god.angelUserCodeProblem @, event.data.problem - when 'abort' - #console.log @id, "aborted." - clearTimeout @abortTimeout - @free() - @god.angelAborted @ - when 'reportIn' - clearTimeout @condemnTimeout - else - console.log "Unsupported message:", event.data + @worker.addEventListener 'message', @onWorkerMessage + + onWorkerMessage: (event) => + switch event.data.type + when 'new-world' + @god.beholdWorld @, event.data.serialized, event.data.goalStates + when 'world-load-progress-changed' + Backbone.Mediator.publish 'god:world-load-progress-changed', event.data unless @dead + when 'console-log' + console.log "|" + @god.id + "'s " + @id + "|", event.data.args... + when 'user-code-problem' + @god.angelUserCodeProblem @, event.data.problem + when 'abort' + #console.log @id, "aborted." + clearTimeout @abortTimeout + @free() + @god.angelAborted @ + when 'reportIn' + clearTimeout @condemnTimeout + else + console.log "Unsupported message:", event.data diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 046498e34..b648361ad 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -28,7 +28,7 @@ module.exports = class LevelLoader extends CocoClass @loadLevelModels() @loadAudio() @playJingle() - setTimeout (=> @update()), 1 # lets everything else resolve first + _.defer @update # Lets everything else resolve first playJingle: -> jingles = ["ident_1", "ident_2"] @@ -80,7 +80,7 @@ module.exports = class LevelLoader extends CocoClass # building = thangType.buildSpriteSheet options # if building # @spriteSheetsToBuild += 1 -# thangType.on 'build-complete', => +# thangType.once 'build-complete', => # @spriteSheetsBuilt += 1 # @notifyProgress() @@ -91,7 +91,7 @@ module.exports = class LevelLoader extends CocoClass # Things to do when either the Session or Supermodel load - update: -> + update: => @notifyProgress() return if @updateCompleted @@ -153,7 +153,7 @@ module.exports = class LevelLoader extends CocoClass return unless building console.log 'Building:', thangType.get('name'), options @spriteSheetsToBuild += 1 - thangType.on 'build-complete', => + thangType.once 'build-complete', => @spriteSheetsBuilt += 1 @notifyProgress() @@ -208,6 +208,11 @@ module.exports = class LevelLoader extends CocoClass @trigger 'loaded-all' if @progress() is 1 destroy: -> + super() @world = null # don't hold onto garbage @supermodel.off 'loaded-one', @onSupermodelLoadedOne - super() + @onSessionLoaded = null + @onSupermodelError = null + @onSupermodelLoadedOne = null + @onSupermodelLoadedAll = null + @notifyProgress = null diff --git a/app/lib/surface/Camera.coffee b/app/lib/surface/Camera.coffee index 07e3a25a3..00ca79864 100644 --- a/app/lib/surface/Camera.coffee +++ b/app/lib/surface/Camera.coffee @@ -186,7 +186,7 @@ module.exports = class Camera extends CocoClass # Target is either just a {x, y} pos or a display object with {x, y} that might change; surface coordinates. time = 0 if @instant newTarget ?= {x:0, y:0} - newTarget = (@newTarget or @target) if @locked + newTarget = (@newTarget or @target) if @locked newZoom = Math.min((Math.max @minZoom, newZoom), MAX_ZOOM) return if @zoom is newZoom and newTarget is newTarget.x and newTarget.y is newTarget.y @@ -199,15 +199,13 @@ module.exports = class Camera extends CocoClass @tweenProgress = 0.01 createjs.Tween.get(@) .to({tweenProgress: 1.0}, time, createjs.Ease.getPowInOut(3)) - .call @onTweenEnd + .call @finishTween else @target = newTarget @zoom = newZoom @updateZoom true - onTweenEnd: => @finishTween() - finishTween: (abort=false) => createjs.Tween.removeTweens(@) return unless @newTarget @@ -263,4 +261,5 @@ module.exports = class Camera extends CocoClass destroy: -> super() - @onTweenEnd = null \ No newline at end of file + createjs.Tween.removeTweens @ + @finishTween = null diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 879f693f7..8ab09e653 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -80,6 +80,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass super() mark.destroy() for name, mark of @marks label.destroy() for name, label of @labels + @imageObject?.off 'animationend', @playNextAction + @playNextAction = null + @displayObject?.off() toString: -> "" @@ -108,7 +111,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @displayObject.sprite = @ @displayObject.layerPriority = @thangType.get 'layerPriority' @displayObject.name = @thang?.spriteName or @thangType.get 'name' - @imageObject.on 'animationend', @onActionEnd + @imageObject.on 'animationend', @playNextAction ################################################## # QUEUEING AND PLAYING ACTIONS @@ -126,10 +129,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @currentRootAction = action @playNextAction() - onActionEnd: (e) => @playNextAction() onSurfaceTicked: (e) -> @age += e.dt - playNextAction: -> + playNextAction: => @playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length playAction: (action) -> @@ -411,7 +413,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers' @instance?.stop() if @instance = @playSound sound, false - @instance.addEventListener "complete", => Backbone.Mediator.publish 'dialogue-sound-completed' + @instance.addEventListener "complete", -> Backbone.Mediator.publish 'dialogue-sound-completed' @notifySpeechUpdated e onClearDialogue: (e) -> diff --git a/app/lib/surface/MusicPlayer.coffee b/app/lib/surface/MusicPlayer.coffee index 7fbac15e0..aa7c8ad98 100644 --- a/app/lib/surface/MusicPlayer.coffee +++ b/app/lib/surface/MusicPlayer.coffee @@ -7,14 +7,14 @@ CROSSFADE_LENGTH = 1500 module.exports = class MusicPlayer extends CocoClass currentMusic: null standingBy: null - + subscriptions: 'level-play-music': 'onPlayMusic' 'audio-player:loaded': 'onAudioLoaded' - + constructor: -> - super(arguments...) - me.on('change:music', @onMusicSettingChanged, @) + super arguments... + me.on 'change:music', @onMusicSettingChanged, @ onAudioLoaded: -> @onPlayMusic(@standingBy) if @standingBy @@ -29,12 +29,12 @@ module.exports = class MusicPlayer extends CocoClass AudioPlayer.preloadSound(src) @standingBy = e return - + @standingBy = null if @currentMusic f = -> @stop() createjs.Tween.get(@currentMusic).to({volume:0.0}, CROSSFADE_LENGTH).call(f) - + @currentMusic = createjs.Sound.play(src, 'none', 0, 0, -1, 0.3) if src and e.play return unless @currentMusic @currentMusic.volume = 0.0 @@ -48,4 +48,7 @@ module.exports = class MusicPlayer extends CocoClass return unless @currentMusic createjs.Tween.removeTweens(@currentMusic) @currentMusic.volume = if me.get('music') then 1.0 else 0.0 - + + destroy: -> + super() + me.off 'change:music', @onMusicSettingChanged, @ diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 904e44e0e..5f576afd0 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -90,14 +90,19 @@ module.exports = Surface = class Surface extends CocoClass @dimmer?.destroy() @stage.clear() @musicPlayer?.destroy() + @stage.removeAllChildren() @stage.removeEventListener 'stagemousemove', @onMouseMove @stage.removeEventListener 'stagemousedown', @onMouseDown @stage.removeAllEventListeners() + @stage.enableDOMEvents false + @stage.enableMouseOver 0 @playScrubbedSounds = null @onMouseMove = null @onMouseDown = null @tick = null - + @canvas.off 'mousewheel', @onMouseWheel + @onMouseWheel = null + setWorld: (@world) -> @worldLoaded = true @world.getFrame(Math.min(@getCurrentFrame(), @world.totalFrames - 1)).restoreState() unless @options.choosing @@ -337,7 +342,7 @@ module.exports = Surface = class Surface extends CocoClass @stage.enableMouseOver(10) @stage.addEventListener 'stagemousemove', @onMouseMove @stage.addEventListener 'stagemousedown', @onMouseDown - @hookUpZoomControls() + @canvas.on 'mousewheel', @onMouseWheel @hookUpChooseControls() if @options.choosing console.log "Setting fps", @world.frameRate unless @world.frameRate is 30 createjs.Ticker.setFPS @world.frameRate @@ -436,12 +441,11 @@ module.exports = Surface = class Surface extends CocoClass onBackground = not @stage.hitTest e.stageX, e.stageY Backbone.Mediator.publish 'surface:stage-mouse-down', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e - hookUpZoomControls: -> - @canvas.bind 'mousewheel', (e) => - # https://github.com/brandonaaron/jquery-mousewheel - e.preventDefault() - return if @disabled - Backbone.Mediator.publish 'surface:mouse-scrolled', deltaX: e.deltaX, deltaY: e.deltaY unless @disabled + onMouseWheel: (e) => + # https://github.com/brandonaaron/jquery-mousewheel + e.preventDefault() + return if @disabled + Backbone.Mediator.publish 'surface:mouse-scrolled', deltaX: e.deltaX, deltaY: e.deltaY unless @disabled hookUpChooseControls: -> chooserOptions = stage: @stage, surfaceLayer: @surfaceLayer, camera: @camera, restrictRatio: @options.choosing is 'ratio-region' diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 7fce777b6..4f740d5e8 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -25,7 +25,7 @@ class CocoModel extends Backbone.Model @loadSchema() @once 'sync', @onLoaded, @ @saveBackup = _.debounce(@saveBackup, 500) - + type: -> @constructor.className @@ -36,18 +36,18 @@ class CocoModel extends Backbone.Model if @saveBackups existing = storage.load @id if existing - @set(existing, {silent:true}) + @set(existing, {silent:true}) CocoModel.backedUp[@id] = @ - + set: -> res = super(arguments...) @saveBackup() if @saveBackups and @loaded and @hasLocalChanges() res - + saveBackup: -> storage.save(@id, @attributes) CocoModel.backedUp[@id] = @ - + @backedUp = {} loadSchema: -> @@ -55,7 +55,7 @@ class CocoModel extends Backbone.Model @constructor.schema = new CocoSchema(@urlRoot) @constructor.schema.fetch() - @constructor.schema.on 'sync', => + @constructor.schema.once 'sync', => @constructor.schema.loaded = true @addSchemaDefaults() @trigger 'schema-loaded' @@ -90,7 +90,7 @@ class CocoModel extends Backbone.Model revert: -> @set(@_revertAttributes, {silent: true}) if @_revertAttributes @clearBackup() - + clearBackup: -> storage.remove @id diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 50cb5906c..9db467078 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -8,14 +8,14 @@ class SuperModel @mustPopulate = model model.saveBackups = @shouldSaveBackups(model) model.fetch() unless model.loaded or model.loading - model.on('sync', @modelLoaded) unless model.loaded + model.once('sync', @modelLoaded) unless model.loaded model.once('error', @modelErrored) unless model.loaded url = model.url() @models[url] = model unless @models[url]? @modelLoaded(model) if model.loaded # replace or overwrite - shouldPopulate: (url) -> return true + shouldPopulate: (url) -> return true shouldSaveBackups: (model) -> return false modelErrored: (model) => @@ -23,7 +23,7 @@ class SuperModel modelLoaded: (model) => schema = model.schema() - return schema.on('sync', => @modelLoaded(model)) unless schema.loaded + return schema.once('sync', => @modelLoaded(model)) unless schema.loaded refs = model.getReferencedModels(model.attributes, schema.attributes) refs = [] unless @mustPopulate is model or @shouldPopulate(model) # console.log 'Loaded', model.get('name') @@ -33,7 +33,7 @@ class SuperModel continue if @models[refURL] @models[refURL] = ref ref.fetch() - ref.on 'sync', @modelLoaded + ref.once 'sync', @modelLoaded @trigger 'loaded-one', model: model @trigger 'loaded-all' if @finished() diff --git a/app/models/User.coffee b/app/models/User.coffee index debfd1f34..c3bf71146 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -28,7 +28,6 @@ module.exports = class User extends CocoModel profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}" script = $("") $('head').append(script) - $('body').on('load',(e)->console.log('we did it!', e)) window[functionName] = (profile) => @gravatarProfile = profile @trigger('change', @) diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index b1ea0e2d3..905099fd3 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -42,6 +42,7 @@ module.exports = class CocoView extends Backbone.View @undelegateEvents() # removes both events and subs view.destroy() for id, view of @subviews @modalClosed = null + $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed afterInsert: -> @@ -85,14 +86,14 @@ module.exports = class CocoView extends Backbone.View # Modals - toggleModal: (e) -> + toggleModal: (e) -> if $(e.currentTarget).prop('target') is '_blank' return true - # special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value. + # special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value. elem = $(e.target) return unless elem.data('toggle') is 'coco-modal' target = elem.data('target') - view = application.router.getView(target, '_modal') # could set up a system for loading cached modals, if told to + view = application.router.getView(target, '_modal') # could set up a system for loading cached modals, if told to @openModalView(view) openModalView: (modalView) -> @@ -101,12 +102,12 @@ module.exports = class CocoView extends Backbone.View waitingModal = modalView visibleModal.hide() return - modalView.render() + modalView.render() $('#modal-wrapper').empty().append modalView.el modalView.afterInsert() visibleModal = modalView modalOptions = {show: true, backdrop: if modalView.closesOnClickOutside then true else 'static'} - $('#modal-wrapper .modal').modal(modalOptions).on('hidden.bs.modal', @modalClosed) + $('#modal-wrapper .modal').modal(modalOptions).on 'hidden.bs.modal', @modalClosed window.currentModal = modalView @getRootView().stopListeningToShortcuts(true) @@ -114,7 +115,8 @@ module.exports = class CocoView extends Backbone.View visibleModal.willDisappear() if visibleModal visibleModal.destroy() visibleModal = null - if waitingModal + $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed + if waitingModal wm = waitingModal waitingModal = null @openModalView(wm) @@ -212,8 +214,8 @@ module.exports = class CocoView extends Backbone.View slider.on('slide',changeCallback) slider.on('slidechange',changeCallback) slider - - + + mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i diff --git a/app/views/play/level/control_bar_view.coffee b/app/views/play/level/control_bar_view.coffee index b70bc95ea..65bca0d6c 100644 --- a/app/views/play/level/control_bar_view.coffee +++ b/app/views/play/level/control_bar_view.coffee @@ -37,6 +37,7 @@ module.exports = class ControlBarView extends View setBus: (@bus) -> onPlayerStatesChanged: (e) -> + # TODO: this doesn't fire any more. Replacement? return unless @bus is e.bus numPlayers = _.keys(e.players).length return if numPlayers is @numPlayers diff --git a/app/views/play/level/tome/cast_button_view.coffee b/app/views/play/level/tome/cast_button_view.coffee index 9bebbf7c1..2c754f358 100644 --- a/app/views/play/level/tome/cast_button_view.coffee +++ b/app/views/play/level/tome/cast_button_view.coffee @@ -11,6 +11,8 @@ module.exports = class CastButtonView extends View 'tome:cast-spells': 'onCastSpells' 'god:world-load-progress-changed': 'onWorldLoadProgressChanged' 'god:new-world-created': 'onNewWorld' + 'click .cast-button': -> Backbone.Mediator.publish 'tome:manual-cast', {} + 'click .cast-options a': 'onCastOptionsClick' constructor: (options) -> super options @@ -26,8 +28,10 @@ module.exports = class CastButtonView extends View afterRender: -> super() + @castButton = $('.cast-button', @$el) + @castButtonGroup = $('.cast-button-group', @$el) + @castOptions = $('.autocast-delays', @$el) # TODO: use a User setting instead of localStorage - @hookUpButtons() delay = localStorage.getItem 'autocastDelay' delay ?= 5000 @setAutocastDelay delay @@ -35,19 +39,11 @@ module.exports = class CastButtonView extends View attachTo: (spellView) -> @$el.detach().prependTo(spellView.toolbarView.$el).show() - hookUpButtons: -> - # hook up cast button callbacks - @castButton = $('.cast-button', @$el) - @castButtonGroup = $('.cast-button-group', @$el) - @castOptions = $('.autocast-delays', @$el) - - @castButton.click (e) => - Backbone.Mediator.publish 'tome:manual-cast', {} - @castOptions.find('a').click (e) => - Backbone.Mediator.publish 'focus-editor' - @castButtonGroup.removeClass 'open' - @setAutocastDelay $(e.target).attr 'data-delay' - false + onCastOptionsClick: (e) -> + Backbone.Mediator.publish 'focus-editor' + @castButtonGroup.removeClass 'open' + @setAutocastDelay $(e.target).attr 'data-delay' + false onSpellChanged: (e) -> @updateCastButton() diff --git a/app/views/play/level/tome/spell_debug_view.coffee b/app/views/play/level/tome/spell_debug_view.coffee index eab097fb0..c85edc748 100644 --- a/app/views/play/level/tome/spell_debug_view.coffee +++ b/app/views/play/level/tome/spell_debug_view.coffee @@ -60,7 +60,7 @@ module.exports = class DebugView extends View @variableChain = @markerRange = null @update() - onMouseOut: (e) => + onMouseOut: (e) -> @variableChain = @markerRange = null @update() @@ -138,3 +138,4 @@ module.exports = class DebugView extends View destroy: -> super() @ace?.removeEventListener "mousemove", @onMouseMove + @onMouseMove = null diff --git a/app/views/play/level/tome/spell_palette_view.coffee b/app/views/play/level/tome/spell_palette_view.coffee index 445364274..ebb71e09c 100644 --- a/app/views/play/level/tome/spell_palette_view.coffee +++ b/app/views/play/level/tome/spell_palette_view.coffee @@ -60,3 +60,4 @@ module.exports = class SpellPaletteView extends View destroy: -> super() entry.destroy() for entry in @entries + @toggleBackground = null diff --git a/app/views/play/level/tome/spell_view.coffee b/app/views/play/level/tome/spell_view.coffee index bf16e60d5..bb0316115 100644 --- a/app/views/play/level/tome/spell_view.coffee +++ b/app/views/play/level/tome/spell_view.coffee @@ -74,47 +74,51 @@ module.exports = class SpellView extends View $(@ace.container).find('.ace_gutter').on 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick createACEShortcuts: -> - @ace.commands.addCommand + @aceCommands = [] + addCommand = (c) => + @ace.commands.addCommand c + @aceCommands.push c.name + addCommand name: 'run-code' bindKey: {win: 'Shift-Enter|Ctrl-Enter|Ctrl-S', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter|Command-S|Ctrl-S'} exec: (e) => @recompile() - @ace.commands.addCommand + addCommand name: 'toggle-playing' bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'} exec: -> Backbone.Mediator.publish 'level-toggle-playing' - @ace.commands.addCommand + addCommand name: 'end-current-script' bindKey: {win: 'Shift-Space', mac: 'Shift-Space'} exec: -> Backbone.Mediator.publish 'level:shift-space-pressed' - @ace.commands.addCommand + addCommand name: 'end-all-scripts' bindKey: {win: 'Escape', mac: 'Escape'} exec: -> Backbone.Mediator.publish 'level:escape-pressed' - @ace.commands.addCommand + addCommand name: 'toggle-grid' bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'} exec: -> Backbone.Mediator.publish 'level-toggle-grid' - @ace.commands.addCommand + addCommand name: 'toggle-debug' bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'} exec: -> Backbone.Mediator.publish 'level-toggle-debug' - @ace.commands.addCommand + addCommand name: 'toggle-pathfinding' bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'} exec: -> Backbone.Mediator.publish 'level-toggle-pathfinding' - @ace.commands.addCommand + addCommand name: 'level-scrub-forward' bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'} exec: -> Backbone.Mediator.publish 'level-scrub-forward' - @ace.commands.addCommand + addCommand name: 'level-scrub-back' bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'} exec: -> Backbone.Mediator.publish 'level-scrub-back' - @ace.commands.addCommand + addCommand name: 'spell-step-forward' bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'} exec: -> Backbone.Mediator.publish 'spell-step-forward' - @ace.commands.addCommand + addCommand name: 'spell-step-backward' bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'} exec: -> Backbone.Mediator.publish 'spell-step-backward' @@ -212,6 +216,9 @@ module.exports = class SpellView extends View @updateACEText @spell.originalSource @recompile cast + recompileIfNeeded: => + @recompile() if @recompileNeeded + recompile: (cast=true) => @setRecompileNeeded false return if @spell.source is @getSource() @@ -238,7 +245,7 @@ module.exports = class SpellView extends View autocastDelay = @autocastDelay ? 3000 onSignificantChange = [ _.debounce @setRecompileNeeded, autocastDelay - 100 - @currentAutocastHandler = _.debounce (=> @recompile() if @recompileNeeded), autocastDelay + @currentAutocastHandler = _.debounce @recompileIfNeeded, autocastDelay ] onAnyChange = [ _.debounce @updateAether, 500 @@ -500,9 +507,17 @@ module.exports = class SpellView extends View destroy: -> super() + $(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick @firepad?.dispose() - @ace.destroy() + @ace?.commands.removeCommand command for command in @aceCommands + @ace?.destroy() @ace = null + @aceDoc?.off 'change', @onCodeChangeMetaHandler + @aceDoc = null + @aceSession?.selection.off 'changeCursor', @onCursorActivity + @aceSession = null @debugView?.destroy() @spell = null @session.off 'change:multiplayer', @onMultiplayerChanged, @ + for fat in ['notifySpellChanged', 'notifyEditingEnded', 'notifyEditingBegan', 'onFirepadLoaded', 'onLoaded', 'recompile', 'toggleBackground', 'setRecompileNeeded', 'onCursorActivity', 'highlightCurrentLine', 'updateAether', 'onCodeChangeMetaHandler', 'recompileIfNeeded', 'currentAutocastHandler'] + @[fat] = null diff --git a/app/views/play/level/tome/thang_list_entry_view.coffee b/app/views/play/level/tome/thang_list_entry_view.coffee index eed97cb4a..b3e9cfa66 100644 --- a/app/views/play/level/tome/thang_list_entry_view.coffee +++ b/app/views/play/level/tome/thang_list_entry_view.coffee @@ -97,12 +97,12 @@ module.exports = class ThangListEntryView extends View @$el.popover('setContent').popover('show') @$el.parent().parent().parent().i18n() clearTimeout @hideSpellsTimeout if @hideSpellsTimeout - popover = @$el.parent().parent().parent().find('.popover') - popover.off 'mouseenter mouseleave' - popover.mouseenter (e) => @onMouseEnter() - popover.mouseleave (e) => @onMouseLeave() + @popover = @$el.parent().parent().parent().find('.popover') + @popover.off 'mouseenter mouseleave' + @popover.mouseenter (e) => @onMouseEnter() + @popover.mouseleave (e) => @onMouseLeave() thangID = @thang.id - popover.find('code').click (e) -> + @popover.find('code').click (e) -> Backbone.Mediator.publish "level-select-sprite", thangID: thangID, spellName: $(@).data 'spell-name' hideSpells: => @@ -139,3 +139,5 @@ module.exports = class ThangListEntryView extends View destroy: -> super() @avatar?.destroy() + @popover?.off 'mouseenter mouseleave' + @popover?.find('code').off 'click' diff --git a/app/views/play/level_view.coffee b/app/views/play/level_view.coffee index 8e8bb7962..3cdb5e3b1 100644 --- a/app/views/play/level_view.coffee +++ b/app/views/play/level_view.coffee @@ -76,9 +76,7 @@ module.exports = class PlayLevelView extends View @sessionID = @getQueryVariable 'session' $(window).on('resize', @onWindowResize) - @supermodel.once 'error', => - msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.") - @$el.html('
' + msg + '
') + @supermodel.once 'error', @onLevelLoadError @saveScreenshot = _.throttle @saveScreenshot, 30000 if @isEditorPreview @@ -94,6 +92,10 @@ module.exports = class PlayLevelView extends View if localStorage? localStorage["lastLevel"] = @levelID + onLevelLoadError: (e) => + msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.") + @$el.html('
' + msg + '
') + setLevel: (@level, @supermodel) -> @god?.level = @level.serialize @supermodel if @world @@ -181,21 +183,21 @@ module.exports = class PlayLevelView extends View onWindowResize: (s...) -> $('#pointer').css('opacity', 0.0) - onDisableControls: (e) => + onDisableControls: (e) -> return if e.controls and not ('level' in e.controls) @shortcutsEnabled = false @wasFocusedOn = document.activeElement $('body').focus() - onEnableControls: (e) => + onEnableControls: (e) -> return if e.controls? and not ('level' in e.controls) @shortcutsEnabled = true $(@wasFocusedOn).focus() if @wasFocusedOn @wasFocusedOn = null - onDonePressed: => @showVictory() + onDonePressed: -> @showVictory() - onShowVictory: (e) => + onShowVictory: (e) -> console.log 'show vict', e $('#level-done-button').show() @showVictory() if e.showModal @@ -221,7 +223,7 @@ module.exports = class PlayLevelView extends View @openModalView new InfiniteLoopModal() window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name - onPlayNextLevel: => + onPlayNextLevel: -> nextLevel = @getNextLevel() nextLevelID = nextLevel.get('slug') or nextLevel.id url = "/play/level/#{nextLevelID}" @@ -235,7 +237,7 @@ module.exports = class PlayLevelView extends View levels = @supermodel.getModels(Level) return l for l in levels when l.get('original') is nextLevelOriginal - onHighlightDom: (e) => + onHighlightDom: (e) -> if e.delay delay = e.delay delete e.delay @@ -289,16 +291,16 @@ module.exports = class PlayLevelView extends View ), 1) - animatePointer: => + animatePointer: -> pointer = $('#pointer') pointer.css('transition', 'all 0.6s ease-out') pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)") setTimeout((=> pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800) - onFocusDom: (e) => $(e.selector).focus() + onFocusDom: (e) -> $(e.selector).focus() - onEndHighlight: => + onEndHighlight: -> $('#pointer').css('opacity', 0.0) clearInterval(@pointerInterval) @@ -380,6 +382,8 @@ module.exports = class PlayLevelView extends View destroy: -> super() + @supermodel.off 'error', @onLevelLoadError + @levelLoader?.off 'loaded-all', @onLevelLoaderLoaded @levelLoader?.destroy() @surface?.destroy() @god?.destroy() @@ -393,3 +397,8 @@ module.exports = class PlayLevelView extends View #@instance.save() unless @instance.loading console.profileEnd?() if PROFILE_ME @session.off 'change:multiplayer', @onMultiplayerChanged, @ + @onLevelLoadError = null + @onLevelLoaderLoaded = null + @onSupermodelLoadedOne = null + @preloadNextLevel = null + @saveScreenshot = null