Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-10-17 15:46:23 -07:00
commit 64db50e122
34 changed files with 394 additions and 141 deletions

View file

@ -12,6 +12,7 @@ module.exports = class CocoRouter extends Backbone.Router
@bind 'route', @_trackPageView
Backbone.Mediator.subscribe 'auth:gplus-api-loaded', @onGPlusAPILoaded, @
Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @
@initializeSocialMediaServices = _.once @initializeSocialMediaServices
routes:
'': go('HomeView') # This will go somewhere deprecated when FrontView is done.
@ -125,7 +126,7 @@ module.exports = class CocoRouter extends Backbone.Router
$('#page-container').empty().append view.el
window.currentView = view
@activateTab()
@renderLoginButtons()
@renderLoginButtons() if view.usesSocialMedia
view.afterInsert()
view.didReappear()
@ -139,8 +140,22 @@ module.exports = class CocoRouter extends Backbone.Router
onGPlusAPILoaded: =>
@renderLoginButtons()
initializeSocialMediaServices: ->
return if application.testing or application.demoing
services = [
'./lib/services/facebook'
'./lib/services/google'
'./lib/services/twitter'
'./lib/services/linkedin'
]
for service in services
service = require service
service()
renderLoginButtons: ->
@initializeSocialMediaServices()
$('.share-buttons, .partner-badges').addClass('fade-in').delay(10000).removeClass('fade-in', 5000)
setTimeout(FB.XFBML.parse, 10) if FB?.XFBML?.parse # Handles FB login and Like
twttr?.widgets?.load?()

View file

@ -24,9 +24,9 @@ init = ->
watchForErrors()
setUpIOSLogging()
path = document.location.pathname
testing = path.startsWith '/test'
demoing = path.startsWith '/demo'
initializeServices() unless testing or demoing
app.testing = path.startsWith '/test'
app.demoing = path.startsWith '/demo'
initializeUtilityServices() unless app.testing or app.demoing
setUpBackboneMediator()
app.initialize()
Backbone.history.start({ pushState: true })
@ -74,15 +74,11 @@ setUpMoment = ->
me.on 'change:preferredLanguage', (me) ->
moment.lang me.get('preferredLanguage', true), {}
initializeServices = ->
initializeUtilityServices = ->
services = [
'./lib/services/filepicker'
'./lib/services/segmentio'
'./lib/services/olark'
'./lib/services/facebook'
'./lib/services/google'
'./lib/services/twitter'
'./lib/services/linkedin'
]
for service in services

View file

@ -105,7 +105,7 @@ module.exports = class LevelLoader extends CocoClass
@loadThangsRequiredByThangType heroThangType
for itemThangType in _.values(heroConfig.inventory)
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original"
url = "/db/thang.type/#{itemThangType}/version?project=name,components,original,rasterIcon"
if itemResource = @maybeLoadURL(url, ThangType, 'thang')
@worldNecessities.push itemResource
else
@ -183,7 +183,7 @@ module.exports = class LevelLoader extends CocoClass
else if component.config.requiredThangTypes
requiredThangTypes = requiredThangTypes.concat component.config.requiredThangTypes
for thangType in requiredThangTypes
url = "/db/thang.type/#{thangType}/version?project=name,components,original"
url = "/db/thang.type/#{thangType}/version?project=name,components,original,rasterIcon"
@worldNecessities.push @maybeLoadURL(url, ThangType, 'thang')
onThangNamesLoaded: (thangNames) ->

View file

@ -44,8 +44,10 @@ functionParameters =
evaluateBoard: ['board', 'player']
getPossibleMoves: ['board']
minimax_alphaBeta: ['board', 'player', 'depth', 'alpha', 'beta']
distanceTo: ['target']
chooseAction: []
plan: []
initializeCentroids: []
update: []
getNearestEnemy: []

View file

@ -522,7 +522,7 @@ module.exports = Surface = class Surface extends CocoClass
newWidth = 0.55 * pageWidth
newHeight = newWidth / aspectRatio
return unless newWidth > 0 and newHeight > 0
return if newWidth is oldWidth and newHeight is oldHeight
return if newWidth is oldWidth and newHeight is oldHeight and not @options.spectateGame
#scaleFactor = if application.isIPadApp then 2 else 1 # Retina
scaleFactor = 1
@normalCanvas.add(@webGLCanvas).attr width: newWidth * scaleFactor, height: newHeight * scaleFactor
@ -533,7 +533,9 @@ module.exports = Surface = class Surface extends CocoClass
@normalStage.scaleX *= newWidth / oldWidth
@normalStage.scaleY *= newHeight / oldHeight
@camera.onResize newWidth, newHeight
if @options.spectateGame
# Since normalCanvas is absolutely positioned, it needs help aligning with webGLCanvas. But not further than +149px (1920px screen).
@normalCanvas.css 'left', Math.min 149, @webGLCanvas.offset().left
#- Camera focus on hero
focusOnHero: ->

View file

