diff --git a/app/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee
index d5e061eb5..035961159 100644
--- a/app/schemas/subscriptions/tome.coffee
+++ b/app/schemas/subscriptions/tome.coffee
@@ -126,3 +126,7 @@ module.exports =
'tome:winnability-updated': c.object {title: 'Winnability Updated', description: 'When we think we can now win (or can no longer win), we may want to emphasize the submit button versus the run button (or vice versa), so this fires when we get new goal states (even preloaded goal states) suggesting success or failure change.', required: ['winnable']},
winnable: {type: 'boolean'}
+
+ 'tome:show-problem-alert': c.object {title: 'Show Problem Alert', description: 'A problem alert needs to be shown.', required: ['problem']},
+ problem: {type: 'object'}
+ lineOffsetPx: {type: ['number', 'undefined']}
diff --git a/app/styles/play/level/tome/problem_alert.sass b/app/styles/play/level/tome/problem_alert.sass
index 2ffb62181..42c7be399 100644
--- a/app/styles/play/level/tome/problem_alert.sass
+++ b/app/styles/play/level/tome/problem_alert.sass
@@ -1,16 +1,17 @@
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
-.problem-alert
+
+#problem-alert-view.problem-alert
z-index: 10
position: absolute
// Position these at the end of the spell editor, right above the spell toolbar.
- bottom: -20px
- left: 10px
- right: 10px
+ top: 45px
+ right: 500px
background: transparent
border: 1px solid transparent
padding: 0
+ font-size: 18px
text-shadow: none
color: white
word-wrap: break-word
diff --git a/app/templates/play/level.jade b/app/templates/play/level.jade
index 6396233dc..b012ac1a8 100644
--- a/app/templates/play/level.jade
+++ b/app/templates/play/level.jade
@@ -20,6 +20,8 @@
#gold-view
+ #problem-alert-view
+
#level-chat-view
#multiplayer-status-view
diff --git a/app/templates/play/level/tome/problem_alert.jade b/app/templates/play/level/tome/problem_alert.jade
index 25df2198a..0ca57fe69 100644
--- a/app/templates/play/level/tome/problem_alert.jade
+++ b/app/templates/play/level/tome/problem_alert.jade
@@ -1,4 +1,4 @@
-button.close(type="button", data-dismiss="alert") ×
+button.close(type="button") ×
if hint
span.problem-title!= hint
br
diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee
index 1c31dfc64..84385a055 100644
--- a/app/views/play/level/PlayLevelView.coffee
+++ b/app/views/play/level/PlayLevelView.coffee
@@ -23,6 +23,7 @@ RealTimeCollection = require 'collections/RealTimeCollection'
# subviews
LevelLoadingView = require './LevelLoadingView'
+ProblemAlertView = require './tome/ProblemAlertView'
TomeView = require './tome/TomeView'
ChatView = require './LevelChatView'
HUDView = require './LevelHUDView'
@@ -249,6 +250,7 @@ module.exports = class PlayLevelView extends RootView
@insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session
if @level.get('type') in ['ladder', 'hero-ladder']
@insertSubView new MultiplayerStatusView levelID: @levelID, session: @session, level: @level
+ @insertSubView new ProblemAlertView {}
worldName = utils.i18n @level.attributes, 'name'
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel}
#_.delay (=> Backbone.Mediator.publish('level:set-debug', debug: true)), 5000 if @isIPadApp() # if me.displayName() is 'Nick'
diff --git a/app/views/play/level/tome/Problem.coffee b/app/views/play/level/tome/Problem.coffee
index 5c8e3d2e1..027cad84b 100644
--- a/app/views/play/level/tome/Problem.coffee
+++ b/app/views/play/level/tome/Problem.coffee
@@ -1,20 +1,14 @@
-ProblemAlertView = require './ProblemAlertView'
Range = ace.require('ace/range').Range
module.exports = class Problem
annotation: null
- alertView: null
markerRange: null
- constructor: (@aether, @aetherProblem, @ace, withAlert=false, isCast=false, @levelID) ->
+ constructor: (@aether, @aetherProblem, @ace, isCast=false, @levelID) ->
@buildAnnotation()
- @buildAlertView() if withAlert
@buildMarkerRange() if isCast
Backbone.Mediator.publish("problem:problem-created", line:@annotation.row, text: @annotation.text) if application.isIPadApp
destroy: ->
- unless @alertView?.destroyed
- @alertView?.$el?.remove()
- @alertView?.destroy()
@removeMarkerRange()
@userCodeProblem.off() if @userCodeProblem
@@ -29,11 +23,6 @@ module.exports = class Problem
text: text,
type: @aetherProblem.level ? 'error'
- buildAlertView: ->
- @alertView = new ProblemAlertView problem: @
- @alertView.render()
- $(@ace.container).append @alertView.el
-
buildMarkerRange: ->
return unless @aetherProblem.range
[start, end] = @aetherProblem.range
diff --git a/app/views/play/level/tome/ProblemAlertView.coffee b/app/views/play/level/tome/ProblemAlertView.coffee
index 7f43af83e..e19163df9 100644
--- a/app/views/play/level/tome/ProblemAlertView.coffee
+++ b/app/views/play/level/tome/ProblemAlertView.coffee
@@ -3,32 +3,68 @@ template = require 'templates/play/level/tome/problem_alert'
{me} = require 'lib/auth'
module.exports = class ProblemAlertView extends CocoView
+ id: 'problem-alert-view'
className: 'problem-alert'
template: template
- subscriptions: {}
+ subscriptions:
+ 'tome:show-problem-alert': 'onShowProblemAlert'
+ 'tome:manual-cast': 'onHideProblemAlert'
+ 'real-time-multiplayer:manual-cast': 'onHideProblemAlert'
events:
'click .close': 'onRemoveClicked'
constructor: (options) ->
super options
- @problem = options.problem
+ if options.problem?
+ @problem = options.problem
+ @onWindowResize()
+ else
+ @$el.hide()
+ $(window).on 'resize', @onWindowResize
+
+ destroy: ->
+ $(window).off 'resize', @onWindowResize
+ super()
getRenderData: (context={}) ->
context = super context
- format = (s) -> s?.replace(//g, '>').replace(/\n/g, '
')
- context.message = format @problem.aetherProblem.message
- context.hint = format @problem.aetherProblem.hint
+ if @problem?
+ format = (s) -> s?.replace(//g, '>').replace(/\n/g, '
')
+ context.message = format @problem.aetherProblem.message
+ context.hint = format @problem.aetherProblem.hint
context
afterRender: ->
super()
- @$el.addClass('alert').addClass("alert-#{@problem.aetherProblem.level}").hide().fadeIn('slow')
- @$el.addClass('no-hint') unless @problem.aetherProblem.hint
- Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0
+ if @problem?
+ @$el.addClass('alert').addClass("alert-#{@problem.aetherProblem.level}").hide().fadeIn('slow')
+ @$el.addClass('no-hint') unless @problem.aetherProblem.hint
+ Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0
+
+ onShowProblemAlert: (data) ->
+ return if @problem? and data.problem.aetherProblem.message is @problem.aetherProblem.message and data.problem.aetherProblem.hint is @problem.aetherProblem.hint
+ return unless $('#code-area').is(":visible")
+ @problem = data.problem
+ @lineOffsetPx = data.lineOffsetPx or 0
+ @$el.show()
+ @onWindowResize()
+ @render()
+
+ onHideProblemAlert: ->
+ @onRemoveClicked()
onRemoveClicked: ->
- @$el.remove()
- @destroy()
- #@problem.destroy() # let's try leaving the annotations / marker ranges alone
+ @$el.hide()
+ @problem = null
+
+ onWindowResize: (e) =>
+ # TODO: This all seems a little hacky
+ if @problem?
+ @$el.css('left', $('#goals-view').outerWidth(true) + 'px')
+ @$el.css('right', $('#code-area').outerWidth(true) + 'px')
+
+ # 45px from top roughly aligns top of alert with top of first code line
+ # TODO: calculate this in a more dynamic, less sketchy way
+ @$el.css('top', (45 + @lineOffsetPx) + 'px')
diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee
index 17477be54..bd9ce0eb4 100644
--- a/app/views/play/level/tome/SpellView.coffee
+++ b/app/views/play/level/tome/SpellView.coffee
@@ -515,7 +515,13 @@ module.exports = class SpellView extends CocoView
for aetherProblem, problemIndex in aether.getAllProblems()
continue if key = aetherProblem.userInfo?.key and key of seenProblemKeys
seenProblemKeys[key] = true if key
- @problems.push problem = new Problem aether, aetherProblem, @ace, isCast and problemIndex is 0, isCast, @spell.levelID
+ @problems.push problem = new Problem aether, aetherProblem, @ace, isCast, @spell.levelID
+ if isCast and problemIndex is 0
+ if problem.aetherProblem.range?
+ # Line height is 20px
+ # TODO: Can we get line height dynamically from ace?
+ lineOffsetPx = problem.aetherProblem.range[0].row * 20 - @ace.session.getScrollTop()
+ Backbone.Mediator.publish 'tome:show-problem-alert', problem: problem, lineOffsetPx: lineOffsetPx
@saveUserCodeProblem(aether, aetherProblem) if isCast
annotations.push problem.annotation if problem.annotation
@aceSession.setAnnotations annotations