codecombat/app/core/initialize.coffee
Phoenix Eliot 663c220eaf Show wev-dev iFrame error messages like Aether's
This heavily refactors SpellView and adds infrastructure for receiving and reporting Errors raised by the web-dev iFrame. The web-dev error system, the Aether error system, and the Ace html-worker avoid disturbing each others' errors/annotations (though currently Aether+web-dev errors won't coexist), and they clear/update their own asynchronously.

Show web-dev iFrame errors as Ace annotations

Add functional error banners (with poor messages)

Improve error banners, don't allow duplicate Problems

Refactor setAnnotations override

Convert all constructor calls for Problems

Add comments, clean up

Clean up

Don't clear things unnecessarily

Clean up error message sending from iFrame

Add web-dev:error schema

Clarify error message attributes

Refactor displaying AetherProblems

Refactor displaying user problem banners

Refactor onWebDevError

Set ace styles on updating @problems

Clean up, fix off-by-1 error

Add comment

Show stale web-dev errors differently
Some web-dev errors are generated by "stale" code — code that's still running in the iFrame but doesn't have the player's recent changes.
This shows those errors differently than if they weren't "stale", and suggests they re-run their code.

Hook up web-dev event schema

Destroy ignored duplicate problems

Functionalize a bit of stuff

Fix ProblemAlertView never loading
2016-08-31 10:59:06 -07:00

169 lines
6 KiB
CoffeeScript

Backbone.Mediator.setValidationEnabled false
app = null
channelSchemas =
'auth': require 'schemas/subscriptions/auth'
'bus': require 'schemas/subscriptions/bus'
'editor': require 'schemas/subscriptions/editor'
'errors': require 'schemas/subscriptions/errors'
'ipad': require 'schemas/subscriptions/ipad'
'misc': require 'schemas/subscriptions/misc'
'play': require 'schemas/subscriptions/play'
'surface': require 'schemas/subscriptions/surface'
'tome': require 'schemas/subscriptions/tome'
'god': require 'schemas/subscriptions/god'
'scripts': require 'schemas/subscriptions/scripts'
'web-dev': require 'schemas/subscriptions/web-dev'
'world': require 'schemas/subscriptions/world'
definitionSchemas =
'bus': require 'schemas/definitions/bus'
'misc': require 'schemas/definitions/misc'
init = ->
return if app
if not window.userObject._id
$.ajax '/auth/whoami', cache: false, success: (res) ->
window.userObject = res
init()
return
app = require 'core/application'
setupConsoleLogging()
watchForErrors()
setUpIOSLogging()
path = document.location.pathname
app.testing = _.string.startsWith path, '/test'
app.demoing = _.string.startsWith path, '/demo'
setUpBackboneMediator()
app.initialize()
loadOfflineFonts() unless app.isProduction()
Backbone.history.start({ pushState: true })
handleNormalUrls()
setUpMoment() # Set up i18n for moment
module.exports.init = init
handleNormalUrls = ->
# http://artsy.github.com/blog/2012/06/25/replacing-hashbang-routes-with-pushstate/
$(document).on 'click', "a[href^='/']", (event) ->
href = $(event.currentTarget).attr('href')
# chain 'or's for other black list routes
passThrough = href.indexOf('sign_out') >= 0
# Allow shift+click for new tabs, etc.
if !passThrough && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey
event.preventDefault()
# Remove leading slashes and hash bangs (backward compatablility)
url = href.replace(/^\//,'').replace('\#\!\/','')
# Instruct Backbone to trigger routing events
app.router.navigate url, { trigger: true }
return false
setUpBackboneMediator = ->
Backbone.Mediator.addDefSchemas schemas for definition, schemas of definitionSchemas
Backbone.Mediator.addChannelSchemas schemas for channel, schemas of channelSchemas
Backbone.Mediator.setValidationEnabled document.location.href.search(/codecombat.com/) is -1
if false # Debug which events are being fired
originalPublish = Backbone.Mediator.publish
Backbone.Mediator.publish = ->
console.log 'Publishing event:', arguments... unless /(tick|frame-changed)/.test(arguments[0])
originalPublish.apply Backbone.Mediator, arguments
setUpMoment = ->
{me} = require 'core/auth'
moment.lang me.get('preferredLanguage', true), {}
me.on 'change:preferredLanguage', (me) ->
moment.lang me.get('preferredLanguage', true), {}
setupConsoleLogging = ->
# IE9 doesn't expose console object unless debugger tools are loaded
unless console?
window.console =
info: ->
log: ->
error: ->
debug: ->
unless console.debug
# Needed for IE10 and earlier
console.debug = console.log
watchForErrors = ->
currentErrors = 0
window.onerror = (msg, url, line, col, error) ->
return if currentErrors >= 3
return unless me.isAdmin() or document.location.href.search(/codecombat.com/) is -1 or document.location.href.search(/\/editor\//) isnt -1
++currentErrors
message = "Error: #{msg}<br>Check the JS console for more."
#msg += "\nLine: #{line}" if line?
#msg += "\nColumn: #{col}" if col?
#msg += "\nError: #{error}" if error?
#msg += "\nStack: #{stack}" if stack = error?.stack
unless webkit?.messageHandlers # Don't show these notys on iPad
noty text: message, layout: 'topCenter', type: 'error', killer: false, timeout: 5000, dismissQueue: true, maxVisible: 3, callback: {onClose: -> --currentErrors}
Backbone.Mediator.publish 'application:error', message: "Line #{line} of #{url}:\n#{msg}" # For iOS app
window.addIPadSubscription = (channel) ->
window.iPadSubscriptions[channel] = true
window.removeIPadSubscription = (channel) ->
window.iPadSubscriptions[channel] = false
setUpIOSLogging = ->
return unless webkit?.messageHandlers
for level in ['debug', 'log', 'info', 'warn', 'error']
do (level) ->
originalLog = console[level]
console[level] = ->
originalLog.apply console, arguments
try
webkit?.messageHandlers?.consoleLogHandler?.postMessage level: level, arguments: (a?.toString?() ? ('' + a) for a in arguments)
catch e
webkit?.messageHandlers?.consoleLogHandler?.postMessage level: level, arguments: ['could not post log: ' + e]
loadOfflineFonts = ->
$('head').prepend '<link rel="stylesheet" type="text/css" href="/fonts/openSansCondensed.css">'
$('head').prepend '<link rel="stylesheet" type="text/css" href="/fonts/openSans.css">'
# This is so hacky... hopefully it's restrictive enough to not be slow.
# We could also keep a list of events we are actually subscribed for and only try to send those over.
seen = null
window.serializeForIOS = serializeForIOS = (obj, depth=3) ->
return {} unless depth
root = not seen?
seen ?= []
clone = {}
keysHandled = 0
for own key, value of obj
continue if ++keysHandled > 50
if not value
clone[key] = value
else if value is window or value.firstElementChild or value.preventDefault
null # Don't include these things
else if value in seen
null # No circular references
else if _.isArray value
clone[key] = (serializeForIOS(child, depth - 1) for child in value)
seen.push value
else if _.isObject value
value = value.attributes if value.id and value.attributes
clone[key] = serializeForIOS value, depth - 1
seen.push value
else
clone[key] = value
seen = null if root
clone
window.onbeforeunload = (e) ->
leavingMessage = _.result(window.currentView, 'onLeaveMessage')
if leavingMessage
return leavingMessage
else
return
$ -> init()