@ -216,13 +216,13 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
tome_cast_button_castable: "Lançar" # Temporary, if tome_cast_button_run isn't translated.
tome_cast_button_casting: "Conjurando" # Temporary, if tome_cast_button_running isn't translated.
tome_cast_button_cast: "Feitiço" # Temporary, if tome_cast_button_ran isn't translated.
# tome_cast_button_run: "Run"
# tome_cast_button_running: "Running"
# tome_cast_button_ran: "Ran"
tome_cast_button_run: "Rodar"
tome_cast_button_running: "Rodando"
tome_cast_button_ran: "Encerrado"
tome_submit_button: "Enviar"
# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button.
tome_reload_method: "Recarregar o código original para este método" # Title text for individual method reload button.
tome_select_method: "Selecione um Método"
# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos).
tome_see_all_methods: "Visualizar todos os métodos que você pode editar" # Title text for method list selector (shown when there are multiple programmable methdos).
tome_select_a_thang: "Selecione alguém para "
tome_available_spells: "Feitiços Disponíveis"
tome_your_skills: "Suas habilidades"
@ -236,14 +236,14 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
time_total: "Máximo:"
time_goto: "Ir para:"
infinite_loop_try_again: "Tentar novamente"
# infinite_loop_reset_level: "Reset Level"
infinite_loop_reset_level: "Resetar nível"
# infinite_loop_comment_out: "Comment Out My Code"
# tip_toggle_play: "Toggle play/paused with Ctrl+P."
# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward."
tip_toggle_play: "Alterne entre rodando/pausado com Ctrl+P."
tip_scrub_shortcut: "Ctrl+[ e Ctrl+] rebobina e avança."
# tip_guide_exists: "Click the guide at the top of the page for useful info."
# tip_open_source: "CodeCombat is 100% open source!"
# tip_beta_launch: "CodeCombat launched its beta in October, 2013."
# tip_think_solution: "Think of the solution, not the problem."
tip_open_source: "CodeCombat é 100% código aberto!"
tip_beta_launch: "CodeCombat lançou sua versão beta em outubro de 2013."
tip_think_solution: "Pense na solução, não no problema."
# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra"
# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis"
# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra"
@ -257,16 +257,16 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep."
# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't."
# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda"
# tip_no_try: "Do. Or do not. There is no try. - Yoda"
# tip_patience: "Patience you must have, young Padawan. - Yoda"
# tip_documented_bug: "A documented bug is not a bug; it is a feature."
tip_no_try: "Faça. Ou não faça. Não existe tentar. - Yoda"
tip_patience: "Paciência você deve ter, jovem Padawan. - Yoda"
tip_documented_bug: "Um bug documentado não é um bug; é uma funcionalidade."
# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela"
# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds"
# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay"
# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem."
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
tip_brute_force: "Na dúvida, utilize força bruta. - Ken Thompson"
customize_wizard: "Personalize o feiticeiro"
game_menu:
@ -277,14 +277,14 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
guide_tab: "Guia"
multiplayer_tab: "Multijogador"
inventory_caption: "Equipar seu herói"
# choose_hero_caption: "Choose hero, language"
choose_hero_caption: "Escolha seu herói, linguagem"
# save_load_caption: "... and view history"
# options_caption: "Configure settings"
options_caption: "Configurar preferências"
guide_caption: "Documentos e dicas"
# multiplayer_caption: "Play with friends!"
multiplayer_caption: "Jogue com seus amigos!"
# inventory:
# choose_inventory: "Equip Items"
choose_inventory: "Equipar itens"
choose_hero:
choose_hero: "Escolha seu Herói"
@ -300,23 +300,23 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
granularity_change_history: "Histórico"
options:
# general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level
general_options: "Opções Gerais" # Check out the Options tab in the Game Menu while playing a level
volume_label: "Volume"
music_label: "Música"
# music_description: "Turn background music on/off."
# autorun_label: "Autorun"
# autorun_description: "Control automatic code execution."
music_description: "Ligar/desligar música de fundo."
autorun_label: "Rodar automaticamente"
autorun_description: "Controlar execução automática do código."
editor_config: "Editor de Configurações"
editor_config_title: "Editor de Configurações"
# editor_config_level_language_label: "Language for This Level"
# editor_config_level_language_description: "Define the programming language for this particular level."
# editor_config_default_language_label: "Default Programming Language"
# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels."
editor_config_level_language_label: "Linguagem para este nível"
editor_config_level_language_description: "Definir linguagem para esse nível específico."
editor_config_default_language_label: "Linguagem de programação padrão"
editor_config_default_language_description: "Define a linguagem de programação que você quer utilizar quando iniciar novos níveis."
editor_config_keybindings_label: "Teclas de Atalho"
editor_config_keybindings_default: "Padrão (Ace)"
editor_config_keybindings_description: "Adicionar atalhos conhecidos de editores comuns."
# editor_config_livecompletion_label: "Live Autocompletion"
# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing."
editor_config_livecompletion_label: "Autocompletar durante a escrita"
editor_config_livecompletion_description: "Mostra opções de autocompletar enquanto estiver escrevendo."
editor_config_invisibles_label: "Mostrar Invisíveis"
editor_config_invisibles_description: "Mostrar invisíveis como espaços e tabs."
editor_config_indentguides_label: "Mostrar Linhas de Identação"
@ -367,8 +367,8 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
forum_page: "nosso fórum"
forum_suffix: " ao invés disso."
send: "Enviar opinião"
# contact_candidate: "Contact Candidate" # Deprecated
# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # Deprecated
contact_candidate: "Contactar Candidato" # Deprecated
recruitment_reminder: "Utilize esse formulário para entrar em contato com candidatos que você esteja interessado em entrevistar. Lembre-se que o CodeCombat cobra 15% do salário do primeiro ano. A taxa de contratação é cobrada quando da contratação do empregado e é reembolsável por 90 dias, se o empregado não permanece no emprego. Empregados de meio-turno, remotos ou com contrato serão gratuitos como estagiários." # Deprecated
account_settings:
title: "Configurações da Conta"
@ -416,20 +416,20 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
enter: "Enter"
escape: "Esc"
shift: "Shift"
# cast_spell: "Cast current spell."
# run_real_time: "Run in real time."
cast_spell: "Lançar feitiço atual."
run_real_time: "Rodar em tempo real."
# continue_script: "Continue past current script."
# skip_scripts: "Skip past all skippable scripts."
# toggle_playback: "Toggle play/pause."
# scrub_playback: "Scrub back and forward through time."
# single_scrub_playback: "Scrub back and forward through time by a single frame."
# scrub_execution: "Scrub through current spell execution."
# toggle_debug: "Toggle debug display."
# toggle_grid: "Toggle grid overlay."
# toggle_pathfinding: "Toggle pathfinding overlay."
# beautify: "Beautify your code by standardizing its formatting."
# maximize_editor: "Maximize/minimize code editor."
# move_wizard: "Move your Wizard around the level."
toggle_playback: "Alternar play/pause."
scrub_playback: "Rolar para frente e para trás no tempo."
single_scrub_playback: "Rolar para frente e para trás no tempo, quadro a quadro."
scrub_execution: "Rolar através da execução do feitiço atual."
toggle_debug: "Ligar/desligar informações de debug."
toggle_grid: "Ligar/desligar exibição da grade."
toggle_pathfinding: "Ligar/desligar exibição do pathfinding (caminho)."
beautify: "Embeleze seu código a partir da padronização de formatação."
maximize_editor: "Maximizar/minimizar editor de código."
move_wizard: "Mova o Wizard pelo nível."
community:
main_title: "Comunidade CodeCombat"
@ -468,7 +468,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
article_title: "Editor de Artigo"
thang_title: "Editor de Thang"
level_title: "Editor de Nível"
# achievement_title: "Achievement Editor"
achievement_title: "Editor de Conquistas"
back: "Voltar"
revert: "Reverter"
revert_models: "Reverter Modelos"
@ -979,11 +979,11 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
projects_header_2: "Projetos (Top 3)"
projects_blurb: "Destaque seus projetos para impressionar os empregadores."
project_name: "Nome do Projeto"
# project_name_help: "What was the project called?"
project_name_help: "Como o projeto se chama?"
project_description: "Descrição"
# project_description_help: "Briefly describe the project."
project_description_help: "Descreva o projeto brevemente."
project_picture: "Imagem"
# project_picture_help: "Upload a 230x115px or larger image showing off the project."
project_picture_help: "Envie uma imagem com 230x115px ou maior mostrando o projeto."
project_link: "Link"
project_link_help: "Link para o projeto."
# player_code: "Player Code"
@ -1035,11 +1035,11 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
# inactive_developers: "Inactive Developers"
admin:
# av_espionage: "Espionage" # Really not important to translate /admin controls.
# av_espionage_placeholder: "Email or username"
# av_usersearch: "User Search"
# av_usersearch_placeholder: "Email, username, name, whatever"
# av_usersearch_search: "Search"
av_espionage: "Espionagem" # Really not important to translate /admin controls.
av_espionage_placeholder: "Email ou username"
av_usersearch: "Busca de Usuário"
av_usersearch_placeholder: "Email, username, nome, qualquer coisa"
av_usersearch_search: "Buscar"
av_title: "Visualização de Administrador"
av_entities_sub_title: "Entidades"
av_entities_users_url: "Usuários"

