Fixed a ton more leaks.

This commit is contained in:
Nick Winter 2014-02-12 12:41:41 -08:00
parent ad8a8e00e8
commit 5655084981
17 changed files with 162 additions and 113 deletions

View file

@ -131,6 +131,8 @@ module.exports = class God
@dead = true @dead = true
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @) Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
@goalManager = null @goalManager = null
@fillWorkerPool = null
@simulateWorld = null
#### Bad code for running worlds on main thread (profiling / IE9) #### #### Bad code for running worlds on main thread (profiling / IE9) ####
simulateWorld: => simulateWorld: =>
@ -207,6 +209,7 @@ class Angel
if @worker if @worker
worker = @worker worker = @worker
_.defer -> worker.terminate _.defer -> worker.terminate
@worker.removeEventListener 'message', @onWorkerMessage
@worker = null @worker = null
@ @
@ -217,6 +220,7 @@ class Angel
terminate: => terminate: =>
@worker?.terminate() @worker?.terminate()
@worker?.removeEventListener 'message', @onWorkerMessage
@worker = null @worker = null
return if @dead return if @dead
@free() @free()
@ -225,6 +229,10 @@ class Angel
destroy: -> destroy: ->
@dead = true @dead = true
@abort() @abort()
@terminate = null
@testWorker = null
@condemnWorker = null
@onWorkerMessage = null
testWorker: => testWorker: =>
@worker.postMessage {func: 'reportIn'} @worker.postMessage {func: 'reportIn'}
@ -235,7 +243,9 @@ class Angel
@abort() @abort()
listen: -> listen: ->
@worker.addEventListener 'message', (event) => @worker.addEventListener 'message', @onWorkerMessage
onWorkerMessage: (event) =>
switch event.data.type switch event.data.type
when 'new-world' when 'new-world'
@god.beholdWorld @, event.data.serialized, event.data.goalStates @god.beholdWorld @, event.data.serialized, event.data.goalStates

View file

@ -28,7 +28,7 @@ module.exports = class LevelLoader extends CocoClass
@loadLevelModels() @loadLevelModels()
@loadAudio() @loadAudio()
@playJingle() @playJingle()
setTimeout (=> @update()), 1 # lets everything else resolve first _.defer @update # Lets everything else resolve first
playJingle: -> playJingle: ->
jingles = ["ident_1", "ident_2"] jingles = ["ident_1", "ident_2"]
@ -80,7 +80,7 @@ module.exports = class LevelLoader extends CocoClass
# building = thangType.buildSpriteSheet options # building = thangType.buildSpriteSheet options
# if building # if building
# @spriteSheetsToBuild += 1 # @spriteSheetsToBuild += 1
# thangType.on 'build-complete', => # thangType.once 'build-complete', =>
# @spriteSheetsBuilt += 1 # @spriteSheetsBuilt += 1
# @notifyProgress() # @notifyProgress()
@ -91,7 +91,7 @@ module.exports = class LevelLoader extends CocoClass
# Things to do when either the Session or Supermodel load # Things to do when either the Session or Supermodel load
update: -> update: =>
@notifyProgress() @notifyProgress()
return if @updateCompleted return if @updateCompleted
@ -153,7 +153,7 @@ module.exports = class LevelLoader extends CocoClass
return unless building return unless building
console.log 'Building:', thangType.get('name'), options console.log 'Building:', thangType.get('name'), options
@spriteSheetsToBuild += 1 @spriteSheetsToBuild += 1
thangType.on 'build-complete', => thangType.once 'build-complete', =>
@spriteSheetsBuilt += 1 @spriteSheetsBuilt += 1
@notifyProgress() @notifyProgress()
@ -208,6 +208,11 @@ module.exports = class LevelLoader extends CocoClass
@trigger 'loaded-all' if @progress() is 1 @trigger 'loaded-all' if @progress() is 1
destroy: -> destroy: ->
super()
@world = null # don't hold onto garbage @world = null # don't hold onto garbage
@supermodel.off 'loaded-one', @onSupermodelLoadedOne @supermodel.off 'loaded-one', @onSupermodelLoadedOne
super() @onSessionLoaded = null
@onSupermodelError = null
@onSupermodelLoadedOne = null
@onSupermodelLoadedAll = null
@notifyProgress = null

