mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-24 08:08:15 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
d5117518a4
19 changed files with 52 additions and 76 deletions
|
@ -100,7 +100,7 @@ module.exports = class Tracker
|
|||
eventObject["user"] = me.id
|
||||
dataToSend = JSON.stringify eventObject
|
||||
# console.log dataToSend if debugAnalytics
|
||||
$.post("http://analytics.codecombat.com/analytics", dataToSend).fail ->
|
||||
$.post("#{window.location.protocol or 'http:'}//analytics.codecombat.com/analytics", dataToSend).fail ->
|
||||
console.error "Analytics post failed!"
|
||||
else
|
||||
request = @supermodel.addRequestResource 'log_event', {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
module.exports.load = (key) ->
|
||||
# Pass false for fromCache to fetch keys that have been stored outside of lscache.
|
||||
module.exports.load = (key, fromCache=true) ->
|
||||
return lscache.get key if fromCache
|
||||
s = localStorage.getItem(key)
|
||||
return null unless s
|
||||
try
|
||||
|
@ -8,8 +10,17 @@ module.exports.load = (key) ->
|
|||
console.warn('error loading from storage', key)
|
||||
return null
|
||||
|
||||
module.exports.save = (key, value) ->
|
||||
s = JSON.stringify(value)
|
||||
localStorage.setItem(key, s)
|
||||
# Pass 0 for expirationInMinutes to persist it as long as possible outside of lscache expiration.
|
||||
module.exports.save = (key, value, expirationInMinutes) ->
|
||||
expirationInMinutes ?= 7 * 24 * 60
|
||||
if expirationInMinutes
|
||||
lscache.set key, value, expirationInMinutes
|
||||
else
|
||||
localStorage.setItem key, JSON.stringify(value)
|
||||
|
||||
module.exports.remove = (key) -> localStorage.removeItem key
|
||||
# Pass false for fromCache to remove keys that have been stored outside of lscache.
|
||||
module.exports.remove = (key, fromCache=true) ->
|
||||
if fromCache
|
||||
lscache.remove key
|
||||
else
|
||||
localStorage.removeItem key
|
||||
|
|
|
@ -70,9 +70,9 @@ module.exports = class World
|
|||
setThang: (thang) ->
|
||||
thang.stateChanged = true
|
||||
for old, i in @thangs
|
||||
console.error 'world trying to set', thang, 'over', old unless old? and thang?
|
||||
if old.id is thang.id
|
||||
@thangs[i] = thang
|
||||
break
|
||||
@thangMap[thang.id] = thang
|
||||
|
||||
thangDialogueSounds: (startFrame=0) ->
|
||||
|
@ -102,7 +102,9 @@ module.exports = class World
|
|||
if @realTime and not @countdownFinished
|
||||
@realTimeSpeedFactor = 1
|
||||
unless @showsCountdown
|
||||
if @levelID in ['village-guard', 'thornbush-farm', 'back-to-back', 'ogre-encampment', 'woodland-cleaver', 'shield-rush', 'peasant-protection', 'munchkin-swarm', 'munchkin-harvest', 'swift-dagger', 'shrapnel', 'arcane-ally', 'touch-of-death', 'bonemender']
|
||||
if @levelID in ['woodland-cleaver', 'village-guard', 'shield-rush']
|
||||
@realTimeSpeedFactor = 2
|
||||
else if @levelID in ['thornbush-farm', 'back-to-back', 'ogre-encampment', 'peasant-protection', 'munchkin-swarm', 'munchkin-harvest', 'swift-dagger', 'shrapnel', 'arcane-ally', 'touch-of-death', 'bonemender']
|
||||
@realTimeSpeedFactor = 3
|
||||
if @showsCountdown
|
||||
return setTimeout @finishCountdown(continueLaterFn), REAL_TIME_COUNTDOWN_DELAY
|
||||
|
|
|
@ -529,8 +529,6 @@
|
|||
volume_label: "Volume"
|
||||
music_label: "Music"
|
||||
music_description: "Turn background music on/off."
|
||||
autorun_label: "Autorun"
|
||||
autorun_description: "Control automatic code execution."
|
||||
editor_config: "Editor Config"
|
||||
editor_config_title: "Editor Configuration"
|
||||
editor_config_level_language_label: "Language for This Level"
|
||||
|
|
|
@ -63,10 +63,10 @@ _.extend UserSchema.properties,
|
|||
githubID: {type: 'integer', title: 'GitHub ID'}
|
||||
gplusID: c.shortString({title: 'G+ ID'})
|
||||
|
||||
wizardColor1: c.pct({title: 'Wizard Clothes Color'})
|
||||
wizardColor1: c.pct({title: 'Wizard Clothes Color'}) # No longer used
|
||||
volume: c.pct({title: 'Volume'})
|
||||
music: { type: 'boolean' }
|
||||
autocastDelay: { type: 'integer' }
|
||||
autocastDelay: { type: 'integer' } # No longer used
|
||||
lastLevel: { type: 'string' }
|
||||
heroConfig: c.HeroConfigSchema
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ block footer
|
|||
.fb-like(data-href="https://www.facebook.com/codecombat", data-send="false", data-layout="button_count", data-width="350", data-show-faces="true", data-ref="coco_footer_#{fbRef}")
|
||||
if !isIE
|
||||
a.twitter-follow-button(href="https://twitter.com/CodeCombat", data-show-count="true", data-show-screen-name="false", data-dnt="true", data-align="right", data-i18n="nav.twitter_follow") Follow
|
||||
iframe.github-star-button(src="http://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20")
|
||||
iframe.github-star-button(src="https://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20")
|
||||
|
||||
#footer-credits
|
||||
span
|
||||
|
|
|
@ -39,4 +39,4 @@ block modal-footer-content
|
|||
.g-plusone(data-href="http://codecombat.com", data-size="medium")
|
||||
.fb-like(data-href="https://www.facebook.com/codecombat", data-send="false", data-layout="button_count", data-width="350", data-show-faces="true", data-ref="coco_victory_#{fbRef}")
|
||||
a.twitter-follow-button(href="https://twitter.com/CodeCombat", data-show-count="true", data-show-screen-name="false", data-dnt="true", data-align="right", data-i18n="nav.twitter_follow") Follow
|
||||
iframe.github-star-button(src="http://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20")
|
||||
iframe.github-star-button(src="https://ghbtns.com/github-btn.html?user=codecombat&repo=codecombat&type=watch&count=true", allowtransparency="true", frameborder="0", scrolling="0", width="110", height="20")
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
span.glyphicon.glyphicon-remove
|
||||
|
||||
ul#game-menu-nav.nav.nav-pills.nav-stacked
|
||||
li
|
||||
a#change-hero-tab
|
||||
span.glyphicon.glyphicon-user
|
||||
span(data-i18n='[title]game_menu.choose_hero_caption;play.change_hero')
|
||||
if showsChooseHero
|
||||
li
|
||||
a#change-hero-tab
|
||||
span.glyphicon.glyphicon-user
|
||||
span(data-i18n='[title]game_menu.choose_hero_caption;play.change_hero')
|
||||
|
||||
for submenu, index in submenus
|
||||
li(class=submenu === showTab ? "active" : "")
|
||||
|
|
|
@ -22,16 +22,6 @@
|
|||
span(data-i18n="options.music_label") Music
|
||||
span.help-block(data-i18n="options.music_description") Turn background music on/off.
|
||||
|
||||
.form-group.select-group
|
||||
label.control-label(for="option-autorun-delay", data-i18n="options.autorun_label") Autorun
|
||||
select#option-autorun-delay.form-control(name="autorunDelay")
|
||||
option(value=1000, selected=(autorunDelay === 1000), data-i18n="common.delay_1_sec") 1 second
|
||||
option(value=3000, selected=(autorunDelay === 3000), data-i18n="common.delay_3_sec") 3 seconds
|
||||
option(value=5000, selected=(autorunDelay === 5000), data-i18n="common.delay_5_sec") 5 seconds
|
||||
option(value=90019001, selected=(autorunDelay === 90019001), data-i18n="common.manual") Manual
|
||||
span.help-block(data-i18n="options.autorun_description") Control automatic code execution.
|
||||
|
||||
|
||||
img.hr(src="/images/pages/play/modal/hr.png", draggable="false")
|
||||
|
||||
h3(data-i18n="options.editor_config_title") Editor Configuration
|
||||
|
|
|
@ -51,5 +51,5 @@ module.exports = class HomeView extends RootView
|
|||
me.set 'hourOfCode', true
|
||||
me.patch()
|
||||
# We may also insert the tracking pixel for everyone on the CampaignView so as to count directly-linked visitors.
|
||||
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
$('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
application.tracker?.trackEvent 'Hour of Code Begin'
|
||||
|
|
|
@ -37,6 +37,9 @@ module.exports = class LadderPlayModal extends ModalView
|
|||
aceConfig.language = @$el.find('#tome-language').val()
|
||||
me.set 'aceConfig', aceConfig
|
||||
me.patch()
|
||||
if @session
|
||||
@session.set 'codeLanguage', aceConfig.language
|
||||
@session.patch()
|
||||
|
||||
# PART 1: Load challengers from the db unless some are in the matches
|
||||
startLoadingChallengersMaybe: ->
|
||||
|
@ -88,6 +91,7 @@ module.exports = class LadderPlayModal extends ModalView
|
|||
ctx.teamID = @team
|
||||
ctx.otherTeamID = @otherTeam
|
||||
ctx.tutorialLevelExists = @tutorialLevelExists
|
||||
ctx.language = @session?.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python'
|
||||
ctx.languages = [
|
||||
{id: 'python', name: 'Python'}
|
||||
{id: 'javascript', name: 'JavaScript'}
|
||||
|
|
|
@ -108,7 +108,7 @@ module.exports = class CampaignView extends RootView
|
|||
# If it's a new player who didn't appear to come from Hour of Code, we register her here without setting the hourOfCode property.
|
||||
elapsed = (new Date() - new Date(me.get('dateCreated')))
|
||||
if not trackedHourOfCode and not me.get('hourOfCode') and elapsed < 5 * 60 * 1000
|
||||
$('body').append($('<img src="http://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
$('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
trackedHourOfCode = true
|
||||
|
||||
@requiresSubscription = not me.isPremium()
|
||||
|
|
|
@ -352,6 +352,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
@realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID
|
||||
# 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') unless @observing
|
||||
$(window).trigger 'resize'
|
||||
|
||||
playAmbientSound: ->
|
||||
return if @destroyed
|
||||
|
|
|
@ -47,10 +47,7 @@ module.exports = class CastButtonView extends CocoView
|
|||
afterRender: ->
|
||||
super()
|
||||
@castButton = $('.cast-button', @$el)
|
||||
@castOptions = $('.autocast-delays', @$el)
|
||||
#delay = me.get('autocastDelay') # No more autocast
|
||||
delay = 90019001
|
||||
@setAutocastDelay delay
|
||||
spell.view?.createOnCodeChangeHandlers() for spellKey, spell of @spells
|
||||
if @options.level.get('hidesSubmitUntilRun') or @options.level.get('hidesRealTimePlayback')
|
||||
@$el.find('.submit-button').hide() # Hide Submit for the first few until they run it once.
|
||||
if @options.session.get('state')?.complete and @options.level.get 'hidesRealTimePlayback'
|
||||
|
@ -150,17 +147,6 @@ module.exports = class CastButtonView extends CocoView
|
|||
waitTime = moment().add(timeUntilResubmit, 'ms').fromNow()
|
||||
submitAgainLabel.text waitTime
|
||||
|
||||
setAutocastDelay: (delay) ->
|
||||
#console.log 'Set autocast delay to', delay
|
||||
return unless delay
|
||||
delay = 90019001 # No more autocast
|
||||
@autocastDelay = delay = parseInt delay
|
||||
me.set('autocastDelay', delay)
|
||||
me.patch()
|
||||
spell.view?.setAutocastDelay delay for spellKey, spell of @spells
|
||||
@castOptions.find('a').each ->
|
||||
$(@).toggleClass('selected', parseInt($(@).attr('data-delay')) is delay)
|
||||
|
||||
onJoinedRealTimeMultiplayerGame: (e) ->
|
||||
@inRealTimeMultiplayerSession = true
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ module.exports = class SpellView extends CocoView
|
|||
@createACE()
|
||||
@createACEShortcuts()
|
||||
@fillACE()
|
||||
@createOnCodeChangeHandlers()
|
||||
@lockDefaultCode()
|
||||
if @session.get('multiplayer')
|
||||
@createFirepad()
|
||||
|
@ -613,11 +614,7 @@ module.exports = class SpellView extends CocoView
|
|||
Backbone.Mediator.publish 'tome:spell-loaded', spell: @spell
|
||||
@updateLines()
|
||||
|
||||
recompileIfNeeded: =>
|
||||
@recompile() if @recompileNeeded
|
||||
|
||||
recompile: (cast=true, realTime=false) ->
|
||||
@setRecompileNeeded false
|
||||
hasChanged = @spell.source isnt @getSource()
|
||||
if hasChanged
|
||||
@spell.transpile @getSource()
|
||||
|
@ -640,17 +637,9 @@ module.exports = class SpellView extends CocoView
|
|||
catch error
|
||||
console.warn 'Error resizing ACE after an update:', error
|
||||
|
||||
# Called from CastButtonView initially and whenever the delay is changed
|
||||
setAutocastDelay: (@autocastDelay) ->
|
||||
@createOnCodeChangeHandlers()
|
||||
|
||||
createOnCodeChangeHandlers: ->
|
||||
@aceDoc.removeListener 'change', @onCodeChangeMetaHandler if @onCodeChangeMetaHandler
|
||||
autocastDelay = @autocastDelay ? 3000
|
||||
onSignificantChange = [
|
||||
_.debounce @setRecompileNeeded, autocastDelay - 100
|
||||
@currentAutocastHandler = _.debounce @recompileIfNeeded, autocastDelay
|
||||
]
|
||||
onSignificantChange = []
|
||||
onAnyChange = [
|
||||
_.debounce @updateAether, 500
|
||||
_.debounce @notifyEditingEnded, 1000
|
||||
|
@ -671,10 +660,7 @@ module.exports = class SpellView extends CocoView
|
|||
callback() for callback in onAnyChange # Then these
|
||||
@aceDoc.on 'change', @onCodeChangeMetaHandler
|
||||
|
||||
setRecompileNeeded: (@recompileNeeded) =>
|
||||
|
||||
onCursorActivity: =>
|
||||
@currentAutocastHandler?()
|
||||
onCursorActivity: => # Used to refresh autocast delay; doesn't do anything at the moment.
|
||||
|
||||
# Design for a simpler system?
|
||||
# * Keep Aether linting, debounced, on any significant change
|
||||
|
@ -801,7 +787,7 @@ module.exports = class SpellView extends CocoView
|
|||
@userCodeProblem.save()
|
||||
null
|
||||
|
||||
# Autocast:
|
||||
# Autocast (preload the world in the background):
|
||||
# Goes immediately if the code is a) changed and b) complete/valid and c) the cursor is at beginning or end of a line
|
||||
# We originally thought it would:
|
||||
# - Go after specified delay if a) and b) but not c)
|
||||
|
@ -815,12 +801,9 @@ module.exports = class SpellView extends CocoView
|
|||
endOfLine = cursorPosition.column >= currentLine.length # just typed a semicolon or brace, for example
|
||||
beginningOfLine = not currentLine.substr(0, cursorPosition.column).trim().length # uncommenting code, for example
|
||||
incompleteThis = /^(s|se|sel|self|t|th|thi|this)$/.test currentLine.trim()
|
||||
# console.log "finished=#{valid and (endOfLine or beginningOfLine) and not incompleteThis}", valid, endOfLine, beginningOfLine, incompleteThis, cursorPosition, currentLine.length, aether, new Date() - 0, currentLine
|
||||
#console.log "finished=#{valid and (endOfLine or beginningOfLine) and not incompleteThis}", valid, endOfLine, beginningOfLine, incompleteThis, cursorPosition, currentLine.length, aether, new Date() - 0, currentLine
|
||||
if valid and (endOfLine or beginningOfLine) and not incompleteThis
|
||||
if @autocastDelay > 60000
|
||||
@preload()
|
||||
else
|
||||
@recompile()
|
||||
@preload()
|
||||
|
||||
singleLineCommentRegex: ->
|
||||
if @_singleLineCommentRegex
|
||||
|
@ -836,7 +819,10 @@ module.exports = class SpellView extends CocoView
|
|||
|
||||
preload: ->
|
||||
# Send this code over to the God for preloading, but don't change the cast state.
|
||||
return if @spell.source.indexOf 'while' # If they're working with while-loops, it's more likely to be an incomplete infinite loop, so don't preload.
|
||||
#console.log 'preload?', @spell.source.indexOf('while'), @spell.source.length, @spellThang?.castAether?.metrics?.statementsExecuted
|
||||
return if @spell.source.indexOf('while') isnt -1 # If they're working with while-loops, it's more likely to be an incomplete infinite loop, so don't preload.
|
||||
return if @spell.source.length > 500 # Only preload on really short methods
|
||||
return if @spellThang?.castAether?.metrics?.statementsExecuted > 2000 # Don't preload if they are running significant amounts of user code
|
||||
oldSource = @spell.source
|
||||
oldSpellThangAethers = {}
|
||||
for thangID, spellThang of @spell.thangs
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# There's one TomeView per Level. It has:
|
||||
# - a CastButtonView, which has
|
||||
# - a cast button
|
||||
# - an autocast settings options button
|
||||
# - a submit/done button
|
||||
# - for each spell (programmableMethod):
|
||||
# - a Spell, which has
|
||||
# - a list of Thangs that share that Spell, with one aether per Thang per Spell
|
||||
|
|
|
@ -43,6 +43,7 @@ module.exports = class GameMenuModal extends ModalView
|
|||
'guide': 'list'
|
||||
'save-load': 'floppy-disk'
|
||||
'multiplayer': 'globe'
|
||||
context.showsChooseHero = @options.levelID not in ['zero-sum']
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
|
|
|
@ -20,7 +20,6 @@ module.exports = class OptionsView extends CocoView
|
|||
|
||||
events:
|
||||
'change #option-music': 'updateMusic'
|
||||
'change #option-autorun-delay': 'updateAutorun'
|
||||
'change #option-key-bindings': 'updateInvisibles'
|
||||
'change #option-key-bindings': 'updateKeyBindings'
|
||||
'change #option-indent-guides': 'updateIndentGuides'
|
||||
|
@ -44,7 +43,6 @@ module.exports = class OptionsView extends CocoView
|
|||
@aceConfig = _.defaults @aceConfig, @defaultConfig
|
||||
c.aceConfig = @aceConfig
|
||||
c.music = me.get('music', true)
|
||||
c.autorunDelay = me.get('autocastDelay') ? 5000
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
|
@ -80,9 +78,6 @@ module.exports = class OptionsView extends CocoView
|
|||
updateMusic: ->
|
||||
me.set 'music', @$el.find('#option-music').prop('checked')
|
||||
|
||||
updateAutorun: ->
|
||||
me.set 'autocastDelay', parseInt(@$el.find('#option-autorun-delay').val())
|
||||
|
||||
updateInvisibles: ->
|
||||
@aceConfig.invisibles = @$el.find('#option-invisibles').prop('checked')
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@
|
|||
"modernizr": "~2.8.3",
|
||||
"backfire": "~0.3.0",
|
||||
"fastclick": "~1.0.3",
|
||||
"three.js": "*"
|
||||
"three.js": "*",
|
||||
"lscache": "~1.0.5"
|
||||
},
|
||||
"overrides": {
|
||||
"backbone": {
|
||||
|
|
Loading…
Reference in a new issue