SuperModel = require 'models/SuperModel' utils = require 'lib/utils' CocoClass = require 'lib/CocoClass' loadingScreenTemplate = require 'templates/loading' visibleModal = null waitingModal = null classCount = 0 makeScopeName = -> "view-scope-#{classCount++}" doNothing = -> module.exports = class CocoView extends Backbone.View startsLoading: false cache: false # signals to the router to keep this view around template: -> '' events: 'click a': 'toggleModal' 'click button': 'toggleModal' 'click li': 'toggleModal' subscriptions: {} shortcuts: {} # Setup, Teardown constructor: (options) -> @supermodel ?= options?.supermodel or new SuperModel() @options = options @subscriptions = utils.combineAncestralObject(@, 'subscriptions') @events = utils.combineAncestralObject(@, 'events') @scope = makeScopeName() @shortcuts = utils.combineAncestralObject(@, 'shortcuts') @subviews = {} @listenToShortcuts() # Backbone.Mediator handles subscription setup/teardown automatically super options destroy: -> @stopListening() @off() @stopListeningToShortcuts() @undelegateEvents() # removes both events and subs view.destroy() for id, view of @subviews $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed @[key] = undefined for key, value of @ @destroyed = true @off = doNothing @destroy = doNothing afterInsert: -> willDisappear: -> # the router removes this view but this view will be cached @undelegateEvents() @hidden = true @stopListeningToShortcuts() view.willDisappear() for id, view of @subviews didReappear: -> # the router brings back this view from the cache @delegateEvents() @hidden = false @listenToShortcuts() view.didReappear() for id, view of @subviews # View Rendering render: -> return @ unless me super() return @template if _.isString(@template) @$el.html @template(@getRenderData()) @afterRender() @showLoading() if @startsLoading @$el.i18n() @ getRenderData: (context) -> context ?= {} context.isProduction = document.location.href.search(/codecombat.com/) isnt -1 context.me = me context.pathname = document.location.pathname # like "/play/level" context.fbRef = context.pathname.replace(/[^a-zA-Z0-9+/=\-.:_]/g, '').slice(0, 40) or 'home' context.isMobile = @isMobile() context.isIE = @isIE() context afterRender: -> # Modals 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. 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 @openModalView(view) openModalView: (modalView, softly=false) -> return if waitingModal # can only have one waiting at once if visibleModal waitingModal = modalView return if softly return visibleModal.hide() if visibleModal.$el.is(':visible') # close, then this will get called again return @modalClosed(visibleModal) # was closed, but modalClosed was not called somehow 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 window.currentModal = modalView @getRootView().stopListeningToShortcuts(true) modalClosed: => visibleModal.willDisappear() if visibleModal visibleModal.destroy() visibleModal = null window.currentModal = null #$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed if waitingModal wm = waitingModal waitingModal = null @openModalView(wm) else @getRootView().listenToShortcuts(true) Backbone.Mediator.publish 'modal-closed' # Loading RootViews showLoading: ($el=@$el) -> $el.find('>').addClass('hidden') $el.append($('
') .append('