View file

@ -199,15 +199,13 @@ module.exports = class Camera extends CocoClass
@tweenProgress = 0.01 @tweenProgress = 0.01
createjs.Tween.get(@) createjs.Tween.get(@)
.to({tweenProgress: 1.0}, time, createjs.Ease.getPowInOut(3)) .to({tweenProgress: 1.0}, time, createjs.Ease.getPowInOut(3))
.call @onTweenEnd .call @finishTween
else else
@target = newTarget @target = newTarget
@zoom = newZoom @zoom = newZoom
@updateZoom true @updateZoom true
onTweenEnd: => @finishTween()
finishTween: (abort=false) => finishTween: (abort=false) =>
createjs.Tween.removeTweens(@) createjs.Tween.removeTweens(@)
return unless @newTarget return unless @newTarget
@ -263,4 +261,5 @@ module.exports = class Camera extends CocoClass
destroy: -> destroy: ->
super() super()
@onTweenEnd = null createjs.Tween.removeTweens @
@finishTween = null

View file

@ -80,6 +80,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
super() super()
mark.destroy() for name, mark of @marks mark.destroy() for name, mark of @marks
label.destroy() for name, label of @labels label.destroy() for name, label of @labels
@imageObject?.off 'animationend', @playNextAction
@playNextAction = null
@displayObject?.off()
toString: -> "<CocoSprite: #{@thang?.id}>" toString: -> "<CocoSprite: #{@thang?.id}>"
@ -108,7 +111,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@displayObject.sprite = @ @displayObject.sprite = @
@displayObject.layerPriority = @thangType.get 'layerPriority' @displayObject.layerPriority = @thangType.get 'layerPriority'
@displayObject.name = @thang?.spriteName or @thangType.get 'name' @displayObject.name = @thang?.spriteName or @thangType.get 'name'
@imageObject.on 'animationend', @onActionEnd @imageObject.on 'animationend', @playNextAction
################################################## ##################################################
# QUEUEING AND PLAYING ACTIONS # QUEUEING AND PLAYING ACTIONS
@ -126,10 +129,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@currentRootAction = action @currentRootAction = action
@playNextAction() @playNextAction()
onActionEnd: (e) => @playNextAction()
onSurfaceTicked: (e) -> @age += e.dt onSurfaceTicked: (e) -> @age += e.dt
playNextAction: -> playNextAction: =>
@playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length @playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length
playAction: (action) -> playAction: (action) ->
@ -411,7 +413,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers' sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers'
@instance?.stop() @instance?.stop()
if @instance = @playSound sound, false 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 @notifySpeechUpdated e
onClearDialogue: (e) -> onClearDialogue: (e) ->

View file

@ -13,8 +13,8 @@ module.exports = class MusicPlayer extends CocoClass
'audio-player:loaded': 'onAudioLoaded' 'audio-player:loaded': 'onAudioLoaded'
constructor: -> constructor: ->
super(arguments...) super arguments...
me.on('change:music', @onMusicSettingChanged, @) me.on 'change:music', @onMusicSettingChanged, @
onAudioLoaded: -> onAudioLoaded: ->
@onPlayMusic(@standingBy) if @standingBy @onPlayMusic(@standingBy) if @standingBy
@ -49,3 +49,6 @@ module.exports = class MusicPlayer extends CocoClass
createjs.Tween.removeTweens(@currentMusic) createjs.Tween.removeTweens(@currentMusic)
@currentMusic.volume = if me.get('music') then 1.0 else 0.0 @currentMusic.volume = if me.get('music') then 1.0 else 0.0
destroy: ->
super()
me.off 'change:music', @onMusicSettingChanged, @

View file