View file

@ -58,3 +58,54 @@
-moz-filter: $filters
-o-filter: $filters
filter: $filters
@mixin flexbox()
display: -webkit-box
display: -moz-box
display: -ms-flexbox
display: -webkit-flex
display: flex
@mixin flex($values)
-webkit-box-flex: $values
-moz-box-flex: $values
-webkit-flex: $values
-ms-flex: $values
flex: $values
@mixin order($val)
-webkit-box-ordinal-group: $val
-moz-box-ordinal-group: $val
-ms-flex-order: $val
-webkit-order: $val
order: $val
@mixin flex-justify()
-webkit-box-pack: justify
-webkit-justify-content: space-between
-ms-flex-pack: justify
justify-content: space-between
@mixin flex-align-content-start()
-webkit-align-content: flex-start
-ms-flex-align-content: flex-start
align-content: flex-start
@mixin flex-center()
-webkit-box-align: center
-webkit-align-items: center
-ms-flex-align: center
align-items: center
@mixin flex-wrap()
-webkit-flex-wrap: wrap
-ms-flex-wrap: wrap
flex-wrap: wrap
@mixin flex-column()
-webkit-box-orient: vertical
-moz-box-orient: vertical
-ms-box-orient: vertical
-webkit-flex-direction: column
-ms-flex-direction: column
-flex-direction: column

View file

@ -4,7 +4,8 @@
.problem-alert
z-index: 10
position: absolute
bottom: -135px
// Position these at the end of the spell editor, right above the spell toolbar.
bottom: -20px
left: 10px
right: 10px
background: transparent

View file

