mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-17 19:12:33 -05:00
Adding easy observing of leaderboard matches (except top 5).
This commit is contained in:
parent
c5e47fda50
commit
c977ecc16f
15 changed files with 91 additions and 53 deletions
|
@ -88,6 +88,8 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
@listenToOnce @opponentSession, 'sync', @loadDependenciesForSession
|
@listenToOnce @opponentSession, 'sync', @loadDependenciesForSession
|
||||||
|
|
||||||
loadDependenciesForSession: (session) ->
|
loadDependenciesForSession: (session) ->
|
||||||
|
if me.id isnt session.get 'creator'
|
||||||
|
session.patch = session.save = -> console.error "Not saving session, since we didn't create it."
|
||||||
if session is @session
|
if session is @session
|
||||||
codeLanguage = session.get('codeLanguage') or me.get('aceConfig')?.language or 'python'
|
codeLanguage = session.get('codeLanguage') or me.get('aceConfig')?.language or 'python'
|
||||||
modulePath = "vendor/aether-#{codeLanguage}"
|
modulePath = "vendor/aether-#{codeLanguage}"
|
||||||
|
@ -136,6 +138,7 @@ module.exports = class LevelLoader extends CocoClass
|
||||||
@onWorldNecessitiesLoaded()
|
@onWorldNecessitiesLoaded()
|
||||||
|
|
||||||
addSessionBrowserInfo: (session) ->
|
addSessionBrowserInfo: (session) ->
|
||||||
|
return unless me.id is session.get 'creator'
|
||||||
return unless $.browser?
|
return unless $.browser?
|
||||||
browser = {}
|
browser = {}
|
||||||
browser['desktop'] = $.browser.desktop if $.browser.desktop
|
browser['desktop'] = $.browser.desktop if $.browser.desktop
|
||||||
|
|
|
@ -342,7 +342,7 @@ _.extend LevelSchema.properties,
|
||||||
type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference'
|
type: 'string', links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], format: 'latest-version-original-reference'
|
||||||
}}
|
}}
|
||||||
campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden' # Automatically set by campaign editor.
|
campaign: c.shortString title: 'Campaign', description: 'Which campaign this level is part of (like "desert").', format: 'hidden' # Automatically set by campaign editor.
|
||||||
scoreTypes: c.array {title: 'Score Types', description: 'What metric to show leaderboards for.'},
|
scoreTypes: c.array {title: 'Score Types', description: 'What metric to show leaderboards for.', uniqueItems: true},
|
||||||
c.shortString(title: 'Score Type', 'enum': ['time', 'damage-taken', 'damage-dealt', 'gold-collected', 'difficulty']) # TODO: good version of LoC; total gear value.
|
c.shortString(title: 'Score Type', 'enum': ['time', 'damage-taken', 'damage-dealt', 'gold-collected', 'difficulty']) # TODO: good version of LoC; total gear value.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,17 +16,21 @@
|
||||||
th
|
th
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|
||||||
.rank-cell
|
tbody
|
||||||
font-weight: bold
|
tr.viewable
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
.name-col-cell
|
.rank-cell
|
||||||
max-width: 150px
|
font-weight: bold
|
||||||
white-space: nowrap
|
|
||||||
overflow: hidden
|
|
||||||
text-overflow: ellipsis
|
|
||||||
|
|
||||||
.hero-portrait-cell, .code-language-cell
|
.name-col-cell
|
||||||
background: transparent url(/images/common/code_languages/javascript_small.png) no-repeat center center
|
max-width: 150px
|
||||||
padding: 0 9px
|
white-space: nowrap
|
||||||
background-size: 30px 30px
|
overflow: hidden
|
||||||
height: 30px
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
.hero-portrait-cell, .code-language-cell
|
||||||
|
background: transparent url(/images/common/code_languages/javascript_small.png) no-repeat center center
|
||||||
|
background-size: 30px 30px
|
||||||
|
height: 30px
|
||||||
|
width: 32px
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
.glyphicon.glyphicon-play
|
.glyphicon.glyphicon-play
|
||||||
span(data-i18n="nav.play").home-text Levels
|
span(data-i18n="nav.play").home-text Levels
|
||||||
|
|
||||||
if isMultiplayerLevel
|
if isMultiplayerLevel && !observing
|
||||||
.multiplayer-area-container
|
.multiplayer-area-container
|
||||||
.multiplayer-area
|
.multiplayer-area
|
||||||
.multiplayer-label(data-i18n="play_level.control_bar_multiplayer")
|
.multiplayer-label(data-i18n="play_level.control_bar_multiplayer")
|
||||||
|
@ -28,17 +28,19 @@ else
|
||||||
|
|
||||||
.buttons-area
|
.buttons-area
|
||||||
|
|
||||||
button.btn.btn-inverse#game-menu-button(title="Show game menu")
|
if !observing
|
||||||
.hamburger
|
button.btn.btn-inverse#game-menu-button(title="Show game menu")
|
||||||
span.icon-bar
|
.hamburger
|
||||||
span.icon-bar
|
span.icon-bar
|
||||||
span.icon-bar
|
span.icon-bar
|
||||||
span.game-menu-text(data-i18n="play_level.game_menu") Game Menu
|
span.icon-bar
|
||||||
|
span.game-menu-text(data-i18n="play_level.game_menu") Game Menu
|
||||||
|
|
||||||
if spectateGame
|
if spectateGame
|
||||||
button.btn.btn-xs.btn-inverse.banner#next-game-button(title="Next Game", data-i18n="play_level.next-game") Next game!
|
button.btn.btn-xs.btn-inverse.banner#next-game-button(title="Next Game", data-i18n="play_level.next-game") Next game!
|
||||||
|
|
||||||
button.btn.btn-xs.btn-primary.banner#level-done-button(data-i18n="play_level.done") Done
|
if !observing
|
||||||
|
button.btn.btn-xs.btn-primary.banner#level-done-button(data-i18n="play_level.done") Done
|
||||||
|
|
||||||
if me.get('anonymous')
|
if me.get('anonymous')
|
||||||
button.btn.btn-xs.btn-primary.banner#control-bar-sign-up-button(data-toggle='coco-modal', data-target='core/AuthModal', data-i18n="signup.sign_up")
|
button.btn.btn-xs.btn-primary.banner#control-bar-sign-up-button(data-toggle='coco-modal', data-target='core/AuthModal', data-i18n="signup.sign_up")
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose)
|
button.btn.btn-lg.btn-illustrated.cast-button(title=castVerbose)
|
||||||
span(data-i18n="play_level.tome_run_button_ran") Ran
|
span(data-i18n="play_level.tome_run_button_ran") Ran
|
||||||
|
|
||||||
button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose)
|
if !observing
|
||||||
span(data-i18n="play_level.tome_submit_button") Submit
|
button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose)
|
||||||
span.spl.secret.submit-again-time
|
span(data-i18n="play_level.tome_submit_button") Submit
|
||||||
|
span.spl.secret.submit-again-time
|
||||||
button.btn.btn-lg.btn-illustrated.done-button.secret
|
|
||||||
span(data-i18n="play_level.done") Done
|
button.btn.btn-lg.btn-illustrated.done-button.secret
|
||||||
|
span(data-i18n="play_level.done") Done
|
||||||
|
|
|
@ -11,16 +11,23 @@ if topScores
|
||||||
th(colspan=4, data-i18n="general.player")
|
th(colspan=4, data-i18n="general.player")
|
||||||
th(data-i18n="general.score")
|
th(data-i18n="general.score")
|
||||||
th(data-i18n="general.when")
|
th(data-i18n="general.when")
|
||||||
|
th
|
||||||
tbody
|
tbody
|
||||||
for row, rank in topScores
|
for row, rank in topScores
|
||||||
- var isMyRow = row.creator == me.id
|
- var isMyRow = row.creator == me.id
|
||||||
tr(class=isMyRow ? "success" : "", data-player-id=row.creator, data-session-id=row.id)
|
- var viewable = rank >= 5 || me.isAdmin();
|
||||||
|
tr(class=isMyRow ? "success" : "" + (viewable ? " viewable" : ""), data-player-id=row.creator, data-session-id=row.session, title=viewable ? "View solution" : "Can't view top 5 solutions")
|
||||||
td.rank-cell= rank + 1
|
td.rank-cell= rank + 1
|
||||||
td.code-language-cell(style="background-image: url(/images/common/code_languages/#{row.codeLanguage}_small.png)" title=_.string.capitalize(row.codeLanguage))
|
td.code-language-cell(style="background-image: url(/images/common/code_languages/#{row.codeLanguage}_small.png)" title=_.string.capitalize(row.codeLanguage))
|
||||||
td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{row.hero}/portrait.png)")
|
td.hero-portrait-cell(style="background-image: url(/file/db/thang.type/#{row.hero}/portrait.png)")
|
||||||
td.name-col-cell= row.creatorName || "Anonymous"
|
td.name-col-cell= row.creatorName || "Anonymous"
|
||||||
td.score-cell= row.score
|
td.score-cell= row.score
|
||||||
td.ago-cell= row.ago
|
td.ago-cell= row.ago
|
||||||
|
td.viewable-cell
|
||||||
|
if viewable
|
||||||
|
.glyphicon.glyphicon-eye-open
|
||||||
|
else
|
||||||
|
.glyphicon.glyphicon-eye-close
|
||||||
else if loading
|
else if loading
|
||||||
h3(data-i18n="common.loading")
|
h3(data-i18n="common.loading")
|
||||||
else
|
else
|
||||||
|
|
|
@ -206,7 +206,7 @@ module.exports = class CampaignView extends RootView
|
||||||
for nextLevelOriginal in level.nextLevels ? []
|
for nextLevelOriginal in level.nextLevels ? []
|
||||||
if nextLevel = _.find(@campaign.renderedLevels, original: nextLevelOriginal)
|
if nextLevel = _.find(@campaign.renderedLevels, original: nextLevelOriginal)
|
||||||
@createLine level.position, nextLevel.position
|
@createLine level.position, nextLevel.position
|
||||||
@showLeaderboard @options.justBeatLevel?.get('slug') if @options.showLeaderboard #or true
|
@showLeaderboard @options.justBeatLevel?.get('slug') if @options.showLeaderboard or true
|
||||||
@applyCampaignStyles()
|
@applyCampaignStyles()
|
||||||
@testParticles()
|
@testParticles()
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ module.exports = class CampaignView extends RootView
|
||||||
@openModalView authModal
|
@openModalView authModal
|
||||||
|
|
||||||
showLeaderboard: (levelSlug) ->
|
showLeaderboard: (levelSlug) ->
|
||||||
#levelSlug ?= 'keeping-time' # Testing: show Keeping Time
|
levelSlug ?= 'siege-of-stonehold' # Testing
|
||||||
leaderboardModal = new LeaderboardModal supermodel: @supermodel, levelSlug: levelSlug
|
leaderboardModal = new LeaderboardModal supermodel: @supermodel, levelSlug: levelSlug
|
||||||
@openModalView leaderboardModal
|
@openModalView leaderboardModal
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ module.exports = class ControlBarView extends CocoView
|
||||||
@level = options.level
|
@level = options.level
|
||||||
@levelID = @level.get('slug')
|
@levelID = @level.get('slug')
|
||||||
@spectateGame = options.spectateGame ? false
|
@spectateGame = options.spectateGame ? false
|
||||||
|
@observing = options.session.get('creator') isnt me.id
|
||||||
super options
|
super options
|
||||||
if @level.get('type') in ['hero-ladder'] and me.isAdmin()
|
if @level.get('type') in ['hero-ladder'] and me.isAdmin()
|
||||||
@isMultiplayerLevel = true
|
@isMultiplayerLevel = true
|
||||||
|
@ -66,6 +67,7 @@ module.exports = class ControlBarView extends CocoView
|
||||||
c.difficultyTitle = "#{$.i18n.t 'play.level_difficulty'}#{c.levelDifficulty}"
|
c.difficultyTitle = "#{$.i18n.t 'play.level_difficulty'}#{c.levelDifficulty}"
|
||||||
@lastDifficulty = c.levelDifficulty
|
@lastDifficulty = c.levelDifficulty
|
||||||
c.spectateGame = @spectateGame
|
c.spectateGame = @spectateGame
|
||||||
|
c.observing = @observing
|
||||||
@homeViewArgs = [{supermodel: if @hasReceivedMemoryWarning then null else @supermodel}]
|
@homeViewArgs = [{supermodel: if @hasReceivedMemoryWarning then null else @supermodel}]
|
||||||
if @level.get('type', true) in ['ladder', 'ladder-tutorial', 'hero-ladder']
|
if @level.get('type', true) in ['ladder', 'ladder-tutorial', 'hero-ladder']
|
||||||
levelID = @level.get('slug').replace /\-tutorial$/, ''
|
levelID = @level.get('slug').replace /\-tutorial$/, ''
|
||||||
|
|
|
@ -95,6 +95,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
|
|
||||||
@isEditorPreview = @getQueryVariable 'dev'
|
@isEditorPreview = @getQueryVariable 'dev'
|
||||||
@sessionID = @getQueryVariable 'session'
|
@sessionID = @getQueryVariable 'session'
|
||||||
|
@observing = @getQueryVariable 'observing'
|
||||||
|
|
||||||
@opponentSessionID = @getQueryVariable('opponent')
|
@opponentSessionID = @getQueryVariable('opponent')
|
||||||
@opponentSessionID ?= @options.opponent
|
@opponentSessionID ?= @options.opponent
|
||||||
|
@ -109,7 +110,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
setTimeout f, 100
|
setTimeout f, 100
|
||||||
else
|
else
|
||||||
@load()
|
@load()
|
||||||
application.tracker?.trackEvent 'Started Level Load', category: 'Play Level', level: @levelID, label: @levelID, ['Google Analytics']
|
application.tracker?.trackEvent 'Started Level Load', category: 'Play Level', level: @levelID, label: @levelID, ['Google Analytics'] unless @observing
|
||||||
|
|
||||||
setLevel: (@level, givenSupermodel) ->
|
setLevel: (@level, givenSupermodel) ->
|
||||||
@supermodel.models = givenSupermodel.models
|
@supermodel.models = givenSupermodel.models
|
||||||
|
@ -134,8 +135,9 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@loadEndTime = new Date()
|
@loadEndTime = new Date()
|
||||||
loadDuration = @loadEndTime - @loadStartTime
|
loadDuration = @loadEndTime - @loadStartTime
|
||||||
console.debug "Level unveiled after #{(loadDuration / 1000).toFixed(2)}s"
|
console.debug "Level unveiled after #{(loadDuration / 1000).toFixed(2)}s"
|
||||||
application.tracker?.trackEvent 'Finished Level Load', category: 'Play Level', label: @levelID, level: @levelID, loadDuration: loadDuration, ['Google Analytics']
|
unless @observing
|
||||||
application.tracker?.trackTiming loadDuration, 'Level Load Time', @levelID, @levelID
|
application.tracker?.trackEvent 'Finished Level Load', category: 'Play Level', label: @levelID, level: @levelID, loadDuration: loadDuration, ['Google Analytics']
|
||||||
|
application.tracker?.trackTiming loadDuration, 'Level Load Time', @levelID, @levelID
|
||||||
|
|
||||||
# CocoView overridden methods ###############################################
|
# CocoView overridden methods ###############################################
|
||||||
|
|
||||||
|
@ -147,7 +149,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
window.onPlayLevelViewLoaded? @ # still a hack
|
window.onPlayLevelViewLoaded? @ # still a hack
|
||||||
@insertSubView @loadingView = new LevelLoadingView autoUnveil: @options.autoUnveil, level: @levelLoader?.level ? @level # May not have @level loaded yet
|
@insertSubView @loadingView = new LevelLoadingView autoUnveil: @options.autoUnveil or @observing, level: @levelLoader?.level ? @level # May not have @level loaded yet
|
||||||
@$el.find('#level-done-button').hide()
|
@$el.find('#level-done-button').hide()
|
||||||
$('body').addClass('is-playing')
|
$('body').addClass('is-playing')
|
||||||
$('body').bind('touchmove', false) if @isIPadApp()
|
$('body').bind('touchmove', false) if @isIPadApp()
|
||||||
|
@ -233,7 +235,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@god.setGoalManager @goalManager
|
@god.setGoalManager @goalManager
|
||||||
|
|
||||||
insertSubviews: ->
|
insertSubviews: ->
|
||||||
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, level: @level
|
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, level: @level, observing: @observing
|
||||||
@insertSubView new LevelPlaybackView session: @session, level: @level
|
@insertSubView new LevelPlaybackView session: @session, level: @level
|
||||||
@insertSubView new GoalsView {}
|
@insertSubView new GoalsView {}
|
||||||
@insertSubView new LevelFlagsView levelID: @levelID, world: @world if @$el.hasClass 'flags'
|
@insertSubView new LevelFlagsView levelID: @levelID, world: @world if @$el.hasClass 'flags'
|
||||||
|
@ -283,7 +285,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early
|
return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early
|
||||||
|
|
||||||
# Save latest level played.
|
# Save latest level played.
|
||||||
if not (@levelLoader.level.get('type') in ['ladder', 'ladder-tutorial'])
|
if not @observing and not (@levelLoader.level.get('type') in ['ladder', 'ladder-tutorial'])
|
||||||
me.set('lastLevel', @levelID)
|
me.set('lastLevel', @levelID)
|
||||||
me.save()
|
me.save()
|
||||||
application.tracker?.identify()
|
application.tracker?.identify()
|
||||||
|
@ -321,7 +323,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
if @otherSession and not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'])
|
if @otherSession and not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'])
|
||||||
# TODO: colorize name and cloud by team, colorize wizard by user's color config
|
# TODO: colorize name and cloud by team, colorize wizard by user's color config
|
||||||
@surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team'), levelSlug: @level.get('slug'), codeLanguage: @otherSession.get('submittedCodeLanguage')
|
@surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team'), levelSlug: @level.get('slug'), codeLanguage: @otherSession.get('submittedCodeLanguage')
|
||||||
if @isEditorPreview
|
if @isEditorPreview or @observing
|
||||||
@loadingView.startUnveiling()
|
@loadingView.startUnveiling()
|
||||||
@loadingView.unveil()
|
@loadingView.unveil()
|
||||||
|
|
||||||
|
@ -337,7 +339,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
Backbone.Mediator.publish 'playback:real-time-playback-waiting', {}
|
Backbone.Mediator.publish 'playback:real-time-playback-waiting', {}
|
||||||
@realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID
|
@realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID
|
||||||
# TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()?
|
# TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()?
|
||||||
application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID, ls: @session?.get('_id')
|
application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID, ls: @session?.get('_id') unless @observing
|
||||||
|
|
||||||
playAmbientSound: ->
|
playAmbientSound: ->
|
||||||
return if @destroyed
|
return if @destroyed
|
||||||
|
@ -414,7 +416,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
return if @victorySeen
|
return if @victorySeen
|
||||||
@victorySeen = true
|
@victorySeen = true
|
||||||
victoryTime = (new Date()) - @loadEndTime
|
victoryTime = (new Date()) - @loadEndTime
|
||||||
if victoryTime > 10 * 1000 # Don't track it if we're reloading an already-beaten level
|
if not @observing and victoryTime > 10 * 1000 # Don't track it if we're reloading an already-beaten level
|
||||||
application.tracker?.trackEvent 'Saw Victory',
|
application.tracker?.trackEvent 'Saw Victory',
|
||||||
category: 'Play Level'
|
category: 'Play Level'
|
||||||
level: @level.get('name')
|
level: @level.get('name')
|
||||||
|
@ -436,12 +438,12 @@ module.exports = class PlayLevelView extends RootView
|
||||||
@tome.reloadAllCode()
|
@tome.reloadAllCode()
|
||||||
Backbone.Mediator.publish 'level:restarted', {}
|
Backbone.Mediator.publish 'level:restarted', {}
|
||||||
$('#level-done-button', @$el).hide()
|
$('#level-done-button', @$el).hide()
|
||||||
application.tracker?.trackEvent 'Confirmed Restart', category: 'Play Level', level: @level.get('name'), label: @level.get('name')
|
application.tracker?.trackEvent 'Confirmed Restart', category: 'Play Level', level: @level.get('name'), label: @level.get('name') unless @observing
|
||||||
|
|
||||||
onInfiniteLoop: (e) ->
|
onInfiniteLoop: (e) ->
|
||||||
return unless e.firstWorld
|
return unless e.firstWorld
|
||||||
@openModalView new InfiniteLoopModal()
|
@openModalView new InfiniteLoopModal()
|
||||||
application.tracker?.trackEvent 'Saw Initial Infinite Loop', category: 'Play Level', level: @level.get('name'), label: @level.get('name')
|
application.tracker?.trackEvent 'Saw Initial Infinite Loop', category: 'Play Level', level: @level.get('name'), label: @level.get('name') unless @observing
|
||||||
|
|
||||||
onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation
|
onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ module.exports = class CastButtonView extends CocoView
|
||||||
@spells = options.spells
|
@spells = options.spells
|
||||||
@castShortcut = '⇧↵'
|
@castShortcut = '⇧↵'
|
||||||
@updateReplayabilityInterval = setInterval @updateReplayability, 1000
|
@updateReplayabilityInterval = setInterval @updateReplayability, 1000
|
||||||
|
@observing = options.session.get('creator') isnt me.id
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
clearInterval @updateReplayabilityInterval
|
clearInterval @updateReplayabilityInterval
|
||||||
|
@ -40,6 +41,7 @@ module.exports = class CastButtonView extends CocoView
|
||||||
castRealTimeShortcutVerbose = (if @isMac() then 'Cmd' else 'Ctrl') + '+' + castShortcutVerbose
|
castRealTimeShortcutVerbose = (if @isMac() then 'Cmd' else 'Ctrl') + '+' + castShortcutVerbose
|
||||||
context.castVerbose = castShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_code')
|
context.castVerbose = castShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_code')
|
||||||
context.castRealTimeVerbose = castRealTimeShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_real_time')
|
context.castRealTimeVerbose = castRealTimeShortcutVerbose + ': ' + $.i18n.t('keyboard_shortcuts.run_real_time')
|
||||||
|
context.observing = @observing
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
|
|
|
@ -15,6 +15,7 @@ module.exports = class Spell
|
||||||
@otherSession = options.otherSession
|
@otherSession = options.otherSession
|
||||||
@spectateView = options.spectateView
|
@spectateView = options.spectateView
|
||||||
@spectateOpponentCodeLanguage = options.spectateOpponentCodeLanguage
|
@spectateOpponentCodeLanguage = options.spectateOpponentCodeLanguage
|
||||||
|
@observing = options.observing
|
||||||
@supermodel = options.supermodel
|
@supermodel = options.supermodel
|
||||||
@skipProtectAPI = options.skipProtectAPI
|
@skipProtectAPI = options.skipProtectAPI
|
||||||
@worker = options.worker
|
@worker = options.worker
|
||||||
|
@ -189,6 +190,7 @@ module.exports = class Spell
|
||||||
return true if @spectateView # Use transpiled code for both teams if we're just spectating.
|
return true if @spectateView # Use transpiled code for both teams if we're just spectating.
|
||||||
return true if @isEnemySpell() # Use transpiled for enemy spells.
|
return true if @isEnemySpell() # Use transpiled for enemy spells.
|
||||||
# Players without permissions can't view the raw code.
|
# Players without permissions can't view the raw code.
|
||||||
|
return false if @observing and @levelType is 'hero'
|
||||||
return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions', true))
|
return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions', true))
|
||||||
false
|
false
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ module.exports = class SpellView extends CocoView
|
||||||
@writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything
|
@writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything
|
||||||
@highlightCurrentLine = _.throttle @highlightCurrentLine, 100
|
@highlightCurrentLine = _.throttle @highlightCurrentLine, 100
|
||||||
$(window).on 'resize', @onWindowResize
|
$(window).on 'resize', @onWindowResize
|
||||||
|
@observing = @session.get('creator') isnt me.id
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
|
@ -119,14 +120,15 @@ module.exports = class SpellView extends CocoView
|
||||||
name: 'run-code'
|
name: 'run-code'
|
||||||
bindKey: {win: 'Shift-Enter|Ctrl-Enter', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter'}
|
bindKey: {win: 'Shift-Enter|Ctrl-Enter', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter'}
|
||||||
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {}
|
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {}
|
||||||
addCommand
|
unless @observing
|
||||||
name: 'run-code-real-time'
|
addCommand
|
||||||
bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'}
|
name: 'run-code-real-time'
|
||||||
exec: =>
|
bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'}
|
||||||
if @options.level.get('replayable') and (timeUntilResubmit = @session.timeUntilResubmit()) > 0
|
exec: =>
|
||||||
Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit
|
if @options.level.get('replayable') and (timeUntilResubmit = @session.timeUntilResubmit()) > 0
|
||||||
else
|
Backbone.Mediator.publish 'tome:manual-cast-denied', timeUntilResubmit: timeUntilResubmit
|
||||||
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
else
|
||||||
|
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||||
addCommand
|
addCommand
|
||||||
name: 'no-op'
|
name: 'no-op'
|
||||||
bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'}
|
bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'}
|
||||||
|
|
|
@ -134,6 +134,7 @@ module.exports = class TomeView extends CocoView
|
||||||
language: language
|
language: language
|
||||||
spectateView: @options.spectateView
|
spectateView: @options.spectateView
|
||||||
spectateOpponentCodeLanguage: @options.spectateOpponentCodeLanguage
|
spectateOpponentCodeLanguage: @options.spectateOpponentCodeLanguage
|
||||||
|
observing: @options.observing
|
||||||
levelID: @options.levelID
|
levelID: @options.levelID
|
||||||
level: @options.level
|
level: @options.level
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ module.exports = class LeaderboardTabView extends CocoView
|
||||||
template: template
|
template: template
|
||||||
className: 'leaderboard-tab-view'
|
className: 'leaderboard-tab-view'
|
||||||
|
|
||||||
events: {}
|
events:
|
||||||
|
'click tbody tr.viewable': 'onClickRow'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -67,3 +68,8 @@ module.exports = class LeaderboardTabView extends CocoView
|
||||||
@hasShown = true
|
@hasShown = true
|
||||||
topScores = new TopScoresCollection @level, @scoreType, @timespan
|
topScores = new TopScoresCollection @level, @scoreType, @timespan
|
||||||
@sessions = @supermodel.loadCollection(topScores, 'sessions', null, 0).model
|
@sessions = @supermodel.loadCollection(topScores, 'sessions', null, 0).model
|
||||||
|
|
||||||
|
onClickRow: (e) ->
|
||||||
|
sessionID = $(e.target).closest('tr').data 'session-id'
|
||||||
|
url = "/play/level/#{@level.get('slug')}?session=#{sessionID}&observing=true"
|
||||||
|
window.open url, '_blank'
|
||||||
|
|
|
@ -15,7 +15,10 @@ class LevelSessionHandler extends Handler
|
||||||
|
|
||||||
formatEntity: (req, document) ->
|
formatEntity: (req, document) ->
|
||||||
documentObject = super(req, document)
|
documentObject = super(req, document)
|
||||||
if req.user?.isAdmin() or req.user?.id is document.creator or ('employer' in (req.user?.get('permissions') ? []))
|
if req.user?.isAdmin() or
|
||||||
|
req.user?.id is document.creator or
|
||||||
|
('employer' in (req.user?.get('permissions') ? [])) or
|
||||||
|
!document.submittedCode # TODO: only allow leaderboard access to non-top-5 solutions
|
||||||
return documentObject
|
return documentObject
|
||||||
else
|
else
|
||||||
return _.omit documentObject, @privateProperties
|
return _.omit documentObject, @privateProperties
|
||||||
|
@ -47,8 +50,9 @@ class LevelSessionHandler extends Handler
|
||||||
@sendSuccess res, documents
|
@sendSuccess res, documents
|
||||||
|
|
||||||
hasAccessToDocument: (req, document, method=null) ->
|
hasAccessToDocument: (req, document, method=null) ->
|
||||||
return true if req.method is 'GET' and document.get('submitted')
|
get = (method ? req.method).toLowerCase() is 'get'
|
||||||
return true if ('employer' in (req.user?.get('permissions') ? [])) and (method ? req.method).toLowerCase() is 'get'
|
return true if get and document.get('submitted')
|
||||||
|
return true if get and ('employer' in (req.user?.get('permissions') ? []))
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
getCodeLanguageCounts: (req, res) ->
|
getCodeLanguageCounts: (req, res) ->
|
||||||
|
|
Loading…
Reference in a new issue