@ -90,13 +90,18 @@ module.exports = Surface = class Surface extends CocoClass
@dimmer?.destroy() @dimmer?.destroy()
@stage.clear() @stage.clear()
@musicPlayer?.destroy() @musicPlayer?.destroy()
@stage.removeAllChildren()
@stage.removeEventListener 'stagemousemove', @onMouseMove @stage.removeEventListener 'stagemousemove', @onMouseMove
@stage.removeEventListener 'stagemousedown', @onMouseDown @stage.removeEventListener 'stagemousedown', @onMouseDown
@stage.removeAllEventListeners() @stage.removeAllEventListeners()
@stage.enableDOMEvents false
@stage.enableMouseOver 0
@playScrubbedSounds = null @playScrubbedSounds = null
@onMouseMove = null @onMouseMove = null
@onMouseDown = null @onMouseDown = null
@tick = null @tick = null
@canvas.off 'mousewheel', @onMouseWheel
@onMouseWheel = null
setWorld: (@world) -> setWorld: (@world) ->
@worldLoaded = true @worldLoaded = true
@ -337,7 +342,7 @@ module.exports = Surface = class Surface extends CocoClass
@stage.enableMouseOver(10) @stage.enableMouseOver(10)
@stage.addEventListener 'stagemousemove', @onMouseMove @stage.addEventListener 'stagemousemove', @onMouseMove
@stage.addEventListener 'stagemousedown', @onMouseDown @stage.addEventListener 'stagemousedown', @onMouseDown
@hookUpZoomControls() @canvas.on 'mousewheel', @onMouseWheel
@hookUpChooseControls() if @options.choosing @hookUpChooseControls() if @options.choosing
console.log "Setting fps", @world.frameRate unless @world.frameRate is 30 console.log "Setting fps", @world.frameRate unless @world.frameRate is 30
createjs.Ticker.setFPS @world.frameRate createjs.Ticker.setFPS @world.frameRate
@ -436,8 +441,7 @@ module.exports = Surface = class Surface extends CocoClass
onBackground = not @stage.hitTest e.stageX, e.stageY 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 Backbone.Mediator.publish 'surface:stage-mouse-down', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e
hookUpZoomControls: -> onMouseWheel: (e) =>
@canvas.bind 'mousewheel', (e) =>
# https://github.com/brandonaaron/jquery-mousewheel # https://github.com/brandonaaron/jquery-mousewheel
e.preventDefault() e.preventDefault()
return if @disabled return if @disabled

View file

@ -55,7 +55,7 @@ class CocoModel extends Backbone.Model
@constructor.schema = new CocoSchema(@urlRoot) @constructor.schema = new CocoSchema(@urlRoot)
@constructor.schema.fetch() @constructor.schema.fetch()
@constructor.schema.on 'sync', => @constructor.schema.once 'sync', =>
@constructor.schema.loaded = true @constructor.schema.loaded = true
@addSchemaDefaults() @addSchemaDefaults()
@trigger 'schema-loaded' @trigger 'schema-loaded'

View file

@ -8,7 +8,7 @@ class SuperModel
@mustPopulate = model @mustPopulate = model
model.saveBackups = @shouldSaveBackups(model) model.saveBackups = @shouldSaveBackups(model)
model.fetch() unless model.loaded or model.loading 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 model.once('error', @modelErrored) unless model.loaded
url = model.url() url = model.url()
@models[url] = model unless @models[url]? @models[url] = model unless @models[url]?
@ -23,7 +23,7 @@ class SuperModel
modelLoaded: (model) => modelLoaded: (model) =>
schema = model.schema() 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 = model.getReferencedModels(model.attributes, schema.attributes)
refs = [] unless @mustPopulate is model or @shouldPopulate(model) refs = [] unless @mustPopulate is model or @shouldPopulate(model)
# console.log 'Loaded', model.get('name') # console.log 'Loaded', model.get('name')
@ -33,7 +33,7 @@ class SuperModel
continue if @models[refURL] continue if @models[refURL]
@models[refURL] = ref @models[refURL] = ref
ref.fetch() ref.fetch()
ref.on 'sync', @modelLoaded ref.once 'sync', @modelLoaded
@trigger 'loaded-one', model: model @trigger 'loaded-one', model: model
@trigger 'loaded-all' if @finished() @trigger 'loaded-all' if @finished()

View file

@ -28,7 +28,6 @@ module.exports = class User extends CocoModel
profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}" profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}"
script = $("<script src='#{profileUrl}' type='text/javascript'></script>") script = $("<script src='#{profileUrl}' type='text/javascript'></script>")
$('head').append(script) $('head').append(script)
$('body').on('load',(e)->console.log('we did it!', e))
window[functionName] = (profile) => window[functionName] = (profile) =>
@gravatarProfile = profile @gravatarProfile = profile
@trigger('change', @) @trigger('change', @)