@ -12,9 +12,8 @@
position: absolute
left: 10px
top: 48px
bottom: 121px
// Bottom relates to .palette height and padding-top
right: 10px
padding-bottom: 10px
z-index: 1
// Set z-index above palette
display: none
@ -62,9 +61,6 @@
line-height: 20px
overflow: visible
&.user-code-problem.spell-cast
@include editor-height(60px)
&.disabled
@include opacity(80)
.ace_cursor

View file

@ -3,10 +3,10 @@
#spell-palette-view
position: absolute
bottom: 10px
padding-bottom: 10px
left: 10px
right: 10px
height: 140px
//height: 140px
// Height relates to .tab-content height
padding-top: 35px
padding-left: 12px
@ -69,6 +69,37 @@
margin-right: 3px
vertical-align: top
&.hero .properties
@include flexbox()
@include flex-wrap()
@include flex-column()
@include flex-align-content-start()
.property-entry-item-group
display: inline-block
min-height: 38px
max-width: 212px
@include flexbox()
@include flex-wrap()
@include flex-center()
outline: 1px dashed #b86
position: relative
img.item-image
width: 38px
height: 38px
position: absolute
&:not(:hover) img.item-image
-webkit-filter: sepia(100%)
filter: sepia(100%)
.spell-palette-entry-view
margin-left: 38px
width: 174px
width: -webkit-calc(100% - 38px)
width: calc(100% - 38px)
.code-language-logo
position: absolute
width: 16px

View file

@ -13,6 +13,10 @@
border: 1px solid transparent
cursor: pointer
@include user-select(all)
height: 19px
text-overflow: ellipsis
overflow: hidden
white-space: nowrap
&:hover
border: 1px solid #000000

View file

@ -42,12 +42,18 @@
position: relative
margin: 0 auto
canvas#surface
background-color: #333
canvas#webgl-surface, canvas#normal-surface
display: block
z-index: 1
margin: 0 auto
canvas#webgl-surface
background-color: #333
canvas#normal-surface
position: absolute
top: 0
pointer-events: none
min-width: 1024px
position: relative

View file

@ -75,14 +75,15 @@ body
a(href='/teachers', data-i18n="nav.teachers") Teachers
if me.isAdmin()
a(href='/admin', data-i18n="nav.admin") Admin
.share-buttons
if !isIE
.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_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")
if usesSocialMedia
.share-buttons
if !isIE
.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_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")
.partner-badges
a.mixpanel-badge(href="https://mixpanel.com/f/partner")

View file

@ -43,11 +43,11 @@
.form
.form-group.select-group
span.help-block(data-i18n="choose_hero.programming_language_description") Which programming language do you want to use?
label.control-label(for="option-code-language", data-i18n="choose_hero.programming_language") Programming Language
select#option-code-language(name="code-language")
for option in codeLanguages
option(value=option.id, selected=codeLanguage === option.id)= option.name
span.help-block(data-i18n="choose_hero.programming_language_description") Which programming language do you want to use?
if level
.form-group.select-group

View file

@ -9,7 +9,6 @@
.progress-bar.progress-bar-success
#tip-wrapper
strong.tip(data-i18n='play_level.tip_insert_positions') Shift+Click a point on the map to insert it into the spell editor.
strong.tip(data-i18n='play_level.tip_toggle_play') Toggle play/paused with Ctrl+P.
strong.tip(data-i18n='play_level.tip_scrub_shortcut') Ctrl+[ and Ctrl+] rewind and fast-forward.
strong.tip(data-i18n='play_level.tip_guide_exists') Click the guide at the top of the page for useful info.

View file

@ -1,3 +1,3 @@
button.btn.btn-lg.btn-inverse.banner.cast-button(title=castVerbose, data-i18n="play_level.tome_run_button_ran") Ran
button.btn.btn-lg.btn-success.banner.submit-button(title=castRealTimeVerbose) Submit
button.btn.btn-lg.btn-success.banner.submit-button(title=castRealTimeVerbose, data-i18n="play_level.tome_submit_button") Submit

View file

@ -1,12 +1,17 @@
img(src="/images/level/code_palette_background.png").code-palette-background
span.code-palette-background
.code-language-logo
ul(class="nav nav-pills" + (tabbed ? ' multiple-tabs' : ''))
each slug, group in entryGroupSlugs
li(class=group == "this" || slug == "available-spells" ? "active" : "")
a(data-toggle="pill", data-target='#palette-tab-' + slug)
h4= entryGroupNames[group]
.tab-content
each slug, group in entryGroupSlugs
div(id="palette-tab-" + slug, class="tab-pane nano" + (group == "this" || slug == defaultGroupSlug ? " active" : ""))
div(class="properties properties-" + slug + " nano-content")
if entryGroupSlugs
// Non-hero; group by entry groups, or maybe nothing.
.code-language-logo
ul(class="nav nav-pills" + (tabbed ? ' multiple-tabs' : ''))
each slug, group in entryGroupSlugs
li(class=group == "this" || slug == "available-spells" ? "active" : "")
a(data-toggle="pill", data-target='#palette-tab-' + slug)
h4= entryGroupNames[group]
.tab-content
each slug, group in entryGroupSlugs
div(id="palette-tab-" + slug, class="tab-pane nano" + (group == "this" || slug == defaultGroupSlug ? " active" : ""))
div(class="properties properties-" + slug + " nano-content")
else
// Hero; group by items, no tabs.
.properties

