diff --git a/app/core/Tracker.coffee b/app/core/Tracker.coffee index 4294bc6ce..9e07f41d9 100644 --- a/app/core/Tracker.coffee +++ b/app/core/Tracker.coffee @@ -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', { diff --git a/app/core/storage.coffee b/app/core/storage.coffee index 21e8d59a9..f31e8b300 100644 --- a/app/core/storage.coffee +++ b/app/core/storage.coffee @@ -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 diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee index 37d304f5d..18e175f72 100644 --- a/app/lib/world/world.coffee +++ b/app/lib/world/world.coffee @@ -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 diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 5222b8a3d..3a9d6dea1 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -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" diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index 111ddab62..df9203abc 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -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 diff --git a/app/templates/base.jade b/app/templates/base.jade index 003471b53..92df03f01 100644 --- a/app/templates/base.jade +++ b/app/templates/base.jade @@ -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 diff --git a/app/templates/play/level/modal/victory.jade b/app/templates/play/level/modal/victory.jade index 49a6b896f..effaad52c 100644 --- a/app/templates/play/level/modal/victory.jade +++ b/app/templates/play/level/modal/victory.jade @@ -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") diff --git a/app/templates/play/menu/game-menu-modal.jade b/app/templates/play/menu/game-menu-modal.jade index 689c41b53..ed7beae05 100644 --- a/app/templates/play/menu/game-menu-modal.jade +++ b/app/templates/play/menu/game-menu-modal.jade @@ -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" : "") diff --git a/app/templates/play/menu/options-view.jade b/app/templates/play/menu/options-view.jade index 5f43a07a8..451d960df 100644 --- a/app/templates/play/menu/options-view.jade +++ b/app/templates/play/menu/options-view.jade @@ -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 diff --git a/app/views/HomeView.coffee b/app/views/HomeView.coffee index ac14ba60c..718144ce3 100644 --- a/app/views/HomeView.coffee +++ b/app/views/HomeView.coffee @@ -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($('')) + $('body').append($('')) application.tracker?.trackEvent 'Hour of Code Begin' diff --git a/app/views/ladder/LadderPlayModal.coffee b/app/views/ladder/LadderPlayModal.coffee index 96ba9937a..d0c14ba17 100644 --- a/app/views/ladder/LadderPlayModal.coffee +++ b/app/views/ladder/LadderPlayModal.coffee @@ -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'} diff --git a/app/views/play/CampaignView.coffee b/app/views/play/CampaignView.coffee index f8b981dd7..5797f11a1 100644 --- a/app/views/play/CampaignView.coffee +++ b/app/views/play/CampaignView.coffee @@ -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($('')) + $('body').append($('')) trackedHourOfCode = true @requiresSubscription = not me.isPremium() diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index f4e4633d2..ca263aaf1 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -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 diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee index 64b1324a8..6ad76861b 100644 --- a/app/views/play/level/tome/CastButtonView.coffee +++ b/app/views/play/level/tome/CastButtonView.coffee @@ -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 diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index 55cc20947..5d13a8094 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -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 diff --git a/app/views/play/level/tome/TomeView.coffee b/app/views/play/level/tome/TomeView.coffee index cd08ce075..eaab9776d 100644 --- a/app/views/play/level/tome/TomeView.coffee +++ b/app/views/play/level/tome/TomeView.coffee @@ -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 diff --git a/app/views/play/menu/GameMenuModal.coffee b/app/views/play/menu/GameMenuModal.coffee index 9dad94e47..7e5c3aac2 100644 --- a/app/views/play/menu/GameMenuModal.coffee +++ b/app/views/play/menu/GameMenuModal.coffee @@ -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: -> diff --git a/app/views/play/menu/OptionsView.coffee b/app/views/play/menu/OptionsView.coffee index 8b76b4a1e..205057065 100644 --- a/app/views/play/menu/OptionsView.coffee +++ b/app/views/play/menu/OptionsView.coffee @@ -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') diff --git a/bower.json b/bower.json index ea8592d3f..484277bcb 100644 --- a/bower.json +++ b/bower.json @@ -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": {