View file

@ -42,6 +42,7 @@ module.exports = class CocoView extends Backbone.View
@undelegateEvents() # removes both events and subs @undelegateEvents() # removes both events and subs
view.destroy() for id, view of @subviews view.destroy() for id, view of @subviews
@modalClosed = null @modalClosed = null
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
afterInsert: -> afterInsert: ->
@ -106,7 +107,7 @@ module.exports = class CocoView extends Backbone.View
modalView.afterInsert() modalView.afterInsert()
visibleModal = modalView visibleModal = modalView
modalOptions = {show: true, backdrop: if modalView.closesOnClickOutside then true else 'static'} 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 window.currentModal = modalView
@getRootView().stopListeningToShortcuts(true) @getRootView().stopListeningToShortcuts(true)
@ -114,6 +115,7 @@ module.exports = class CocoView extends Backbone.View
visibleModal.willDisappear() if visibleModal visibleModal.willDisappear() if visibleModal
visibleModal.destroy() visibleModal.destroy()
visibleModal = null visibleModal = null
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
if waitingModal if waitingModal
wm = waitingModal wm = waitingModal
waitingModal = null waitingModal = null

View file

@ -37,6 +37,7 @@ module.exports = class ControlBarView extends View
setBus: (@bus) -> setBus: (@bus) ->
onPlayerStatesChanged: (e) -> onPlayerStatesChanged: (e) ->
# TODO: this doesn't fire any more. Replacement?
return unless @bus is e.bus return unless @bus is e.bus
numPlayers = _.keys(e.players).length numPlayers = _.keys(e.players).length
return if numPlayers is @numPlayers return if numPlayers is @numPlayers

View file

@ -11,6 +11,8 @@ module.exports = class CastButtonView extends View
'tome:cast-spells': 'onCastSpells' 'tome:cast-spells': 'onCastSpells'
'god:world-load-progress-changed': 'onWorldLoadProgressChanged' 'god:world-load-progress-changed': 'onWorldLoadProgressChanged'
'god:new-world-created': 'onNewWorld' 'god:new-world-created': 'onNewWorld'
'click .cast-button': -> Backbone.Mediator.publish 'tome:manual-cast', {}
'click .cast-options a': 'onCastOptionsClick'
constructor: (options) -> constructor: (options) ->
super options super options
@ -26,8 +28,10 @@ module.exports = class CastButtonView extends View
afterRender: -> afterRender: ->
super() super()
@castButton = $('.cast-button', @$el)
@castButtonGroup = $('.cast-button-group', @$el)
@castOptions = $('.autocast-delays', @$el)
# TODO: use a User setting instead of localStorage # TODO: use a User setting instead of localStorage
@hookUpButtons()
delay = localStorage.getItem 'autocastDelay' delay = localStorage.getItem 'autocastDelay'
delay ?= 5000 delay ?= 5000
@setAutocastDelay delay @setAutocastDelay delay
@ -35,15 +39,7 @@ module.exports = class CastButtonView extends View
attachTo: (spellView) -> attachTo: (spellView) ->
@$el.detach().prependTo(spellView.toolbarView.$el).show() @$el.detach().prependTo(spellView.toolbarView.$el).show()
hookUpButtons: -> onCastOptionsClick: (e) ->
# 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' Backbone.Mediator.publish 'focus-editor'
@castButtonGroup.removeClass 'open' @castButtonGroup.removeClass 'open'
@setAutocastDelay $(e.target).attr 'data-delay' @setAutocastDelay $(e.target).attr 'data-delay'

View file

@ -60,7 +60,7 @@ module.exports = class DebugView extends View
@variableChain = @markerRange = null @variableChain = @markerRange = null
@update() @update()
onMouseOut: (e) => onMouseOut: (e) ->
@variableChain = @markerRange = null @variableChain = @markerRange = null
@update() @update()
@ -138,3 +138,4 @@ module.exports = class DebugView extends View
destroy: -> destroy: ->
super() super()
@ace?.removeEventListener "mousemove", @onMouseMove @ace?.removeEventListener "mousemove", @onMouseMove
@onMouseMove = null