View file

@ -1 +1 @@
span= doc.title
span.doc-title= doc.title

View file

@ -105,6 +105,10 @@ if doc.returns
if doc.returns.description
div!= marked(doc.returns.description)
if item
p
em Granted by #{item.get('name')}.
if selectedMethod
p
em Write the body of this method below.

View file

@ -3,7 +3,8 @@
.level-content
#control-bar-view
#canvas-wrapper
canvas(width=924, height=589)#surface
canvas(width=924, height=589)#webgl-surface
canvas(width=924, height=589)#normal-surface
#canvas-left-gradient.gradient
#canvas-top-gradient.gradient
#gold-view.secret.expanded

View file

@ -36,15 +36,17 @@ block append content
th XP
each earnedAchievement in earnedAchievements.models
- var achievement = earnedAchievement.get('achievement');
tr
td= achievement.i18nName()
td= achievement.i18nDescription()
td= moment().format("MMMM Do YYYY", earnedAchievement.get('changed'))
if achievement.isRepeatable()
td= earnedAchievement.get('achievedAmount')
else
td
td= earnedAchievement.get('earnedPoints')
if achievement.get('category')
// No level-specific achievements in here.
tr
td= achievement.i18nName()
td= achievement.i18nDescription()
td= moment().format("MMMM Do YYYY", earnedAchievement.get('changed'))
if achievement.isRepeatable()
td= earnedAchievement.get('achievedAmount')
else
td
td= earnedAchievement.get('earnedPoints')
else
.panel#no-achievements
.panel-body(data-i18n="user.no_achievements") No achievements earned yet.

View file

@ -334,7 +334,7 @@ module.exports = class InventoryView extends CocoView
'the-second-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
'new-sight': {'right-hand': 'longsword', 'programming-book': 'programmaticon-i'}
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'longsword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'a-bolt-in-the-dark': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', eyes: 'crude-glasses'}
'closing-the-distance': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', eyes: 'crude-glasses'}
'the-final-kithmaze': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'kithgard-gates': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
'defence-of-plainswood': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}

View file

@ -70,6 +70,7 @@ module.exports = class RootView extends CocoView
getRenderData: ->
c = super()
c.showBackground = @showBackground
c.usesSocialMedia = @usesSocialMedia
c
afterRender: ->

View file

@ -42,7 +42,7 @@ module.exports = class AuthModal extends ModalView
afterInsert: ->
super()
_.delay application.router.renderLoginButtons, 500
_.delay (=> application.router.renderLoginButtons()), 500
_.delay (=> $('input:visible:first', @$el).focus()), 500
onSignupInstead: (e) ->

View file

@ -205,8 +205,9 @@ module.exports = class SpectateLevelView extends RootView
# initialization
initSurface: ->
surfaceCanvas = $('canvas#surface', @$el)
@surface = new Surface(@world, surfaceCanvas, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, spectateGame: true, wizards: @level.get('type', true) isnt 'hero')
webGLSurface = $('canvas#webgl-surface', @$el)
normalSurface = $('canvas#normal-surface', @$el)
@surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, spectateGame: true, wizards: @level.get('type', true) isnt 'hero')
worldBounds = @world.getBounds()
bounds = [{x:worldBounds.left, y:worldBounds.top}, {x:worldBounds.right, y:worldBounds.bottom}]
@surface.camera.setBounds(bounds)

View file

@ -97,7 +97,7 @@ module.exports = class WorldMapView extends RootView
@$el.find('.level').tooltip()
@$el.addClass _.string.slugify @terrain
@updateVolume()
@highlightElement '.level.next', delay: 8000, duration: 20000, rotation: 0, sides: ['top']
@highlightElement '.level.next', delay: 2000, duration: 60000, rotation: 0, sides: ['top']
onSessionsLoaded: (e) ->
for session in @sessions.models

View file

@ -25,6 +25,7 @@ class LevelSessionsCollection extends CocoCollection
module.exports = class LadderView extends RootView
id: 'ladder-view'
template: require 'templates/play/ladder/ladder'
usesSocialMedia: true
subscriptions:
'application:idle-changed': 'onIdleChanged'

View file

@ -28,6 +28,7 @@ module.exports = class VictoryModal extends ModalView
'enter': -> 'onPlayNextLevel'
constructor: (options) ->
application.router.initializeSocialMediaServices()
victory = options.level.get('victory')
body = utils.i18n(victory, 'body') or 'Sorry, this level has no victory message yet.'
@body = marked(body)

View file

@ -83,9 +83,11 @@ module.exports = class DocFormatter
@doc.title = if @options.shortenize then @doc.shorterName else @doc.shortName
# Grab the language-specific documentation for some sub-properties, if we have it.
toTranslate = [{obj: @doc, prop: 'description'}, {obj: @doc, prop: 'example'}, {obj: @doc, prop: 'returns'}]
toTranslate = [{obj: @doc, prop: 'description'}, {obj: @doc, prop: 'example'}]
for arg in (@doc.args ? [])
toTranslate.push {obj: arg, prop: 'example'}, {obj: arg, prop: 'description'}
if @doc.returns
toTranslate.push {obj: @doc.returns, prop: 'example'}, {obj: @doc.returns, prop: 'description'}
for {obj, prop} in toTranslate
if val = obj[prop]?[@options.language]
obj[prop] = val
@ -93,7 +95,7 @@ module.exports = class DocFormatter
obj[prop] = null
formatPopover: ->
content = popoverTemplate doc: @doc, language: @options.language, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? []), writable: @options.writable, selectedMethod: @options.selectedMethod, cooldowns: @inferCooldowns()
content = popoverTemplate doc: @doc, language: @options.language, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? []), writable: @options.writable, selectedMethod: @options.selectedMethod, cooldowns: @inferCooldowns(), item: @options.item
owner = if @doc.owner is 'this' then @options.thang else window[@doc.owner]
content = content.replace /#{spriteName}/g, @options.thang.type ? @options.thang.spriteName # Prefer type, and excluded the quotes we'd get with @formatValue
content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.'))

View file

@ -4,6 +4,7 @@ template = require 'templates/play/level/tome/spell_palette'
filters = require 'lib/image_filter'
SpellPaletteEntryView = require './SpellPaletteEntryView'
LevelComponent = require 'models/LevelComponent'
ThangType = require 'models/ThangType'
EditorConfigModal = require '../modal/EditorConfigModal'
N_ROWS = 4
@ -26,6 +27,7 @@ module.exports = class SpellPaletteView extends CocoView
super options
@thang = options.thang
@createPalette()
$(window).on 'resize', @onResize
getRenderData: ->
c = super()
@ -38,20 +40,53 @@ module.exports = class SpellPaletteView extends CocoView
afterRender: ->
super()
for group, entries of @entryGroups
groupSlug = @entryGroupSlugs[group]
for columnNumber, entryColumn of entries
col = $('<div class="property-entry-column"></div>').appendTo @$el.find(".properties-#{groupSlug}")
for entry in entryColumn
col.append entry.el
if @entryGroupSlugs
for group, entries of @entryGroups
groupSlug = @entryGroupSlugs[group]
for columnNumber, entryColumn of entries
col = $('<div class="property-entry-column"></div>').appendTo @$el.find(".properties-#{groupSlug}")
for entry in entryColumn
col.append entry.el
entry.render() # Render after appending so that we can access parent container for popover
$('.nano').nanoScroller()
@updateCodeLanguage @options.language
else
@entryGroupElements = {}
for group, entries of @entryGroups
@entryGroupElements[group] = itemGroup = $('<div class="property-entry-item-group"></div>').appendTo @$el.find('.properties')
itemGroup.append $('<img class="item-image"></img>').attr('src', entries[0].options.item.getPortraitURL()).css('top', Math.max(0, 19 * (entries.length - 2) / 2)) if entries[0].options.item?.getPortraitURL
for entry in entries
itemGroup.append entry.el
entry.render() # Render after appending so that we can access parent container for popover
$('.nano').nanoScroller()
@updateCodeLanguage @options.language
@$el.addClass 'hero'
@updateMaxHeight()
afterInsert: ->
super()
_.delay => @$el?.css('bottom', 0) unless $('#spell-view').is('.shown')
updateCodeLanguage: (language) ->
@options.language = language
@$el.find('.code-language-logo').removeClass().addClass 'code-language-logo ' + language
updateMaxHeight: ->
return unless @isHero
nColumns = Math.floor @$el.find('.properties').innerWidth() / 212 # ~212px is a good max entry width; will always have 2 columns
columns = ({items: [], nEntries: 0} for i in [0 ... nColumns])
nRows = 0
for group, entries of @entryGroups
shortestColumn = _.sortBy(columns, (column) -> column.nEntries)[0]
shortestColumn.nEntries += Math.max 2, entries.length
shortestColumn.items.push @entryGroupElements[group]
nRows = Math.max nRows, shortestColumn.nEntries
for column in columns
for item in column.items
item.detach().appendTo @$el.find('.properties')
@$el.find('.properties').css('height', 19 * (nRows + 1))
onResize: (e) =>
@updateMaxHeight()
createPalette: ->
Backbone.Mediator.publish 'tome:palette-cleared', {thangID: @thang.id}
lcs = @supermodel.getModels LevelComponent
@ -86,6 +121,12 @@ module.exports = class SpellPaletteView extends CocoView
else
propStorage =
'this': ['apiProperties', 'apiMethods']
if @options.level.get('type', true) isnt 'hero' or not @options.programmable
@organizePalette propStorage, allDocs, excludedDocs
else
@organizePaletteHero propStorage, allDocs, excludedDocs
organizePalette: (propStorage, allDocs, excludedDocs) ->
count = 0
propGroups = {}
for owner, storages of propStorage
@ -95,13 +136,11 @@ module.exports = class SpellPaletteView extends CocoView
added = _.sortBy(props).slice()
propGroups[owner] = (propGroups[owner] ? []).concat added
count += added.length
Backbone.Mediator.publish 'tome:update-snippets', propGroups: propGroups, allDocs: allDocs, language: @options.language
shortenize = count > 6
tabbify = count >= 10
@entries = []
Backbone.Mediator.publish 'tome:update-snippets', propGroups: propGroups, allDocs: allDocs, language: @options.language
for owner, props of propGroups
for prop in props
doc = _.find (allDocs['__' + prop] ? []), (doc) ->
@ -130,22 +169,68 @@ module.exports = class SpellPaletteView extends CocoView
@defaultGroupSlug = _.string.slugify defaultGroup
@entryGroupSlugs = {}
@entryGroupNames = {}
iOSEntryGroups = {}
for group, entries of @entryGroups
@entryGroups[group] = _.groupBy entries, (entry, i) -> Math.floor i / N_ROWS
@entryGroupSlugs[group] = _.string.slugify group
@entryGroupNames[group] = group
iOSEntryGroups[group] = (entry.doc for entry in entries)
if thisName = {coffeescript: '@', lua: 'self', clojure: 'self'}[@options.language]
if @entryGroupNames.this
@entryGroupNames.this = thisName
iOSEntryGroups[thisName] = iOSEntryGroups.this
delete iOSEntryGroups.this
Backbone.Mediator.publish 'tome:palette-updated', thangID: @thang.id, entryGroups: JSON.stringify(iOSEntryGroups) # TODO: make it sort these by granting items if it's a hero level
addEntry: (doc, shortenize, tabbify, isSnippet=false) ->
organizePaletteHero: (propStorage, allDocs, excludedDocs) ->
# Assign any kind of programmable properties to the items that grant them.
@isHero = true
itemThangTypes = {}
itemThangTypes[tt.get('name')] = tt for tt in @supermodel.getModels ThangType
propsByItem = {}
propCount = 0
itemsByProp = {}
for slot, inventoryID of @thang.inventoryIDs ? {}
if item = itemThangTypes[inventoryID]
for component in item.get('components') when component.config
for owner, storages of propStorage
if props = component.config[storages]
for prop in _.sortBy(props) when prop[0] isnt '_' # no private properties
propsByItem[item.get('name')] ?= []
propsByItem[item.get('name')].push owner: owner, prop: prop, item: item
itemsByProp[prop] = item
++propCount
else
console.log @thang.id, "couldn't find item ThangType for", slot, inventoryID
# Assign any unassigned properties to the hero itself.
for owner, storage of propStorage
for prop in _.reject(@thang[storage] ? [], (prop) -> itemsByProp[prop] or prop[0] is '_') # no private properties
propsByItem['Hero'] ?= []
propsByItem['Hero'].push owner: owner, prop: prop, item: null
++propCount
Backbone.Mediator.publish 'tome:update-snippets', propGroups: propsByItem, allDocs: allDocs, language: @options.language
shortenize = propCount > 6
@entries = []
for itemName, props of propsByItem
for prop, propIndex in props
item = prop.item
owner = prop.owner
prop = prop.prop
doc = _.find (allDocs['__' + prop] ? []), (doc) ->
return true if doc.owner is owner
return (owner is 'this' or owner is 'more') and (not doc.owner? or doc.owner is 'this')
if not doc and not excludedDocs['__' + prop]
console.log 'could not find doc for', prop, 'from', allDocs['__' + prop], 'for', owner, 'of', propGroups, 'with item', item
doc ?= prop
if doc
@entries.push @addEntry(doc, shortenize, false, owner is 'snippets', item, propIndex > 0)
@entryGroups = _.groupBy @entries, (entry) -> itemsByProp[entry.doc.name]?.get('name') ? 'Hero'
iOSEntryGroups = {}
for group, entries of @entryGroups
iOSEntryGroups[group] = (entry.doc for entry in entries)
Backbone.Mediator.publish 'tome:palette-updated', thangID: @thang.id, entryGroups: JSON.stringify(iOSEntryGroups)
addEntry: (doc, shortenize, tabbify, isSnippet=false, item=null, showImage=false) ->
writable = (if _.isString(doc) then doc else doc.name) in (@thang.apiUserProperties ? [])
new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, tabbify: tabbify, isSnippet: isSnippet, language: @options.language, writable: writable, level: @options.level
new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, tabbify: tabbify, isSnippet: isSnippet, language: @options.language, writable: writable, level: @options.level, item: item, showImage: showImage
onDisableControls: (e) -> @toggleControls e, false
onEnableControls: (e) -> @toggleControls e, true
@ -181,4 +266,5 @@ module.exports = class SpellPaletteView extends CocoView
destroy: ->
entry.destroy() for entry in @entries
@toggleBackground = null
$(window).off 'resize', @onResize
super()