View file

@ -60,3 +60,4 @@ module.exports = class SpellPaletteView extends View
destroy: -> destroy: ->
super() super()
entry.destroy() for entry in @entries entry.destroy() for entry in @entries
@toggleBackground = null

View file

@ -74,47 +74,51 @@ module.exports = class SpellView extends View
$(@ace.container).find('.ace_gutter').on 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick $(@ace.container).find('.ace_gutter').on 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
createACEShortcuts: -> createACEShortcuts: ->
@ace.commands.addCommand @aceCommands = []
addCommand = (c) =>
@ace.commands.addCommand c
@aceCommands.push c.name
addCommand
name: 'run-code' name: 'run-code'
bindKey: {win: 'Shift-Enter|Ctrl-Enter|Ctrl-S', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter|Command-S|Ctrl-S'} bindKey: {win: 'Shift-Enter|Ctrl-Enter|Ctrl-S', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter|Command-S|Ctrl-S'}
exec: (e) => @recompile() exec: (e) => @recompile()
@ace.commands.addCommand addCommand
name: 'toggle-playing' name: 'toggle-playing'
bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'} bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'}
exec: -> Backbone.Mediator.publish 'level-toggle-playing' exec: -> Backbone.Mediator.publish 'level-toggle-playing'
@ace.commands.addCommand addCommand
name: 'end-current-script' name: 'end-current-script'
bindKey: {win: 'Shift-Space', mac: 'Shift-Space'} bindKey: {win: 'Shift-Space', mac: 'Shift-Space'}
exec: -> Backbone.Mediator.publish 'level:shift-space-pressed' exec: -> Backbone.Mediator.publish 'level:shift-space-pressed'
@ace.commands.addCommand addCommand
name: 'end-all-scripts' name: 'end-all-scripts'
bindKey: {win: 'Escape', mac: 'Escape'} bindKey: {win: 'Escape', mac: 'Escape'}
exec: -> Backbone.Mediator.publish 'level:escape-pressed' exec: -> Backbone.Mediator.publish 'level:escape-pressed'
@ace.commands.addCommand addCommand
name: 'toggle-grid' name: 'toggle-grid'
bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'} bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'}
exec: -> Backbone.Mediator.publish 'level-toggle-grid' exec: -> Backbone.Mediator.publish 'level-toggle-grid'
@ace.commands.addCommand addCommand
name: 'toggle-debug' name: 'toggle-debug'
bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'} bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'}
exec: -> Backbone.Mediator.publish 'level-toggle-debug' exec: -> Backbone.Mediator.publish 'level-toggle-debug'
@ace.commands.addCommand addCommand
name: 'toggle-pathfinding' name: 'toggle-pathfinding'
bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'} bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'}
exec: -> Backbone.Mediator.publish 'level-toggle-pathfinding' exec: -> Backbone.Mediator.publish 'level-toggle-pathfinding'
@ace.commands.addCommand addCommand
name: 'level-scrub-forward' name: 'level-scrub-forward'
bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'} bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'}
exec: -> Backbone.Mediator.publish 'level-scrub-forward' exec: -> Backbone.Mediator.publish 'level-scrub-forward'
@ace.commands.addCommand addCommand
name: 'level-scrub-back' name: 'level-scrub-back'
bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'} bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'}
exec: -> Backbone.Mediator.publish 'level-scrub-back' exec: -> Backbone.Mediator.publish 'level-scrub-back'
@ace.commands.addCommand addCommand
name: 'spell-step-forward' name: 'spell-step-forward'
bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'} bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'}
exec: -> Backbone.Mediator.publish 'spell-step-forward' exec: -> Backbone.Mediator.publish 'spell-step-forward'
@ace.commands.addCommand addCommand
name: 'spell-step-backward' name: 'spell-step-backward'
bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'} bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'}
exec: -> Backbone.Mediator.publish 'spell-step-backward' exec: -> Backbone.Mediator.publish 'spell-step-backward'
@ -212,6 +216,9 @@ module.exports = class SpellView extends View
@updateACEText @spell.originalSource @updateACEText @spell.originalSource
@recompile cast @recompile cast
recompileIfNeeded: =>
@recompile() if @recompileNeeded
recompile: (cast=true) => recompile: (cast=true) =>
@setRecompileNeeded false @setRecompileNeeded false
return if @spell.source is @getSource() return if @spell.source is @getSource()
@ -238,7 +245,7 @@ module.exports = class SpellView extends View
autocastDelay = @autocastDelay ? 3000 autocastDelay = @autocastDelay ? 3000
onSignificantChange = [ onSignificantChange = [
_.debounce @setRecompileNeeded, autocastDelay - 100 _.debounce @setRecompileNeeded, autocastDelay - 100
@currentAutocastHandler = _.debounce (=> @recompile() if @recompileNeeded), autocastDelay @currentAutocastHandler = _.debounce @recompileIfNeeded, autocastDelay
] ]
onAnyChange = [ onAnyChange = [
_.debounce @updateAether, 500 _.debounce @updateAether, 500
@ -500,9 +507,17 @@ module.exports = class SpellView extends View
destroy: -> destroy: ->
super() super()
$(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
@firepad?.dispose() @firepad?.dispose()
@ace.destroy() @ace?.commands.removeCommand command for command in @aceCommands
@ace?.destroy()
@ace = null @ace = null
@aceDoc?.off 'change', @onCodeChangeMetaHandler
@aceDoc = null
@aceSession?.selection.off 'changeCursor', @onCursorActivity
@aceSession = null
@debugView?.destroy() @debugView?.destroy()
@spell = null @spell = null
@session.off 'change:multiplayer', @onMultiplayerChanged, @ @session.off 'change:multiplayer', @onMultiplayerChanged, @
for fat in ['notifySpellChanged', 'notifyEditingEnded', 'notifyEditingBegan', 'onFirepadLoaded', 'onLoaded', 'recompile', 'toggleBackground', 'setRecompileNeeded', 'onCursorActivity', 'highlightCurrentLine', 'updateAether', 'onCodeChangeMetaHandler', 'recompileIfNeeded', 'currentAutocastHandler']
@[fat] = null

View file

@ -97,12 +97,12 @@ module.exports = class ThangListEntryView extends View
@$el.popover('setContent').popover('show') @$el.popover('setContent').popover('show')
@$el.parent().parent().parent().i18n() @$el.parent().parent().parent().i18n()
clearTimeout @hideSpellsTimeout if @hideSpellsTimeout clearTimeout @hideSpellsTimeout if @hideSpellsTimeout
popover = @$el.parent().parent().parent().find('.popover') @popover = @$el.parent().parent().parent().find('.popover')
popover.off 'mouseenter mouseleave' @popover.off 'mouseenter mouseleave'
popover.mouseenter (e) => @onMouseEnter() @popover.mouseenter (e) => @onMouseEnter()
popover.mouseleave (e) => @onMouseLeave() @popover.mouseleave (e) => @onMouseLeave()
thangID = @thang.id 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' Backbone.Mediator.publish "level-select-sprite", thangID: thangID, spellName: $(@).data 'spell-name'
hideSpells: => hideSpells: =>
@ -139,3 +139,5 @@ module.exports = class ThangListEntryView extends View
destroy: -> destroy: ->
super() super()
@avatar?.destroy() @avatar?.destroy()
@popover?.off 'mouseenter mouseleave'
@popover?.find('code').off 'click'

View file

@ -76,9 +76,7 @@ module.exports = class PlayLevelView extends View
@sessionID = @getQueryVariable 'session' @sessionID = @getQueryVariable 'session'
$(window).on('resize', @onWindowResize) $(window).on('resize', @onWindowResize)
@supermodel.once 'error', => @supermodel.once 'error', @onLevelLoadError
msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.")
@$el.html('<div class="alert">' + msg + '</div>')
@saveScreenshot = _.throttle @saveScreenshot, 30000 @saveScreenshot = _.throttle @saveScreenshot, 30000
if @isEditorPreview if @isEditorPreview
@ -94,6 +92,10 @@ module.exports = class PlayLevelView extends View
if localStorage? if localStorage?
localStorage["lastLevel"] = @levelID localStorage["lastLevel"] = @levelID
onLevelLoadError: (e) =>
msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.")
@$el.html('<div class="alert">' + msg + '</div>')
setLevel: (@level, @supermodel) -> setLevel: (@level, @supermodel) ->
@god?.level = @level.serialize @supermodel @god?.level = @level.serialize @supermodel
if @world if @world
@ -181,21 +183,21 @@ module.exports = class PlayLevelView extends View
onWindowResize: (s...) -> onWindowResize: (s...) ->
$('#pointer').css('opacity', 0.0) $('#pointer').css('opacity', 0.0)
onDisableControls: (e) => onDisableControls: (e) ->
return if e.controls and not ('level' in e.controls) return if e.controls and not ('level' in e.controls)
@shortcutsEnabled = false @shortcutsEnabled = false
@wasFocusedOn = document.activeElement @wasFocusedOn = document.activeElement
$('body').focus() $('body').focus()
onEnableControls: (e) => onEnableControls: (e) ->
return if e.controls? and not ('level' in e.controls) return if e.controls? and not ('level' in e.controls)
@shortcutsEnabled = true @shortcutsEnabled = true
$(@wasFocusedOn).focus() if @wasFocusedOn $(@wasFocusedOn).focus() if @wasFocusedOn
@wasFocusedOn = null @wasFocusedOn = null
onDonePressed: => @showVictory() onDonePressed: -> @showVictory()
onShowVictory: (e) => onShowVictory: (e) ->
console.log 'show vict', e console.log 'show vict', e
$('#level-done-button').show() $('#level-done-button').show()
@showVictory() if e.showModal @showVictory() if e.showModal
@ -221,7 +223,7 @@ module.exports = class PlayLevelView extends View
@openModalView new InfiniteLoopModal() @openModalView new InfiniteLoopModal()
window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name
onPlayNextLevel: => onPlayNextLevel: ->
nextLevel = @getNextLevel() nextLevel = @getNextLevel()
nextLevelID = nextLevel.get('slug') or nextLevel.id nextLevelID = nextLevel.get('slug') or nextLevel.id
url = "/play/level/#{nextLevelID}" url = "/play/level/#{nextLevelID}"
@ -235,7 +237,7 @@ module.exports = class PlayLevelView extends View
levels = @supermodel.getModels(Level) levels = @supermodel.getModels(Level)
return l for l in levels when l.get('original') is nextLevelOriginal return l for l in levels when l.get('original') is nextLevelOriginal
onHighlightDom: (e) => onHighlightDom: (e) ->
if e.delay if e.delay
delay = e.delay delay = e.delay
delete e.delay delete e.delay
@ -289,16 +291,16 @@ module.exports = class PlayLevelView extends View
), 1) ), 1)
animatePointer: => animatePointer: ->
pointer = $('#pointer') pointer = $('#pointer')
pointer.css('transition', 'all 0.6s ease-out') pointer.css('transition', 'all 0.6s ease-out')
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)") pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)")
setTimeout((=> setTimeout((=>
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800) 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) $('#pointer').css('opacity', 0.0)
clearInterval(@pointerInterval) clearInterval(@pointerInterval)
@ -380,6 +382,8 @@ module.exports = class PlayLevelView extends View
destroy: -> destroy: ->
super() super()
@supermodel.off 'error', @onLevelLoadError
@levelLoader?.off 'loaded-all', @onLevelLoaderLoaded
@levelLoader?.destroy() @levelLoader?.destroy()
@surface?.destroy() @surface?.destroy()
@god?.destroy() @god?.destroy()
@ -393,3 +397,8 @@ module.exports = class PlayLevelView extends View
#@instance.save() unless @instance.loading #@instance.save() unless @instance.loading
console.profileEnd?() if PROFILE_ME console.profileEnd?() if PROFILE_ME
@session.off 'change:multiplayer', @onMultiplayerChanged, @ @session.off 'change:multiplayer', @onMultiplayerChanged, @
@onLevelLoadError = null
@onLevelLoaderLoaded = null
@onSupermodelLoadedOne = null
@preloadNextLevel = null
@saveScreenshot = null