View file

@ -65,6 +65,7 @@ module.exports = class SpellView extends CocoView
@problems = []
@writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything
@highlightCurrentLine = _.throttle @highlightCurrentLine, 100
$(window).on 'resize', @onWindowResize
afterRender: ->
super()
@ -208,8 +209,13 @@ module.exports = class SpellView extends CocoView
addZatannaSnippets: (e) ->
return unless @zatanna and @autocomplete
snippetEntries = []
for owner, props of e.propGroups
for group, props of e.propGroups
for prop in props
if _.isString prop # organizePalette
owner = group
else # organizePaletteHero
owner = prop.owner
prop = prop.prop
doc = _.find (e.allDocs['__' + prop] ? []), (doc) ->
return true if doc.owner is owner
return (owner is 'this' or owner is 'more') and (not doc.owner? or doc.owner is 'this')
@ -281,6 +287,8 @@ module.exports = class SpellView extends CocoView
setThang: (thang) ->
@focus()
@lastScreenLineCount = null
@updateLines()
return if thang.id is @thang?.id
@thang = thang
@spellThang = @spell.thangs[@thang.id]
@ -305,6 +313,31 @@ module.exports = class SpellView extends CocoView
return if @aceDoc.undergoingFirepadOperation # from my Firepad ACE adapter
Backbone.Mediator.publish 'tome:editing-began', {}
updateLines: =>
# Make sure there are always blank lines for the player to type on, and that the editor resizes to the height of the lines.
lineCount = @aceDoc.getLength()
lastLine = @aceDoc.$lines[lineCount - 1]
if lastLine isnt ''
cursorPosition = @ace.getCursorPosition()
wasAtEnd = cursorPosition.row is lineCount - 1 and cursorPosition.column is lastLine.length
@aceDoc.insertNewLine row: lineCount, column: 0 #lastLine.length
@ace.navigateLeft(1) if wasAtEnd
++lineCount
screenLineCount = @aceSession.getScreenLength()
if screenLineCount isnt @lastScreenLineCount
@lastScreenLineCount = screenLineCount
lineHeight = @ace.renderer.lineHeight or 20
tomeHeight = $('#tome-view').innerHeight()
spellListTabEntryHeight = $('#spell-list-tab-entry-view').outerHeight()
spellToolbarHeight = $('.spell-toolbar-view').outerHeight()
spellPaletteHeight = $('#spell-palette-view').outerHeight()
maxHeight = tomeHeight - spellListTabEntryHeight - spellToolbarHeight - spellPaletteHeight
linesAtMaxHeight = Math.floor(maxHeight / lineHeight)
lines = Math.max 8, Math.min(screenLineCount + 4, linesAtMaxHeight)
# 2 lines buffer is nice, but 4 leaves room to put problem alerts.
@ace.setOptions minLines: lines, maxLines: lines
$('#spell-palette-view').css('top', 38 + 45 + lineHeight * lines) # Move spell palette up, slightly underlapping us.
onManualCast: (e) ->
cast = @$el.parent().length
@recompile cast, e.realTime
@ -361,14 +394,16 @@ module.exports = class SpellView extends CocoView
_.debounce @notifyEditingEnded, 1000
_.throttle @notifyEditingBegan, 250
_.throttle @notifySpellChanged, 300
_.throttle @updateLines, 500
]
@onCodeChangeMetaHandler = =>
return if @eventsSuppressed
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'code-change', volume: 0.5
@spell.hasChangedSignificantly @getSource(), @spellThang.aether.raw, (hasChanged) =>
if not @spellThang or hasChanged
callback() for callback in onSignificantChange # Do these first
callback() for callback in onAnyChange # Then these
if @spellThang
@spell.hasChangedSignificantly @getSource(), @spellThang.aether.raw, (hasChanged) =>
if not @spellThang or hasChanged
callback() for callback in onSignificantChange # Do these first
callback() for callback in onAnyChange # Then these
@aceDoc.on 'change', @onCodeChangeMetaHandler
setRecompileNeeded: (@recompileNeeded) =>
@ -695,7 +730,15 @@ module.exports = class SpellView extends CocoView
@ace.setValue pretty
onMaximizeToggled: (e) ->
_.delay (=> @ace?.resize true), 500 # Wait $level-resize-transition-time.
_.delay (=> @resize()), 500 + 100 # Wait $level-resize-transition-time, plus a bit.
onWindowResize: (e) =>
_.delay (=> @resize?()), 500 + 100 # Wait $level-resize-transition-time, plus a bit.
resize: ->
@ace?.resize true
@lastScreenLineCount = null
@updateLines()
onChangeEditorConfig: (e) ->
aceConfig = me.get('aceConfig') ? {}
@ -739,4 +782,5 @@ module.exports = class SpellView extends CocoView
@aceSession?.selection.off 'changeCursor', @onCursorActivity
@destroyAceEditor(@ace)
@debugView?.destroy()
$(window).off 'resize', @onWindowResize
super()

View file

@ -181,6 +181,7 @@ module.exports = class TomeView extends CocoView
@spellTabView = null
@removeSubView @spellPaletteView if @spellPaletteView
@spellPaletteView = null
@$el.find('#spell-palette-view').hide()
@castButton?.$el.hide()
@thangList?.$el.show()
@ -204,10 +205,10 @@ module.exports = class TomeView extends CocoView
@castButton.attachTo @spellView
@thangList?.$el.hide()
Backbone.Mediator.publish 'tome:spell-shown', thang: thang, spell: spell
@updateSpellPalette thang, spell
@spellList.setThangAndSpell thang, spell
@spellView?.setThang thang
@spellTabView?.setThang thang
@updateSpellPalette thang, spell
updateSpellPalette: (thang, spell) ->
return unless thang and @spellPaletteView?.thang isnt thang and thang.programmableProperties or thang.apiProperties

View file

@ -48,9 +48,8 @@ module.exports = class AchievementsView extends UserView
# After user is loaded
if @user and not @user.isAnonymous()
context.earnedAchievements = @earnedAchievements
context.achievements = @achievements
context.achievementsByCategory = {}
for achievement in @achievements.models
for achievement in @achievements.models when achievement.get('category')
context.achievementsByCategory[achievement.get('category')] ?= []
context.achievementsByCategory[achievement.get('category')].push achievement
context

View file

@ -30,6 +30,7 @@ module.exports = class JobProfileView extends UserView
id: 'profile-view'
template: template
showBackground: false
usesSocialMedia: true
subscriptions:
'auth:linkedin-api-loaded': 'onLinkedInLoaded'