Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Barney 2015-04-09 10:15:17 +08:00
commit 03e2ab6ee7
46 changed files with 781 additions and 336 deletions

View file

@ -76,7 +76,7 @@
<script src="/lib/ace/ace.js" defer></script> <script src="/lib/ace/ace.js" defer></script>
<script src="/javascripts/vendor.js" defer></script> <script src="/javascripts/vendor.js" defer></script>
<script src="/javascripts/aether.js" defer></script> <script src="/javascripts/aether.js" defer></script>
<script src="/javascripts/app.js" defer></script> <!-- it's all Backbone! --> <script src="/javascripts/app.js" defer></script>
<![endif]> <![endif]>
<script> <script>

View file

@ -72,10 +72,12 @@ module.exports = class LevelLoader extends CocoClass
url += "?team=#{@team}" if @team url += "?team=#{@team}" if @team
session = new LevelSession().setURL url session = new LevelSession().setURL url
session.project = ['creator', 'team', 'heroConfig', 'codeLanguage', 'submittedCodeLanguage', 'state'] if @headless
@sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false}) @sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false})
@session = @sessionResource.model @session = @sessionResource.model
if @opponentSessionID if @opponentSessionID
opponentSession = new LevelSession().setURL "/db/level.session/#{@opponentSessionID}" opponentSession = new LevelSession().setURL "/db/level.session/#{@opponentSessionID}"
opponentSession.project = session.project if @headless
@opponentSessionResource = @supermodel.loadModel(opponentSession, 'opponent_session', {cache: false}) @opponentSessionResource = @supermodel.loadModel(opponentSession, 'opponent_session', {cache: false})
@opponentSession = @opponentSessionResource.model @opponentSession = @opponentSessionResource.model

View file

@ -94,14 +94,12 @@
campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science."
share_progress_modal: share_progress_modal:
blurb: "Youre making great progress! Tell someone how much you've learned with CodeCombat." blurb: "Youre making great progress! Tell your parent how much you've learned with CodeCombat." #{change}
email_invalid: "Email address invalid." email_invalid: "Email address invalid."
form_blurb: "Enter their email below and well show them!" form_blurb: "Enter your parent's email below and well show them!"
form_label: "Email Address" form_label: "Email Address"
placeholder: "email address" placeholder: "email address"
title: "Excellent Work, Apprentice" title: "Excellent Work, Apprentice"
tell_friend: "Tell your Friend"
tell_parent: "Tell your Parent"
login: login:
sign_up: "Create Account" sign_up: "Create Account"
@ -263,6 +261,7 @@
victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!"
victory_experience_gained: "XP Gained" victory_experience_gained: "XP Gained"
victory_gems_gained: "Gems Gained" victory_gems_gained: "Gems Gained"
victory_become_a_viking: "Become a Viking"
guide_title: "Guide" guide_title: "Guide"
tome_minion_spells: "Your Minions' Spells" # Only in old-style levels. tome_minion_spells: "Your Minions' Spells" # Only in old-style levels.
tome_read_only_spells: "Read-Only Spells" # Only in old-style levels. tome_read_only_spells: "Read-Only Spells" # Only in old-style levels.
@ -398,9 +397,9 @@
subscribe: subscribe:
comparison_blurb: "Sharpen your skills with a CodeCombat subscription!" comparison_blurb: "Sharpen your skills with a CodeCombat subscription!"
feature1: "60+ basic levels across 4 worlds" feature1: "80+ basic levels across 4 worlds" # {change}
feature2: "7 powerful <strong>new heroes</strong> with unique skills!" feature2: "7 powerful <strong>new heroes</strong> with unique skills!"
feature3: "30+ bonus levels" feature3: "50+ bonus levels" # {change}
feature4: "<strong>3500 bonus gems</strong> every month!" feature4: "<strong>3500 bonus gems</strong> every month!"
feature5: "Video tutorials" feature5: "Video tutorials"
feature6: "Premium email support" feature6: "Premium email support"
@ -428,6 +427,10 @@
parents_blurb1: "With CodeCombat, your child learns by writing real code. They start by learning simple commands, and progress to more advanced topics." parents_blurb1: "With CodeCombat, your child learns by writing real code. They start by learning simple commands, and progress to more advanced topics."
parents_blurb2: "For $9.99 USD/mo, they get new challenges every week and personal email support from professional programmers." parents_blurb2: "For $9.99 USD/mo, they get new challenges every week and personal email support from professional programmers."
parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe." parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe."
payment_methods: "Payment Methods"
payment_methods_title: "Accepted Payment Methods"
payment_methods_blurb1: "We currently accept credit cards, Alipay, and bitcoins."
payment_methods_blurb2: "If you require an alternate form of payment, please contact"
stripe_description: "Monthly Subscription" stripe_description: "Monthly Subscription"
subscription_required_to_play: "You'll need a subscription to play this level." subscription_required_to_play: "You'll need a subscription to play this level."
unlock_help_videos: "Subscribe to unlock all video tutorials." unlock_help_videos: "Subscribe to unlock all video tutorials."
@ -442,6 +445,7 @@
managed_subs: "Managed Subscriptions" managed_subs: "Managed Subscriptions"
managed_subs_desc: "Add subscriptions for other players (students, children, etc.)" managed_subs_desc: "Add subscriptions for other players (students, children, etc.)"
group_discounts: "Group discounts" group_discounts: "Group discounts"
group_discounts_1: "We also offer group discounts for bulk subscriptions."
group_discounts_1st: "1st subscription" group_discounts_1st: "1st subscription"
group_discounts_full: "Full price" group_discounts_full: "Full price"
group_discounts_2nd: "Subscriptions 2-11" group_discounts_2nd: "Subscriptions 2-11"
@ -588,8 +592,8 @@
teacher_subs_1: "Please contact" teacher_subs_1: "Please contact"
teacher_subs_2: "to set up a free monthly subscription." teacher_subs_2: "to set up a free monthly subscription."
sub_includes_title: "What is included in the subscription?" sub_includes_title: "What is included in the subscription?"
sub_includes_1: "In additional to the 70+ basic levels, students with a monthly subscription get access to these additional features:" sub_includes_1: "In addition to the 80+ basic levels, students with a monthly subscription get access to these additional features:" # {change}
sub_includes_2: "40+ practice levels" sub_includes_2: "50+ practice levels" # {change}
sub_includes_3: "Video tutorials" sub_includes_3: "Video tutorials"
sub_includes_4: "Premium email support" sub_includes_4: "Premium email support"
sub_includes_5: "7 new heroes with unique skills to master" sub_includes_5: "7 new heroes with unique skills to master"
@ -606,7 +610,6 @@
how_much_2: "monthly subscription" how_much_2: "monthly subscription"
how_much_3: "costs $9.99, and can be cancelled anytime." how_much_3: "costs $9.99, and can be cancelled anytime."
how_much_4: "Additionally, we provide discounts for larger groups:" how_much_4: "Additionally, we provide discounts for larger groups:"
group_discounts_1: "We also offer group discounts for bulk subscriptions."
sys_requirements_title: "System Requirements" sys_requirements_title: "System Requirements"
sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later."
sys_requirements_2: "CodeCombat is not supported on iPad yet." sys_requirements_2: "CodeCombat is not supported on iPad yet."

View file

@ -577,8 +577,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
teachers: teachers:
title: "CodeCombat: Informações para Professores" title: "CodeCombat: Informações para Professores"
# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." intro_1: "O CodeCombat é um jogo 'online' que ensina programação. Os estudantes escrevem código em linguagens de programação reais."
# intro_2: "No experience required!" intro_2: "Não é necessário ter experiência!"
free_title: "Quanto custa?" free_title: "Quanto custa?"
# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 per month for access to our other 120+ levels on our exclusive China servers." # cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 per month for access to our other 120+ levels on our exclusive China servers."
# free_1: "CodeCombat Basic is FREE! There are 70+ free levels which cover every concept." # free_1: "CodeCombat Basic is FREE! There are 70+ free levels which cover every concept."
@ -604,8 +604,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
how_much_1: "Uma" how_much_1: "Uma"
how_much_2: "subscrição mensal" how_much_2: "subscrição mensal"
how_much_3: "custa $9.99 e pode ser cancelada a qualquer momento." how_much_3: "custa $9.99 e pode ser cancelada a qualquer momento."
# how_much_4: "Additionally, we provide discounts for larger groups:" how_much_4: "Adicionalmente, oferecemos descontos para grupos maiores:"
# group_discounts_1: "We also offer group discounts for bulk subscriptions." group_discounts_1: "Também oferecemos descontos de grupo para subscrições em massa."
sys_requirements_title: "Requisitos do Sistema" sys_requirements_title: "Requisitos do Sistema"
sys_requirements_1: "Um navegador moderno. As versões mais recentes do Chrome, Firefox ou Safari. Internet Explorer 9 ou mais recente." sys_requirements_1: "Um navegador moderno. As versões mais recentes do Chrome, Firefox ou Safari. Internet Explorer 9 ou mais recente."
sys_requirements_2: "O CodeCombat ainda não é suportado em iPad's." sys_requirements_2: "O CodeCombat ainda não é suportado em iPad's."
@ -867,26 +867,26 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " # scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the "
# scribe_introduction_url_mozilla: "Mozilla Developer Network" # scribe_introduction_url_mozilla: "Mozilla Developer Network"
# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." # scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you."
scribe_attribute_1: "Habilidade com palavras é basicamente do que precisas. Não apenas gramática e ortografia, mas seres capaz de explicar ideias complicadas a outros." scribe_attribute_1: "Habilidade com palavras é basicamente o que precisas. Não apenas gramática e ortografia, mas seres capaz de explicar ideias complicadas a outros."
contact_us_url: "Contacta-nos" contact_us_url: "Contacta-nos"
scribe_join_description: "fala-nos um bocado de ti, a tua experiência com a programação e o tipo de coisas sobre o qual gostavas de escrever. Começamos a partir daí!" scribe_join_description: "fala-nos um bocado de ti, da tua experiência com a programação e do tipo de coisas sobre as quais gostarias de escrever. Começamos a partir daí!"
scribe_subscribe_desc: "Receber e-mails sobre anúncios relativos à escrita de artigos." scribe_subscribe_desc: "Receber e-mails sobre anúncios relativos à escrita de artigos."
diplomat_introduction_pref: "Portanto, se há uma coisa que aprendemos com o nosso " diplomat_introduction_pref: "Portanto, se há uma coisa que aprendemos com o nosso "
diplomat_launch_url: "lançamento em Outubro" diplomat_launch_url: "lançamento em Outubro"
diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras noutro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar estes níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti." diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras num outro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar os níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti."
diplomat_attribute_1: "Fluência em Inglês e no idioma para o qual gostarias de traduzir. Quando são tentadas passar ideias complicadas, é importante uma excelente compreensão das duas!" diplomat_attribute_1: "Fluência em Inglês e no idioma para o qual gostarias de traduzir. Quando são tentadas passar ideias complicadas, é importante uma excelente compreensão das duas!"
diplomat_i18n_page_prefix: "Podes começar a traduzir os nossos níveis se fores à nossa" diplomat_i18n_page_prefix: "Podes começar a traduzir os nossos níveis se fores à nossa"
diplomat_i18n_page: "página de traduções" diplomat_i18n_page: "página de traduções"
diplomat_i18n_page_suffix: ", ou a nossa interface e website no GitHub." diplomat_i18n_page_suffix: ", ou a nossa interface e website no GitHub."
diplomat_join_pref_github: "Encontra o ficheiro 'locale' do teu idioma " diplomat_join_pref_github: "Encontra o ficheiro 'locale' do teu idioma "
diplomat_github_url: "no GitHub" diplomat_github_url: "no GitHub"
diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda esta caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!" diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda a caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!"
diplomat_subscribe_desc: "Receber e-mails sobre desenvolvimentos da i18n e níveis para traduzir." diplomat_subscribe_desc: "Receber e-mails sobre desenvolvimentos da i18n e níveis para traduzir."
# ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." # ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you."
# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" # ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!"
# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" # ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!"
ambassador_join_note_strong: "Nota" ambassador_join_note_strong: "Nota"
ambassador_join_note_desc: "Uma das nossas maiores prioridades é construir níveis multijogador onde os jogadores com dificuldade a passar níveis podem invocar feiticeiros mais experientes para serem ajudados. Esta será uma ótima forma para os embixadores fazerem o que sabem. Vamos manter-te atualizado!" ambassador_join_note_desc: "Uma das nossas maiores prioridades é construir níveis multijogador onde os jogadores com dificuldade para passar níveis possam invocar feiticeiros mais experientes para os ajudarem. Esta será uma ótima forma de os embaixadores fazerem o que sabem. Vamos manter-te atualizado!"
ambassador_subscribe_desc: "Receber e-mails relativos a novidades do suporte e desenvolvimentos do modo multijogador." ambassador_subscribe_desc: "Receber e-mails relativos a novidades do suporte e desenvolvimentos do modo multijogador."
changes_auto_save: "As alterações são guardadas automaticamente quando clicas nas caixas." changes_auto_save: "As alterações são guardadas automaticamente quando clicas nas caixas."
diligent_scribes: "Os Nossos Dedicados Escrivões:" diligent_scribes: "Os Nossos Dedicados Escrivões:"

View file

@ -64,7 +64,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
achievements: "Úspechy" # Tooltip on achievement list button from /play achievements: "Úspechy" # Tooltip on achievement list button from /play
account: "Účet" # Tooltip on account button from /play account: "Účet" # Tooltip on account button from /play
settings: "Nastavenia" # Tooltip on settings button from /play settings: "Nastavenia" # Tooltip on settings button from /play
# poll: "Poll" # Tooltip on poll button from /play poll: "Anketa" # Tooltip on poll button from /play
next: "Ďalší" # Go from choose hero to choose inventory before playing a level next: "Ďalší" # Go from choose hero to choose inventory before playing a level
change_hero: "Zmeniť hrdinu" # Go back from choose inventory to choose hero change_hero: "Zmeniť hrdinu" # Go back from choose inventory to choose hero
choose_inventory: "Vyzbrojiť sa s predmetmi" choose_inventory: "Vyzbrojiť sa s predmetmi"
@ -254,7 +254,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
victory_sign_up: "Přihlásit se pre uloženie progresu" victory_sign_up: "Přihlásit se pre uloženie progresu"
victory_sign_up_poke: "Chceš uložiť svoj kód? Vytvorte si účet zdarma!" victory_sign_up_poke: "Chceš uložiť svoj kód? Vytvorte si účet zdarma!"
victory_rate_the_level: "Ohodnoťte túto úroveň: " # Only in old-style levels. victory_rate_the_level: "Ohodnoťte túto úroveň: " # Only in old-style levels.
victory_return_to_ladder: "Vrátiť sa na Rebríčky" victory_return_to_ladder: "Rebríčky"
victory_play_continue: "Pokračovať" victory_play_continue: "Pokračovať"
victory_saving_progress: "Stav ukladania" victory_saving_progress: "Stav ukladania"
victory_go_home: "Návrat Domov" # Only in old-style levels. victory_go_home: "Návrat Domov" # Only in old-style levels.
@ -334,7 +334,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
tip_javascript_java: "Porovnávať Javu a JavaScript je ako porovnávať auto a lietajúci koberec. - Chris Heilmann" tip_javascript_java: "Porovnávať Javu a JavaScript je ako porovnávať auto a lietajúci koberec. - Chris Heilmann"
tip_move_forward: "Ak nevieš lietať, bež, ak nemôžeš bežať, kráčaj, ak nemôžeš kráčať, choď po štyroch, ale nech už robíš čokoľvek, musíš sa hýbať vpred - Martin Luther King Jr." tip_move_forward: "Ak nevieš lietať, bež, ak nemôžeš bežať, kráčaj, ak nemôžeš kráčať, choď po štyroch, ale nech už robíš čokoľvek, musíš sa hýbať vpred - Martin Luther King Jr."
tip_google: "Máš problém, ktorý nevieš vyriešiť ? Vygoogluj si ho !" tip_google: "Máš problém, ktorý nevieš vyriešiť ? Vygoogluj si ho !"
tip_adding_evil: "pridanie špitky zla." tip_adding_evil: "Pridanie špitky zla."
tip_hate_computers: "Ľudia, ktorí si myslia, že nenávidia počítače, v skutočnosti nenávidia mizerných programátorov. - Larry Niven" tip_hate_computers: "Ľudia, ktorí si myslia, že nenávidia počítače, v skutočnosti nenávidia mizerných programátorov. - Larry Niven"
tip_open_source_contribute: "Aj ty môžeš zlepšiť CodeCombat !" tip_open_source_contribute: "Aj ty môžeš zlepšiť CodeCombat !"
tip_recurse: "Iterácia je ľudská, rekurzia božská.. - L. Peter Deutsch" tip_recurse: "Iterácia je ľudská, rekurzia božská.. - L. Peter Deutsch"
@ -484,11 +484,11 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
backstab: "Pichnutie do chrbta" # As in "this dagger does this much backstab damage" backstab: "Pichnutie do chrbta" # As in "this dagger does this much backstab damage"
skills: "Schopnosti" skills: "Schopnosti"
attack_1: "Upravuje na" attack_1: "Upravuje na"
attack_2: "hodnotu udávanej" attack_2: "hodnotu udávanej újmy zbraňou pre typ"
attack_3: "újmy zbraňou." attack_3: "."
health_1: "Upravuje hodnotu zdravia na" health_1: "Upravuje hodnotu zdravia na"
health_2: "hodnoty zaručenej" health_2: "hodnoty zaručenej brnením"
health_3: "brnením." health_3: "."
speed_1: "Pohybuje sa rýchlosťou" speed_1: "Pohybuje sa rýchlosťou"
speed_2: "metrov za sekundu." speed_2: "metrov za sekundu."
available_for_purchase: "Dostupné na zakúpenie" # Shows up when you have unlocked, but not purchased, a hero in the hero store available_for_purchase: "Dostupné na zakúpenie" # Shows up when you have unlocked, but not purchased, a hero in the hero store
@ -542,73 +542,73 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
editor_config_behaviors_label: "Chytré správanie" editor_config_behaviors_label: "Chytré správanie"
editor_config_behaviors_description: "Automaticky doplňuje hranaté a oblé zátvorky a úvodzovky." editor_config_behaviors_description: "Automaticky doplňuje hranaté a oblé zátvorky a úvodzovky."
# about: about:
# why_codecombat: "Why CodeCombat?" why_codecombat: "Prečo CodeCombat?"
# why_paragraph_1: "If you want to learn to program, you don't need lessons. You need to write a lot of code and have a great time doing it." why_paragraph_1: "Ak sa chceš naučiť programovať, nepotrebuješ lekcie. To, čo potrebuješ je možnosť písať veľa kódu a baviť sa pri tom."
# why_paragraph_2_prefix: "That's what programming is about. It's gotta be fun. Not fun like" why_paragraph_2_prefix: "O tom je programovanie. Nemá to byť zábava typu"
# why_paragraph_2_italic: "yay a badge" why_paragraph_2_italic: "jéj, mám ďalší odznak"
# why_paragraph_2_center: "but fun like" why_paragraph_2_center: ",ale nadšenie ako"
# why_paragraph_2_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" why_paragraph_2_italic_caps: "HNEĎ MAMI, LEN DOKONČÍM TÚTO ÚROVEŇ !"
# why_paragraph_2_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." why_paragraph_2_suffix: "CodeCombat je skutočná hra pre viacej hráčov, od ktorej sa dá ťažko odtrhnúť."
# why_paragraph_3: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." why_paragraph_3: "Ak už sa máš byť závislý na nejakej hre, tak nech je to táto, pri ktorej sa staneš čarodejníkom technického veku."
# press_title: "Bloggers/Press" press_title: "Blogeri/Tlač"
# press_paragraph_1_prefix: "Want to write about us? Feel free to download and use all of the resources included in our" press_paragraph_1_prefix: "Chceš o nás písať ? Môžeš si stiahnúť a použiť všetky zdroje zahrnuté v našom"
# press_paragraph_1_link: "press packet" press_paragraph_1_link: "tlačovom balíčku"
# press_paragraph_1_suffix: ". All logos and images may be used without contacting us directly." press_paragraph_1_suffix: ". Všetky logá a obrázky môžeš použiť bez toho, aby si nás priamo kontaktoval."
# team: "Team" team: "m"
# george_title: "Cofounder" george_title: "Spoluzakladateľ"
# george_blurb: "Businesser" george_blurb: "Podnikateľ"
# scott_title: "Cofounder" scott_title: "Spoluzakladateľ"
# scott_blurb: "Reasonable One" scott_blurb: "Ten rozumný"
# nick_title: "Cofounder" nick_title: "Spoluzakladateľ"
# nick_blurb: "Motivation Guru" nick_blurb: "Motivačný Guru"
# michael_title: "Programmer" michael_title: "Programátor"
# michael_blurb: "Sys Admin" michael_blurb: "Systémový administrátor"
# matt_title: "Programmer" matt_title: "Programátor"
# matt_blurb: "Bicyclist" matt_blurb: "Bicyklista"
# cat_title: "Chief Artisan" cat_title: "Najvyššia remeselníčka"
# cat_blurb: "Airbender" cat_blurb: "Ohýbačka vzduchu"
# josh_title: "Game Designer" josh_title: "Dizajnér hier"
# josh_blurb: "Floor Is Lava" josh_blurb: "Podlaha je láva"
# jose_title: "Music" jose_title: "Hudba"
# jose_blurb: "Taking Off" jose_blurb: "Vzlet"
# retrostyle_title: "Illustration" retrostyle_title: "Ilustrácia"
# retrostyle_blurb: "RetroStyle Games" retrostyle_blurb: "Retro hry"
# teachers: teachers:
# title: "CodeCombat: Info for Teachers" title: "CodeCombat: Informácie pre učiteľov"
# intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages." intro_1: "CodeCombat je online hra, ktorá učí programovať. Študenti píšu kód v skutočných programovacích jazykoch."
# intro_2: "No experience required!" intro_2: "Nie sú nutné žiadne predchádzajúce skúsenosti !"
# free_title: "How much does it cost?" free_title: "Koľko to stojí ?"
# cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 per month for access to our other 120+ levels on our exclusive China servers." # cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 per month for access to our other 120+ levels on our exclusive China servers."
# free_1: "CodeCombat Basic is FREE! There are 70+ free levels which cover every concept." free_1: "CodeCombat Basic is ZDARMA ! K dispozícii je 70+ úrovní pokrývajúcich každý koncept."
# free_2: "A monthly subscription provides access to video tutorials and extra practice levels." free_2: "Mesačné predplatné poskytuje prístup k videonávodom a k úrovniam na precvičenie navyše."
# teacher_subs_title: "Teachers get free subscriptions!" teacher_subs_title: "Pre učiteľov je predplatné zdarma !"
# teacher_subs_1: "Please contact" teacher_subs_1: "Napíšte na"
# teacher_subs_2: "to set up a free monthly subscription." teacher_subs_2: "pre zriadenie mesačného predplatného zdarma."
# sub_includes_title: "What is included in the subscription?" sub_includes_title: "Čo zahrnuje predplatné ?"
# sub_includes_1: "In additional to the 70+ basic levels, students with a monthly subscription get access to these additional features:" sub_includes_1: "Študenti s mesačným predplatným získajú ku 70+ základným úrovniam aj :"
# sub_includes_2: "40+ practice levels" sub_includes_2: "40+ tréningových úrovní"
# sub_includes_3: "Video tutorials" sub_includes_3: "Video návody"
# sub_includes_4: "Premium email support" sub_includes_4: "Prémiovú emailovú podporu"
# sub_includes_5: "7 new heroes with unique skills to master" sub_includes_5: "7 nových hrdinov s jedinečnými schopnosťami"
# sub_includes_6: "3500 bonus gems every month" sub_includes_6: "3500 bonusových diamantov každý mesiac"
# who_for_title: "Who is CodeCombat for?" who_for_title: "Pre koho je určený CodeComabt ?"
# who_for_1: "We recommend CodeCombat for students aged 9 and up. No prior programming experience is needed." who_for_1: "CodeCombat odporúčame pre žiakov od 9 rokov. Nie sú nutné žiadne predchádzajúce skúsenosti s programovaním."
# who_for_2: "We've designed CodeCombat to appeal to both boys and girls." who_for_2: "CodeCombat sme navrhli tak, aby oslovil chlapcov aj dievčatá."
# material_title: "How much material is there?" material_title: "Aký je objem učebnej látky ?"
# material_china: "Approximately 22 hours of gameplay spread over 120+ subscriber-only levels so far, with 5 new levels every week." # material_china: "Approximately 22 hours of gameplay spread over 120+ subscriber-only levels so far, with 5 new levels every week."
# material_1: "Approximately 8 hours of free content and an additional 14 hours of subscriber content, with 5 new levels every week." material_1: "Asi 8 hodín bezplatného obsahu a ďalších 14 hodín pre predplatiteľov. 5 nových úrovní každý týždeň."
# concepts_title: "What concepts are covered?" concepts_title: "Aké pojmy sú pokryté ?"
# how_much_title: "How much does a monthly subscription cost?" how_much_title: "Koľko stojí mesačné predplatné ?"
# how_much_1: "A" how_much_1: ""
# how_much_2: "monthly subscription" how_much_2: "Mesačné predplatné"
# how_much_3: "costs $9.99, and can be cancelled anytime." how_much_3: ", ktoré môže byť kedykoľvek zrušené, stojí 9.99$."
# how_much_4: "Additionally, we provide discounts for larger groups:" how_much_4: "Zľavy pre väčšie skupiny:"
# group_discounts_1: "We also offer group discounts for bulk subscriptions." # group_discounts_1: "We also offer group discounts for bulk subscriptions."
# sys_requirements_title: "System Requirements" sys_requirements_title: "Systémové požiadavky"
# sys_requirements_1: "A modern web browser. Newer versions of Chrome, Firefox, or Safari. Internet Explorer 9 or later." sys_requirements_1: "Moderný webový prehliadač. Nové verzie prehliadačov Chrome, Firefox alebo Safari. Internet Explorer 9 alebo novší."
# sys_requirements_2: "CodeCombat is not supported on iPad yet." sys_requirements_2: "CodeCombat nie je zatiaľ podprovaný pre iPad."
versions: versions:
save_version_title: "Ulož novú verziu" save_version_title: "Ulož novú verziu"
@ -700,43 +700,43 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
# beautify: "Beautify your code by standardizing its formatting." # beautify: "Beautify your code by standardizing its formatting."
# maximize_editor: "Maximize/minimize code editor." # maximize_editor: "Maximize/minimize code editor."
# community: community:
# main_title: "CodeCombat Community" main_title: "CodeCombat Komunita"
# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" introduction: "Pozri si spôsoby ako sa môžeš zapojiť a rozhodni sa, čo ťa najviac láka.Tešime sa na spoluprácu !"
# level_editor_prefix: "Use the CodeCombat" level_editor_prefix: "Použi"
# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" level_editor_suffix: "na vytvorenie a úpravu úrovní. Uživatelia vytvorili úrovne pre svoje triedy, svojích priateľov, žiakov, súrodencov a aj pre deň kódovania. Ak sa bojíš začínať celkom od začiatku, môžeš začať klonovaním a úpravou našich úrovní."
# thang_editor_prefix: "We call units within the game 'thangs'. Use the" thang_editor_prefix: "Jednotky hry nazývame vecičky - 'thangs'. Použi"
# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." thang_editor_suffix: "na úpravu grafiky. Povoľ jednotkám vrhať strely, zmeň smer animácie, zmeň body zásahu alebo nahraj vlastné vektorové sprity."
# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" article_editor_prefix: "Vidíš chybu v našich dokumentoch ? Chceš pridať inštrukcie k vlastným výtvorom ? Pozri sa na"
# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." article_editor_suffix: "a pomôž hráčom, aby získali, čo najviac z hrania na CodeCombat."
# find_us: "Find us on these sites" find_us: "Nájdeš nás na týchto stránkach"
# social_blog: "Read the CodeCombat blog on Sett" social_blog: "Prečítaj si blog na Sette"
# social_discource: "Join the discussion on our Discourse forum" social_discource: "Pridaj sa k diskusii na fóre Discourse"
# social_facebook: "Like CodeCombat on Facebook" social_facebook: "Daj Like CodeCombatu na Facebooku"
# social_twitter: "Follow CodeCombat on Twitter" social_twitter: "Sleduj CodeCombat na Twitteri"
# social_gplus: "Join CodeCombat on Google+" social_gplus: "Pripoj sa ku CodeCombatu na Google+"
# social_hipchat: "Chat with us in the public CodeCombat HipChat room" social_hipchat: "Chatujte s nami vo verejnej HipChat miestnosti"
# contribute_to_the_project: "Contribute to the project" contribute_to_the_project: "Prispej svojou prácou"
# classes: classes:
# archmage_title: "Archmage" archmage_title: "Arcimág"
# archmage_title_description: "(Coder)" archmage_title_description: "(Programátor)"
# archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!" archmage_summary: "Ak si vývojár so záujmom o kódovanie vzdelávacíh hier, staň sa Arcimágom a pomôž nám s vývojom CodeCombatu !"
# artisan_title: "Artisan" artisan_title: "Remeselník"
# artisan_title_description: "(Level Builder)" artisan_title_description: "(Tvorca úrovní)"
# artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program." artisan_summary: "Tvor a zdieľaj úrovne, ktoré si môžu zahrať tvoji priatelia. Staň sa Remeselníkom a nauč sa umenie učiť iných programovať."
# adventurer_title: "Adventurer" adventurer_title: "Dobrodruh"
# adventurer_title_description: "(Level Playtester)" adventurer_title_description: "(Testovač úrovní)"
# adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release." adventurer_summary: "Dostaň sa k naším novým úrovniam (dokonca aj k tým pre predplatiteľov) zdarma a o týždeň skôr. Pomôž nám odhaliť chyby pred ich zverejnemím."
# scribe_title: "Scribe" scribe_title: "Pisár"
# scribe_title_description: "(Article Editor)" scribe_title_description: "(Editor)"
# scribe_summary: "Good code needs good documentation. Write, edit, and improve the docs read by millions of players across the globe." scribe_summary: "Dobrý kód potrebuje dobrú dokumentáciu. Píš, edituj a vylepši dokumenty, ktoré čítajú milióny hráčov na celom svete."
# diplomat_title: "Diplomat" diplomat_title: "Diplomat"
# diplomat_title_description: "(Translator)" diplomat_title_description: "(Prekladateľ)"
# diplomat_summary: "CodeCombat is localized in 45+ languages by our Diplomats. Help us out and contribute translations." diplomat_summary: "CodeCombat je preložený vďaka naším diplomatom do 45+ jazykov. Pomôž nám s prekladom."
# ambassador_title: "Ambassador" ambassador_title: "Ambasador"
# ambassador_title_description: "(Support)" ambassador_title_description: "(Poradca)"
# ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world." ambassador_summary: "Kroť použivateľov nášho fóra a nasmeruj na správnu cestu tých, čo sa pýtajú. Naši ambasádori reprezentujú CodeCombat na celom svete."
# editor: # editor:
# main_title: "CodeCombat Editors" # main_title: "CodeCombat Editors"
@ -828,10 +828,10 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
# polls: # polls:
# priority: "Priority" # priority: "Priority"
# contribute: contribute:
# page_title: "Contributing" page_title: "Prispenie"
# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!" intro_blurb: "CodeCombat je 100% open source! Stovky nadšených hráčov nám pomohli dostať hru na dnešnú úroveň. Pripoj sa k nám a napíš ďalšiu kapitolu CodeCombatu, ktorý učí svet kódovať!"
# alert_account_message_intro: "Hey there!" alert_account_message_intro: "Ahoj!"
# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." # alert_account_message: "To subscribe for class emails, you'll need to be logged in first."
# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." # archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever."
# class_attributes: "Class Attributes" # class_attributes: "Class Attributes"
@ -896,61 +896,61 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
# translating_diplomats: "Our Translating Diplomats:" # translating_diplomats: "Our Translating Diplomats:"
# helpful_ambassadors: "Our Helpful Ambassadors:" # helpful_ambassadors: "Our Helpful Ambassadors:"
# ladder: ladder:
# please_login: "Please log in first before playing a ladder game." please_login: "Pred hraním rebríčkovej hry sa musíš najskôr prihlásiť."
# my_matches: "My Matches" my_matches: "Moje súboje"
# simulate: "Simulate" simulate: "Simuluj"
# simulation_explanation: "By simulating games you can get your game ranked faster!" simulation_explanation: "Simulovaním sa dostane hra rýchlejšie do rebríčka !!"
# simulate_games: "Simulate Games!" simulate_games: "Simuluj hry !"
# simulate_all: "RESET AND SIMULATE GAMES" simulate_all: "RESETUJ A SIMULUJ HRY"
# games_simulated_by: "Games simulated by you:" games_simulated_by: "Tebou simulované hry:"
# games_simulated_for: "Games simulated for you:" games_simulated_for: "Pre teba simulované hry:"
# games_simulated: "Games simulated" games_simulated: "Simulované hry"
# games_played: "Games played" games_played: "Odohrané hry"
# ratio: "Ratio" ratio: "Pomer"
# leaderboard: "Leaderboard" leaderboard: "Rebríček"
# battle_as: "Battle as " battle_as: "Hraj ako "
# summary_your: "Your " summary_your: "Tvoje "
# summary_matches: "Matches - " summary_matches: "Súboje - počet výhier "
# summary_wins: " Wins, " summary_wins: ", počet prehier "
# summary_losses: " Losses" summary_losses: " "
# rank_no_code: "No New Code to Rank" rank_no_code: "Žiadny nový kód na ocenenie"
# rank_my_game: "Rank My Game!" rank_my_game: "Oceň moju hru !"
# rank_submitting: "Submitting..." rank_submitting: "Odosielam..."
# rank_submitted: "Submitted for Ranking" rank_submitted: "Odoslané na ocenenie"
# rank_failed: "Failed to Rank" rank_failed: "Chyba pri oceňovaní"
# rank_being_ranked: "Game Being Ranked" rank_being_ranked: "Hra je oceňovaná"
# rank_last_submitted: "submitted " rank_last_submitted: "odoslané "
# help_simulate: "Help simulate games?" help_simulate: "Pomôžeš so simuláciou hier ?"
# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." code_being_simulated: "Tvoj nový kód je simulovaný iným hráčom. Rebríček sa obnoví po nových súbojoch."
# no_ranked_matches_pre: "No ranked matches for the " no_ranked_matches_pre: "Žiadne ocenené súboje pre "
# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." no_ranked_matches_post: " tím ! Hraj proti súperom a potom sa sem vráť a uvidíš ocenenie svojej hry."
# choose_opponent: "Choose an Opponent" choose_opponent: "Vyber si súpera"
# select_your_language: "Select your language!" select_your_language: "Vyber si jazyk !"
# tutorial_play: "Play Tutorial" tutorial_play: "Hraj tutoriál"
# tutorial_recommended: "Recommended if you've never played before" tutorial_recommended: "Odporúčané, pokiaľ si ešte nikdy nehral"
# tutorial_skip: "Skip Tutorial" tutorial_skip: "Preskoč tutoriál"
# tutorial_not_sure: "Not sure what's going on?" tutorial_not_sure: "Nie si si istý, čo sa deje ?"
# tutorial_play_first: "Play the Tutorial first." tutorial_play_first: "Hraj najskôr tutoriál."
# simple_ai: "Simple AI" simple_ai: "Jednoduchá umelá inteligencia"
# warmup: "Warmup" warmup: "Na rozohratie"
# friends_playing: "Friends Playing" friends_playing: "Hra proti priateľom"
# log_in_for_friends: "Log in to play with your friends!" log_in_for_friends: "Prihlás sa a hraj s priateľmi !"
# social_connect_blurb: "Connect and play against your friends!" social_connect_blurb: "Pripoj sa a hraj proti svojím priateľom !"
# invite_friends_to_battle: "Invite your friends to join you in battle!" invite_friends_to_battle: "Pozvi priateľov a bojuj s nimi !"
# fight: "Fight!" fight: "Bojuj !"
# watch_victory: "Watch your victory" watch_victory: "Pozri si svoju výhru"
# defeat_the: "Defeat the" defeat_the: "Poraz"
# tournament_started: ", started" tournament_started: ", spustený"
# tournament_ends: "Tournament ends" tournament_ends: "Turnaj končí"
# tournament_ended: "Tournament ended" tournament_ended: "Turnaj skončil"
# tournament_rules: "Tournament Rules" tournament_rules: "Pravidlá turnaja"
# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" tournament_blurb: "Píš kód, zbieraj mince, stavaj armády, rozdrv nepriateľov, vyhraj ceny v hodnote 40,000$. Greed tournament! Pozri sa na detaily."
# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" tournament_blurb_criss_cross: "Vyhraj ponuky, buduj cesty, preľsti súperov,zbieraj diamanty grab gems a vylepši svoju kariéru v našom Krížovkárskom turnaji ! Pozri sa na detaily"
# tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details" tournament_blurb_zero_sum: "Odviaž svoju kódovaciu kreativitu pri zbieraní mincí a bojovej taktike v spravodlivom vysokohorskom súboji medzi medzi červenou a modrou čarodejkou. Turnaj začal v piatok 27. marca 2015 a skončil 6. apríla 2015. Súťaž pre zábavu a slávu ! Pozri sa na detaily"
# tournament_blurb_blog: "on our blog" tournament_blurb_blog: "v našom blogu."
# rules: "Rules" rules: "Pravidlá"
# winners: "Winners" winners: "Víťazi"
user: user:
stats: "Stats" stats: "Stats"

View file

@ -1,5 +1,6 @@
storage = require 'core/storage' storage = require 'core/storage'
deltasLib = require 'core/deltas' deltasLib = require 'core/deltas'
locale = require 'locale/locale'
class CocoModel extends Backbone.Model class CocoModel extends Backbone.Model
idAttribute: '_id' idAttribute: '_id'
@ -26,6 +27,7 @@ class CocoModel extends Backbone.Model
# if fixed, RevertModal will also need the fix # if fixed, RevertModal will also need the fix
setProjection: (project) -> setProjection: (project) ->
# TODO: ends up getting done twice, since the URL is modified and the @project is modified. So don't do this, just set project directly... (?)
return if project is @project return if project is @project
url = @getURL() url = @getURL()
url += '&project=' unless /project=/.test url url += '&project=' unless /project=/.test url
@ -399,12 +401,19 @@ class CocoModel extends Backbone.Model
# use it to determine what properties actually need to be translated # use it to determine what properties actually need to be translated
props = workingSchema.props or [] props = workingSchema.props or []
props = (prop for prop in props when parentData[prop]) props = (prop for prop in props when parentData[prop])
#unless props.length
# console.log 'props is', props, 'path is', path, 'data is', data, 'parentData is', parentData, 'workingSchema is', workingSchema
# langCodeArrays.push _.without _.keys(locale), 'update' # Every language has covered a path with no properties to be translated.
# return
return if 'additionalProperties' of i18n # Workaround for #2630: Programmable is weird
# get a list of lang codes where its object has keys for every prop to be translated # get a list of lang codes where its object has keys for every prop to be translated
coverage = _.filter(_.keys(i18n), (langCode) -> coverage = _.filter(_.keys(i18n), (langCode) ->
translations = i18n[langCode] translations = i18n[langCode]
_.all((translations[prop] for prop in props)) _.all((translations[prop] for prop in props))
) )
#console.log 'got coverage', coverage, 'for', path, props, workingSchema, parentData
langCodeArrays.push coverage langCodeArrays.push coverage
) )

View file

@ -88,7 +88,7 @@ _.extend LevelSessionSchema.properties,
type: 'boolean' # Not tracked any more type: 'boolean' # Not tracked any more
frame: frame:
type: 'number' # Not tracked any more type: 'number' # Not tracked any more
thangs: thangs: # ... what is this? Is this used?
type: 'object' type: 'object'
additionalProperties: additionalProperties:
title: 'Thang' title: 'Thang'
@ -241,7 +241,7 @@ _.extend LevelSessionSchema.properties,
description: 'The date a match was computed.' description: 'The date a match was computed.'
playtime: playtime:
title: 'Playtime so far' title: 'Playtime so far'
description: 'The total seconds of playtime on this session when the match was computed.' description: 'The total seconds of playtime on this session when the match was computed. Not currently tracked.'
type: 'number' type: 'number'
metrics: metrics:
type: 'object' type: 'object'

View file

@ -274,6 +274,12 @@ _.extend UserSchema.properties,
levelSystemMiscPatches: c.int() levelSystemMiscPatches: c.int()
thangTypeTranslationPatches: c.int() thangTypeTranslationPatches: c.int()
thangTypeMiscPatches: c.int() thangTypeMiscPatches: c.int()
achievementTranslationPatches: c.int()
achievementMiscPatches: c.int()
pollTranslationPatches: c.int()
pollMiscPatches: c.int()
campaignTranslationPatches: c.int()
campaignMiscPatches: c.int()
earned: c.RewardSchema 'earned by achievements' earned: c.RewardSchema 'earned by achievements'
purchased: c.RewardSchema 'purchased with gems or money' purchased: c.RewardSchema 'purchased with gems or money'

View file

@ -26,9 +26,6 @@
height: 500px height: 500px
width: 100% width: 100%
// TODO: figure out why this is necessary
margin-bottom: 100px
.x.axis .x.axis
font-size: 9pt font-size: 9pt
path path

View file

@ -14,4 +14,4 @@
#poll-view #poll-view
min-height: 200px min-height: 200px
position: relative position: relative
z-index: 0

View file

@ -118,6 +118,20 @@
font-family: $headings-font-family font-family: $headings-font-family
font-size: 18px font-size: 18px
//- Payment methods info popover link
#payment-methods-info
position: absolute
right: 38px
top: 389px
text-decoration: underline
cursor: pointer
font-weight: bold
line-height: 18px
color: black
font-family: $headings-font-family
font-size: 18px
//- Purchase button //- Purchase button
.purchase-button .purchase-button

View file

@ -563,14 +563,20 @@ $gameControlMargin: 30px
width: 100% width: 100%
text-align: center text-align: center
.campaign-name, .levels-completed, .campaign-locked, .campaign-description .campaign-name, .levels-completed, .campaign-locked
margin: 0 margin: 0
color: white color: white
text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0 text-shadow: black 2px 2px 0, black -2px -2px 0, black 2px -2px 0, black -2px 2px 0, black 2px 0px 0, black 0px -2px 0, black -2px 0px 0, black 0px 2px 0
.campaign-locked
margin: 32px 0
.campaign-description .campaign-description
margin: 0px 40px margin: 0px 40px
font-size: 22px background: transparent url(/images/level/popover_border_background.png) no-repeat
background-size: 100% 100%
padding: 12px
color: black
.levels-completed .levels-completed
font-size: 22px font-size: 22px

View file

@ -369,6 +369,11 @@
.text-link .text-link
color: lighten(#0b63bc, 10%) color: lighten(#0b63bc, 10%)
.offer
display: none
p
color: white
html.no-borderimage html.no-borderimage
#hero-victory-modal #hero-victory-modal

View file

@ -39,24 +39,8 @@
font-weight: bold font-weight: bold
color: black color: black
p.parent-blurb, p.friend-blurb
line-height: 16px
display: none
.tell-parent-btn, .tell-friend-btn
margin: 10px
border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round
color: white
// width: 80px
font-size: 28px
line-height: 28px
text-transform: none
font-variant: small-caps
font-family: "Open Sans Condensed", "Helvetica Neue", Helvetica, Arial, sans-serif
.send-container .send-container
margin-top: 10px margin-top: 10px
display: none
.email-form .email-form
.email-input .email-input
width: 200px width: 200px
@ -77,7 +61,7 @@
.continue-container .continue-container
margin-top: 10px margin-top: 10px
margin-right: -12px margin-right: -12px
.back-link, .continue-link .continue-link
color: black color: black
font-weight: normal font-weight: normal
font-size: 11px font-size: 11px

View file

@ -1,12 +1,13 @@
extends /templates/base extends /templates/base
block content block content
if !me.isAdmin() if !me.isAdmin()
div You must be logged in as an admin to view this page. div You must be logged in as an admin to view this page.
else else
if total === 0 if total === 0
h1 Fetching subscriptions data... h4 Fetching dashboard data...
else else
.container-fluid .container-fluid
.row .row
@ -37,15 +38,59 @@ block content
div *Stripe APIs do not return information about inactive subs. div *Stripe APIs do not return information about inactive subs.
br h2 Recent Subscribers
if !subscribers || subscribers.length < 1
table.table.table-condensed.concepts-table h4 Fetching recent subscribers...
else
table.table.table-condensed
thead
tr
th User Start
th Sub Start
if subscriberCancelled
th Cancelled
else
th
th
//- th Name
th Email
th Hero
th Level
th Last Level
th Age
th Spoken
tbody
each subscriber in subscribers
tr
td= subscriber.user.dateCreated.substring(0, 10)
td= subscriber.start.substring(0, 10)
td
if subscriber.cancel
span= subscriber.cancel.substring(0, 10)
td
if subscriber.user.stripe.sponsorID
span Sponsored
//- td
//- a(href="/user/#{subscriber.user._id}")= subscriber.user.name || 'Anoner'
td= subscriber.user.emailLower
td= subscriber.hero
td= subscriber.level
td= subscriber.user.lastLevel
td= subscriber.user.ageRange
td= subscriber.user.preferredLanguage
h2 Subscriptions
if !subs || subs.length < 1
h4 Fetching subscriptions...
else
table.table.table-condensed
thead thead
tr tr
th Day th Day
th Total th Total
th Started th Started
th Cancelled th Cancelled
th Net
tbody tbody
each sub in subs each sub in subs
tr tr
@ -53,3 +98,4 @@ block content
td= sub.total td= sub.total
td= sub.started td= sub.started
td= sub.cancelled td= sub.cancelled
td= sub.started - sub.cancelled

View file

@ -14,9 +14,9 @@ block content
h2 h2
a.spl(href="/editor/level", data-i18n="editor.level_title") a.spl(href="/editor/level", data-i18n="editor.level_title")
p p
span(data-i18n="community.level_editor_prefix") Use the CodeCombat span.spr(data-i18n="community.level_editor_prefix") Use the CodeCombat
a.spl.spr(href="/editor/level", data-i18n="editor.level_title") a(href="/editor/level", data-i18n="editor.level_title")
span(data-i18n="community.level_editor_suffix") to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours! span.spl(data-i18n="community.level_editor_suffix") to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!
.community-columns .community-columns
a(href="/editor/thang") a(href="/editor/thang")
@ -24,9 +24,9 @@ block content
h2 h2
a.spl(href="/editor/thang", data-i18n="editor.thang_title") a.spl(href="/editor/thang", data-i18n="editor.thang_title")
p p
span(data-i18n="community.thang_editor_prefix") We call units within the game 'thangs'. Use the span.spr(data-i18n="community.thang_editor_prefix") We call units within the game 'thangs'. Use the
a.spl.spr(href="/editor/thang", data-i18n="editor.thang_title") a(href="/editor/thang", data-i18n="editor.thang_title")
span(data-i18n="community.thang_editor_suffix") to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites. span.spl(data-i18n="community.thang_editor_suffix") to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites.
.community-columns .community-columns
a(href="/editor/article") a(href="/editor/article")
@ -34,9 +34,9 @@ block content
h2 h2
a.spl(href="/editor/article", data-i18n="editor.article_title") a.spl(href="/editor/article", data-i18n="editor.article_title")
p p
span(data-i18n="community.article_editor_prefix") See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the span.spr(data-i18n="community.article_editor_prefix") See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the
a.spl.spr(href="/editor/article", data-i18n="editor.article_title") a(href="/editor/article", data-i18n="editor.article_title")
span(data-i18n="community.article_editor_suffix") and help CodeCombat players get the most out of their playtime. span.spl(data-i18n="community.article_editor_suffix") and help CodeCombat players get the most out of their playtime.
div div

View file

@ -20,6 +20,9 @@
if showRequiredError if showRequiredError
.alert.alert-success .alert.alert-success
span(data-i18n="signup.required") You need to log in before you can that way. span(data-i18n="signup.required") You need to log in before you can that way.
else if mode === 'signup' && showSignupRationale
.alert.alert-info
span(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account!
form.form form.form
.form-group .form-group

View file

@ -16,9 +16,9 @@ block modal-body-content
p(data-i18n="contact.subscriber_support") Since you're a CodeCombat subscriber, your email will get our priority support. p(data-i18n="contact.subscriber_support") Since you're a CodeCombat subscriber, your email will get our priority support.
else else
p p
span(data-i18n="contact.subscribe_prefix") If you need help figuring out a level, please span.spr(data-i18n="contact.subscribe_prefix") If you need help figuring out a level, please
a.spl.spr(data-toggle="coco-modal", data-target="core/SubscribeModal", data-i18n="contact.subscribe") buy a CodeCombat subscription a(data-toggle="coco-modal", data-target="core/SubscribeModal", data-i18n="contact.subscribe") buy a CodeCombat subscription
span(data-i18n="contact.subscribe_suffix") and we'll be happy to help you with your code. span.spl(data-i18n="contact.subscribe_suffix") and we'll be happy to help you with your code.
.form .form
.form-group .form-group
label.control-label(for="contact-email", data-i18n="general.email") Email label.control-label(for="contact-email", data-i18n="general.email") Email

View file

@ -71,6 +71,7 @@
td.center-ok td.center-ok
span.glyphicon.glyphicon-ok span.glyphicon.glyphicon-ok
#parents-info(data-i18n="subscribe.parents") #parents-info(data-i18n="subscribe.parents")
#payment-methods-info(data-i18n="subscribe.payment_methods")
button.btn.btn-lg.btn-illustrated.purchase-button(data-i18n="subscribe.subscribe_title") button.btn.btn-lg.btn-illustrated.purchase-button(data-i18n="subscribe.subscribe_title")
button.btn.btn-lg.btn-illustrated.parent-button(data-i18n="subscribe.parent_button") button.btn.btn-lg.btn-illustrated.parent-button(data-i18n="subscribe.parent_button")

View file

@ -15,8 +15,8 @@ if campaign
if level.unlocksHero && (!level.purchasedHero || editorMode) if level.unlocksHero && (!level.purchasedHero || editorMode)
img.hero-portrait(src="/file/db/thang.type/#{level.unlocksHero}/portrait.png") img.hero-portrait(src="/file/db/thang.type/#{level.unlocksHero}/portrait.png")
a(href=level.type == 'hero' ? '#' : level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.slug}", disabled=level.disabled, data-level-slug=level.slug, data-level-path=level.levelPath || 'level', data-level-name=level.name) a(href=level.type == 'hero' ? '#' : level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.slug}", disabled=level.disabled, data-level-slug=level.slug, data-level-path=level.levelPath || 'level', data-level-name=level.name)
if level.slug == 'apocalypse' if level.slug == 'lost-viking'
img.star(src="/file/db/thang.type/54ea89112b7506e891ca717d/portrait.png") img.star(src="/file/db/thang.type/5441c3144e9aeb727cc97111/portrait.png")
else if level.requiresSubscription else if level.requiresSubscription
img.star(src="/images/pages/play/star.png") img.star(src="/images/pages/play/star.png")
if levelStatusMap[level.slug] === 'complete' if levelStatusMap[level.slug] === 'complete'
@ -79,7 +79,7 @@ else
else if campaign else if campaign
btn(data-i18n="common.play").btn.btn-illustrated.btn-lg.btn-success.play-button btn(data-i18n="common.play").btn.btn-illustrated.btn-lg.btn-success.play-button
if campaign && campaign.get('description') if campaign && campaign.get('description')
h3.campaign-description p.campaign-description
span= i18n(campaign.attributes, 'description') span= i18n(campaign.attributes, 'description')
.game-controls.header-font .game-controls.header-font

View file

@ -84,8 +84,7 @@ block content
| . | .
p p
strong Tournament ended! strong Tournament ended!
//a(href="#winners") Behold the winners a(href="#winners") Behold the winners
span We will announce winners soon
| . Thanks for playing! You can | . Thanks for playing! You can
strong still play strong still play
| Zero Sum as long as you like. | Zero Sum as long as you like.
@ -122,7 +121,7 @@ block content
if level.get('name') == 'Greed' if level.get('name') == 'Greed'
li li
a(href="#rules", data-toggle="tab", data-i18n="ladder.rules") Rules a(href="#rules", data-toggle="tab", data-i18n="ladder.rules") Rules
if level.get('name') == 'Greed' || (level.get('name') == 'Criss-Cross') if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum'
li li
a(href="#winners", data-toggle="tab", data-i18n="ladder.winners") Winners a(href="#winners", data-toggle="tab", data-i18n="ladder.winners") Winners
@ -761,7 +760,7 @@ block content
a(href="http://discourse.codecombat.com/") Discourse forum a(href="http://discourse.codecombat.com/") Discourse forum
| . | .
if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum'
.tab-pane.well#winners .tab-pane.well#winners
h1(data-i18n="ladder.winners") Winners h1(data-i18n="ladder.winners") Winners
@ -769,17 +768,19 @@ block content
thead thead
tr tr
th(data-i18n="ladder_prizes.rank") Rank th(data-i18n="ladder_prizes.rank") Rank
if level.get('name') == 'Criss-Cross' if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum'
th th
th Human th Human
if level.get('name') == 'Greed' if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum'
th Human wins/losses/ties th Human wins/losses/ties
else else
th Human score th Human score
if level.get('name') == 'Criss-Cross' if level.get('name') == 'Zero Sum'
th
if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum'
th th
th Ogre th Ogre
if level.get('name') == 'Greed' if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum'
th Ogre wins/losses/ties th Ogre wins/losses/ties
else else
th Ogre score th Ogre score
@ -789,30 +790,38 @@ block content
- var ogre = winners.ogres[index] - var ogre = winners.ogres[index]
tr tr
td= human.rank td= human.rank
if level.get('name') == 'Criss-Cross' if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum'
td.code-language-cell(style="background-image: url(/images/common/code_languages/" + human.codeLanguage + "_icon.png)" title=_.string.capitalize(human.codeLanguage)) td.code-language-cell(style="background-image: url(/images/common/code_languages/" + human.codeLanguage + "_icon.png)" title=_.string.capitalize(human.codeLanguage))
td= human.name td= human.name
if level.get('name') == 'Greed' if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum'
td td
span.win= human.wins span.win= human.wins
| - | -
span.loss= human.losses span.loss= human.losses
| - | -
span.tie= 377 - human.wins - human.losses if level.get('name') == 'Greed'
span.tie= 377 - human.wins - human.losses
else if level.get('name') == 'Zero Sum'
span.tie= 108 - human.wins - human.losses
else else
td td
span= Math.round(100 * human.score) span= Math.round(100 * human.score)
if ogre if ogre
if level.get('name') == 'Criss-Cross' if level.get('name') == 'Zero Sum'
td= ogre.rank
if level.get('name') == 'Criss-Cross' || level.get('name') == 'Zero Sum'
td.code-language-cell(style="background-image: url(/images/common/code_languages/" + ogre.codeLanguage + "_icon.png)" title=_.string.capitalize(ogre.codeLanguage)) td.code-language-cell(style="background-image: url(/images/common/code_languages/" + ogre.codeLanguage + "_icon.png)" title=_.string.capitalize(ogre.codeLanguage))
td= ogre.name td= ogre.name
if level.get('name') == 'Greed' if level.get('name') == 'Greed' || level.get('name') == 'Zero Sum'
td td
span.win= ogre.wins span.win= ogre.wins
| - | -
span.loss= ogre.losses span.loss= ogre.losses
| - | -
span.tie= 407 - ogre.wins - ogre.losses if level.get('name') == 'Greed'
span.tie= 407 - ogre.wins - ogre.losses
else if level.get('name') == 'Zero Sum'
span.tie= Math.max(0, 163 - ogre.wins - ogre.losses)
else else
td td
span= Math.round(100 * ogre.score) span= Math.round(100 * ogre.score)

View file

@ -89,5 +89,12 @@ block modal-footer-content
img(src="/images/level/csedweek-logo-final-small.jpg", alt="CS Ed Week Hour of Code", title="I'm finished with my Hour of Code", width=80) img(src="/images/level/csedweek-logo-final-small.jpg", alt="CS Ed Week Hour of Code", title="I'm finished with my Hour of Code", width=80)
strong(data-i18n="play_level.victory_hour_of_code_done") Are You Done? strong(data-i18n="play_level.victory_hour_of_code_done") Are You Done?
a.text-link(href="http://code.org/api/hour/finish") a.text-link(href="http://code.org/api/hour/finish")
span(data-i18n="play_level.victory_hour_of_code_done_yes") Yes, I'm finished with my Hour of Code! span(data-i18n="play_level.victory_hour_of_code_done_yes") Yes, I am finished with my Hour of Code!
.clearfix .clearfix
.offer.lost-viking
p
img.pull-left(src="/file/db/level/55144b509f0c4854051769c1/viking1.png")
img.pull-right(src="/file/db/level/55144b509f0c4854051769c1/viking_2.png")
| Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks.
button.btn.btn-illustrated.btn-primary.btn-lg.world-map-button.continue-from-offer-button(data-i18n="play_level.victory_become_a_viking") Become a Viking

View file

@ -14,6 +14,14 @@ if docs.length === 1
hr hr
h3 Want more programming lessons? h3 Want more programming lessons?
ul ul
li
strong
a(class="resource-link", data-resource="breakout-mentors", href='http://breakoutmentors.com/?referral=codecombat') Breakout Mentors
| : Personalized code mentoring for kids from Stanford and UC Berkeley mentors, online or in person.
li
strong
a(class="resource-link", data-resource="ostraining", href='https://www.ostraining.com/codecombat/') OSTraining
| : Watch over 2600 videos on how to make great websites with Wordpress, Drupal, Joomla, and more.
li li
strong strong
a(class="resource-link", data-resource="one-month", href='http://mbsy.co/bVRtZ') One Month a(class="resource-link", data-resource="one-month", href='http://mbsy.co/bVRtZ') One Month

View file

@ -25,7 +25,10 @@ if topScores
td.ago-cell= row.ago td.ago-cell= row.ago
td.viewable-cell td.viewable-cell
if viewable if viewable
.glyphicon.glyphicon-eye-open if (me.get('preferredLanguage', true) || 'en-US').substr(0, 2) == 'en'
.btn.btn-xs.btn-info Watch
else
.glyphicon.glyphicon-eye-open
else else
.glyphicon.glyphicon-eye-close .glyphicon.glyphicon-eye-close
else if loading else if loading

View file

@ -6,16 +6,6 @@
.blurb-container .blurb-container
h1(data-i18n="share_progress_modal.title") h1(data-i18n="share_progress_modal.title")
p(data-i18n="share_progress_modal.blurb") p(data-i18n="share_progress_modal.blurb")
.container-fluid.btn-picker-container
.row
.col-xs-12.text-center
button.btn.btn-illustrated.tell-parent-btn(data-i18n="share_progress_modal.tell_parent")
.row
.col-xs-12.text-center
button.btn.btn-illustrated.tell-friend-btn(data-i18n="share_progress_modal.tell_friend")
.row.continue-container
.col-xs-12.text-right
a.continue-link(data-i18n="common.continue")
.container-fluid.send-container .container-fluid.send-container
.row .row
@ -30,7 +20,5 @@
.col-xs-4.text-right .col-xs-4.text-right
button.btn.btn-illustrated.send-btn(data-i18n="common.send") button.btn.btn-illustrated.send-btn(data-i18n="common.send")
.row.continue-container .row.continue-container
.col-xs-6 .col-xs-12.text-left
a.back-link(data-i18n="common.back")
.col-xs-6.text-right
a.continue-link(data-i18n="common.continue") a.continue-link(data-i18n="common.continue")

View file

@ -13,7 +13,7 @@ module.exports = class CommunityView extends RootView
titleDescription = $.i18n.t("classes.#{characterClass}_title_description") titleDescription = $.i18n.t("classes.#{characterClass}_title_description")
summary = $.i18n.t("classes.#{characterClass}_summary") summary = $.i18n.t("classes.#{characterClass}_summary")
explanation = "<h4>#{title} #{titleDescription}</h4>#{summary}" explanation = "<h4>#{title} #{titleDescription}</h4>#{summary}"
$(@).find('img').popover(placement: 'bottom', trigger: 'hover', container: 'body', content: explanation, html: true) $(@).find('img').popover(placement: 'top', trigger: 'hover', container: 'body', content: explanation, html: true)
@$el.find('.logo-row img').each -> @$el.find('.logo-row img').each ->
$(@).popover(placement: 'bottom', trigger: 'hover', container: 'body') $(@).popover(placement: 'top', trigger: 'hover', container: 'body')

View file

@ -1,9 +1,8 @@
RootView = require 'views/core/RootView' RootView = require 'views/core/RootView'
template = require 'templates/admin/analytics-subscriptions' template = require 'templates/admin/analytics-subscriptions'
RealTimeCollection = require 'collections/RealTimeCollection' ThangType = require 'models/ThangType'
User = require 'models/User'
# TODO: Add last N subscribers table
# TODO: Add revenue line
# TODO: Graphing code copied/mangled from campaign editor level view. OMG, DRY. # TODO: Graphing code copied/mangled from campaign editor level view. OMG, DRY.
require 'vendor/d3' require 'vendor/d3'
@ -11,10 +10,11 @@ require 'vendor/d3'
module.exports = class AnalyticsSubscriptionsView extends RootView module.exports = class AnalyticsSubscriptionsView extends RootView
id: 'admin-analytics-subscriptions-view' id: 'admin-analytics-subscriptions-view'
template: template template: template
targetSubCount: 2000
constructor: (options) -> constructor: (options) ->
super options super options
@resetData() @resetSubscriptionsData()
if me.isAdmin() if me.isAdmin()
@refreshData() @refreshData()
_.delay (=> @refreshData()), 30 * 60 * 1000 _.delay (=> @refreshData()), 30 * 60 * 1000
@ -23,6 +23,8 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
context = super() context = super()
context.analytics = @analytics ? graphs: [] context.analytics = @analytics ? graphs: []
context.subs = _.cloneDeep(@subs ? []).reverse() context.subs = _.cloneDeep(@subs ? []).reverse()
context.subscribers = @subscribers ? []
context.subscriberCancelled = _.find context.subscribers, (subscriber) -> subscriber.cancel
context.total = @total ? 0 context.total = @total ? 0
context.cancelled = @cancelled ? 0 context.cancelled = @cancelled ? 0
context.monthlyChurn = @monthlyChurn ? 0.0 context.monthlyChurn = @monthlyChurn ? 0.0
@ -33,17 +35,39 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
super() super()
@updateAnalyticsGraphs() @updateAnalyticsGraphs()
resetData: -> resetSubscriptionsData: ->
@analytics = graphs: [] @analytics = graphs: []
@subs = [] @subs = []
@total = 0 @total = 0
@cancelled = 0 @cancelled = 0
@monthlyChurn = 0.0 @monthlyChurn = 0.0
@monthlyGrowth = 0.0
refreshData: -> refreshData: ->
return unless me.isAdmin() return unless me.isAdmin()
@resetData() @resetSubscriptionsData()
@getSubscribers()
@getSubscriptions()
getSubscribers: ->
options =
url: '/db/subscription/-/subscribers'
method: 'POST'
data: {maxCount: 30}
options.error = (model, response, options) =>
return if @destroyed
console.error 'Failed to get subscribers', response
options.success = (subscribers, response, options) =>
return if @destroyed
@subscribers = subscribers
for subscriber in @subscribers
subscriber.level = User.levelFromExp subscriber.user.points
if hero = subscriber.user.heroConfig?.thangType
subscriber.hero = _.invert(ThangType.heroes)[hero]
@render?()
@supermodel.addRequestResource('get_subscribers', options, 0).load()
getSubscriptions: ->
options = options =
url: '/db/subscription/-/subscriptions' url: '/db/subscription/-/subscriptions'
method: 'GET' method: 'GET'
@ -52,7 +76,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
console.error 'Failed to get subscriptions', response console.error 'Failed to get subscriptions', response
options.success = (subs, response, options) => options.success = (subs, response, options) =>
return if @destroyed return if @destroyed
@resetData() @resetSubscriptionsData()
subDayMap = {} subDayMap = {}
for sub in subs for sub in subs
startDay = sub.start.substring(0, 10) startDay = sub.start.substring(0, 10)
@ -66,7 +90,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
for day of subDayMap for day of subDayMap
@subs.push @subs.push
day: day day: day
started: subDayMap[day]['start'] started: subDayMap[day]['start'] or 0
cancelled: subDayMap[day]['cancel'] or 0 cancelled: subDayMap[day]['cancel'] or 0
@subs.sort (a, b) -> a.day.localeCompare(b.day) @subs.sort (a, b) -> a.day.localeCompare(b.day)
@ -78,9 +102,9 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
startedLastMonth += sub.started if @subs.length - i < 31 startedLastMonth += sub.started if @subs.length - i < 31
@monthlyChurn = @cancelled / startedLastMonth * 100.0 if startedLastMonth > 0 @monthlyChurn = @cancelled / startedLastMonth * 100.0 if startedLastMonth > 0
if @subs.length > 30 and @subs[@subs.length - 31].total > 0 if @subs.length > 30 and @subs[@subs.length - 31].total > 0
lastMonthTotal = @subs[@subs.length - 31].total startMonthTotal = @subs[@subs.length - 31].total
thisMonthTotal = @subs[@subs.length - 1].total endMonthTotal = @subs[@subs.length - 1].total
@monthlyGrowth = (thisMonthTotal - lastMonthTotal) / lastMonthTotal * 100 @monthlyGrowth = (endMonthTotal / startMonthTotal - 1) * 100
@updateAnalyticsGraphData() @updateAnalyticsGraphData()
@render?() @render?()
@supermodel.addRequestResource('get_subscriptions', options, 0).load() @supermodel.addRequestResource('get_subscriptions', options, 0).load()
@ -98,6 +122,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
# TODO: Where should this metadata live? # TODO: Where should this metadata live?
# TODO: lineIDs assumed to be unique across graphs # TODO: lineIDs assumed to be unique across graphs
totalSubsID = 'total-subs' totalSubsID = 'total-subs'
targetSubsID = 'target-subs'
startedSubsID = 'started-subs' startedSubsID = 'started-subs'
cancelledSubsID = 'cancelled-subs' cancelledSubsID = 'cancelled-subs'
netSubsID = 'net-subs' netSubsID = 'net-subs'
@ -106,6 +131,11 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
description: 'Total Active Subscriptions' description: 'Total Active Subscriptions'
color: 'green' color: 'green'
strokeWidth: 1 strokeWidth: 1
lineMetadata[targetSubsID] =
description: 'Target Total Subscriptions'
color: 'gold'
strokeWidth: 4
opacity: 1.0
lineMetadata[startedSubsID] = lineMetadata[startedSubsID] =
description: 'New Subscriptions' description: 'New Subscriptions'
color: 'blue' color: 'blue'
@ -162,8 +192,9 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
points: levelPoints points: levelPoints
description: lineMetadata[totalSubsID].description description: lineMetadata[totalSubsID].description
lineColor: lineMetadata[totalSubsID].color lineColor: lineMetadata[totalSubsID].color
strokeWidth: lineMetadata[totalSubsID].strokeWidth
min: 0 min: 0
max: d3.max(@subs, (d) -> d.total) max: Math.max(@targetSubCount, d3.max(@subs, (d) -> d.total))
## Started ## Started
@ -196,9 +227,34 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
points: levelPoints points: levelPoints
description: lineMetadata[startedSubsID].description description: lineMetadata[startedSubsID].description
lineColor: lineMetadata[startedSubsID].color lineColor: lineMetadata[startedSubsID].color
strokeWidth: lineMetadata[startedSubsID].strokeWidth
min: 0 min: 0
max: d3.max(@subs, (d) -> d.started) max: d3.max(@subs, (d) -> d.started)
## Total subs target
# Build line data
levelPoints = []
for sub, i in @subs
levelPoints.push
x: i
y: @targetSubCount
day: sub.day
pointID: "#{targetSubsID}#{i}"
values: []
levelPoints.splice(0, levelPoints.length - timeframeDays) if levelPoints.length > timeframeDays
@analytics.graphs[0].lines.push
lineID: targetSubsID
enabled: true
points: levelPoints
description: lineMetadata[targetSubsID].description
lineColor: lineMetadata[targetSubsID].color
strokeWidth: lineMetadata[targetSubsID].strokeWidth
min: 0
max: @targetSubCount
## Cancelled ## Cancelled
averageCancelled = 0 averageCancelled = 0
@ -235,6 +291,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
points: levelPoints points: levelPoints
description: lineMetadata[cancelledSubsID].description description: lineMetadata[cancelledSubsID].description
lineColor: lineMetadata[cancelledSubsID].color lineColor: lineMetadata[cancelledSubsID].color
strokeWidth: lineMetadata[cancelledSubsID].strokeWidth
min: 0 min: 0
max: d3.max(@subs, (d) -> d.started) max: d3.max(@subs, (d) -> d.started)
@ -310,7 +367,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
xRange = d3.scale.linear().range([0, width]).domain([d3.min(line.points, (d) -> d.x), d3.max(line.points, (d) -> d.x)]) xRange = d3.scale.linear().range([0, width]).domain([d3.min(line.points, (d) -> d.x), d3.max(line.points, (d) -> d.x)])
yRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max]) yRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max])
# x-Axis and guideline once # x-Axis
if currentLine is 0 if currentLine is 0
startDay = new Date(line.points[0].day) startDay = new Date(line.points[0].day)
endDay = new Date(line.points[line.points.length - 1].day) endDay = new Date(line.points[line.points.length - 1].day)
@ -369,7 +426,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
svg.append("text") svg.append("text")
.attr("x", margin + 40 + 10) .attr("x", margin + 40 + 10)
.attr("y", margin + height + xAxisHeight + keyHeight * currentLine + (keyHeight + 10) / 2) .attr("y", margin + height + xAxisHeight + keyHeight * currentLine + (keyHeight + 10) / 2)
.attr("fill", line.lineColor) .attr("fill", if line.lineColor is 'gold' then 'orange' else line.lineColor)
.attr("class", "key-text") .attr("class", "key-text")
.text(line.description) .text(line.description)

View file

@ -33,6 +33,7 @@ module.exports = class AuthModal extends ModalView
getRenderData: -> getRenderData: ->
c = super() c = super()
c.showRequiredError = @options.showRequiredError c.showRequiredError = @options.showRequiredError
c.showSignupRationale = @options.showSignupRationale
c.mode = @mode c.mode = @mode
c.formValues = @previousFormInputs or {} c.formValues = @previousFormInputs or {}
c.me = me c.me = me

View file

@ -37,6 +37,7 @@ module.exports = class SubscribeModal extends ModalView
super() super()
@setupParentButtonPopover() @setupParentButtonPopover()
@setupParentInfoPopover() @setupParentInfoPopover()
@setupPaymentMethodsInfoPopover()
setupParentButtonPopover: -> setupParentButtonPopover: ->
popoverTitle = $.i18n.t 'subscribe.parent_email_title' popoverTitle = $.i18n.t 'subscribe.parent_email_title'
@ -85,6 +86,21 @@ module.exports = class SubscribeModal extends ModalView
).on 'shown.bs.popover', => ).on 'shown.bs.popover', =>
application.tracker?.trackEvent 'Subscription parent hover' application.tracker?.trackEvent 'Subscription parent hover'
setupPaymentMethodsInfoPopover: ->
popoverTitle = $.i18n.t('subscribe.payment_methods_title')
popoverContent = "<p>" + $.i18n.t('subscribe.payment_methods_blurb1') + "</p>"
popoverContent += "<p>" + $.i18n.t('subscribe.payment_methods_blurb2') + " <a href='mailto:support@codecombat.com'>support@codecombat.com</a>."
@$el.find('#payment-methods-info').popover(
animation: true
html: true
placement: 'top'
trigger: 'click'
title: popoverTitle
content: popoverContent
container: @$el
).on 'shown.bs.popover', =>
application.tracker?.trackEvent 'Subscription payment methods hover'
onClickParentSendButton: (e) -> onClickParentSendButton: (e) ->
# TODO: Popover sometimes dismisses immediately after send # TODO: Popover sometimes dismisses immediately after send

View file

@ -27,7 +27,7 @@ module.exports = class I18NEditLevelView extends I18NEditModelView
for doc, index in @model.get('documentation')?.specificArticles ? [] for doc, index in @model.get('documentation')?.specificArticles ? []
if i18n = doc.i18n if i18n = doc.i18n
@wrapRow 'Guide article name', ['name'], doc.name, i18n[lang]?.name, ['documentation', 'specificArticles', index] @wrapRow 'Guide article name', ['name'], doc.name, i18n[lang]?.name, ['documentation', 'specificArticles', index]
@wrapRow "'#{doc.name}' description", ['description'], doc.description, i18n[lang]?.description, ['documentation', 'specificArticles', index], 'markdown' @wrapRow "'#{doc.name}' body", ['body'], doc.body, i18n[lang]?.body, ['documentation', 'specificArticles', index], 'markdown'
# sprite dialogues # sprite dialogues
for script, scriptIndex in @model.get('scripts') ? [] for script, scriptIndex in @model.get('scripts') ? []

View file

@ -110,6 +110,8 @@ module.exports = class LadderView extends RootView
@$el.find('a[href="#rules"]').tab('show') @$el.find('a[href="#rules"]').tab('show')
if link and /#prizes/.test link if link and /#prizes/.test link
@$el.find('a[href="#prizes"]').tab('show') @$el.find('a[href="#prizes"]').tab('show')
if link and /#winners/.test link
@$el.find('a[href="#winners"]').tab('show')
destroy: -> destroy: ->
clearInterval @refreshInterval clearInterval @refreshInterval

View file

@ -48,7 +48,7 @@ module.exports = class SimulateTabView extends CocoView
@startSimulating() @startSimulating()
startSimulating: -> startSimulating: ->
@simulationPageRefreshTimeout = _.delay @refreshAndContinueSimulating, 20 * 60 * 1000 @simulationPageRefreshTimeout = _.delay @refreshAndContinueSimulating, 30 * 60 * 1000
@simulateNextGame() @simulateNextGame()
$('#simulate-button').prop 'disabled', true $('#simulate-button').prop 'disabled', true
$('#simulate-button').text 'Simulating...' $('#simulate-button').text 'Simulating...'
@ -65,7 +65,7 @@ module.exports = class SimulateTabView extends CocoView
# Work around simulator getting super slow on Chrome # Work around simulator getting super slow on Chrome
fetchAndSimulateTaskOriginal = @simulator.fetchAndSimulateTask fetchAndSimulateTaskOriginal = @simulator.fetchAndSimulateTask
@simulator.fetchAndSimulateTask = => @simulator.fetchAndSimulateTask = =>
if @simulator.simulatedByYou >= 5 if @simulator.simulatedByYou >= 20
console.log '------------------- Destroying Simulator and making a new one -----------------' console.log '------------------- Destroying Simulator and making a new one -----------------'
@simulator.destroy() @simulator.destroy()
@simulator = null @simulator = null

View file

@ -1,4 +1,4 @@
module.exports = results = greed: {}, 'criss-cross': {} module.exports = results = greed: {}, 'criss-cross': {}, 'zero-sum': {}
results.greed.humans = [ results.greed.humans = [
{team: 'humans', rank: 1, sessionID: '5381e3537585483905a829c1', name: 'Wizard Dude', playtime: 63184, wins: 363, losses: 0, score: 363} {team: 'humans', rank: 1, sessionID: '5381e3537585483905a829c1', name: 'Wizard Dude', playtime: 63184, wins: 363, losses: 0, score: 363}
@ -746,3 +746,205 @@ results['criss-cross'].ogres = [
{team: 'ogres', rank: 72, sessionID: '53f4847afda22e3305bdbea5', codeLanguage: 'javascript', name: 'CodeMinion', score: 9.706964015} {team: 'ogres', rank: 72, sessionID: '53f4847afda22e3305bdbea5', codeLanguage: 'javascript', name: 'CodeMinion', score: 9.706964015}
{team: 'ogres', rank: 73, sessionID: '53fba49c3baef834054b98c6', codeLanguage: 'javascript', name: 'Moises Banales', score: 8.991630973} {team: 'ogres', rank: 73, sessionID: '53fba49c3baef834054b98c6', codeLanguage: 'javascript', name: 'Moises Banales', score: 8.991630973}
] ]
results['zero-sum'].humans = [
{team: 'humans', rank: 1, userID: '539b571db4c4f9380545317d', sessionID: '55188bcee3ae3a3505f7d1ad', name: 'NoJuice4u', wins: 105, losses: 3, playtime: 46222, codeLanguage: 'javascript'}
{team: 'humans', rank: 2, userID: '54ef65eaef838a790598b305', sessionID: '5518349d1f12482609b452ea', name: 'Qenf', wins: 99, losses: 9, playtime: 13554, codeLanguage: 'python'}
{team: 'humans', rank: 3, userID: '5308bb6cc326dda10cf62d6d', sessionID: '55172d95e3ae3a3505f7a6ed', name: 'Stofflon', wins: 95, losses: 13, playtime: 22609, codeLanguage: 'javascript'}
{team: 'humans', rank: 3, userID: '539bccfb6e1d92310506ffa9', sessionID: '5518d8624c73053505254062', name: 'Driphter', wins: 95, losses: 13, playtime: 32555, codeLanguage: 'javascript'}
{team: 'humans', rank: 5, userID: '54d2c6ac3e16915505f0cef3', sessionID: '551f2d159800ae33059c5052', name: 'KingCode', wins: 93, losses: 15, playtime: 13510, codeLanguage: 'javascript'}
{team: 'humans', rank: 5, userID: '549ee5208f3c153d0518ff24', sessionID: '55195098d69ccd3305a2ea0c', name: 'wabu', wins: 93, losses: 15, playtime: 3274, codeLanguage: 'python'}
{team: 'humans', rank: 5, userID: '5500505220b2567d0514bab9', sessionID: '551582ecf43d375105fbdf00', name: 'Muadibi', wins: 93, losses: 15, playtime: 30381, codeLanguage: 'javascript'}
{team: 'humans', rank: 5, userID: '54f015a0df1b7d7d05610870', sessionID: '55172f2ec13fa03205248953', name: 'Andre95', wins: 93, losses: 15, playtime: 53158, codeLanguage: 'python'}
{team: 'humans', rank: 9, userID: '54ff548501c724b9072f9f6d', sessionID: '551bd2f91794dc1a112bfc28', name: 'mAsTer_A', wins: 91, losses: 16, playtime: 11860, codeLanguage: 'javascript'}
{team: 'humans', rank: 10, userID: '539c13ba6e1d923105072c32', sessionID: '5515a210d792f354050c9339', name: 'rasdasd', wins: 89, losses: 19, playtime: 12710, codeLanguage: 'javascript'}
{team: 'humans', rank: 11, userID: '5516520db77a16330597d9a3', sessionID: '55165233de8bc03205975972', name: 'SlamBlasta', wins: 88, losses: 20, playtime: 10063, codeLanguage: 'python'}
{team: 'humans', rank: 12, userID: '512ef4805a67a8c507000001', sessionID: '550370aeec31df9c691ab632', name: 'Nick', wins: 87, losses: 20, playtime: 22856, codeLanguage: 'python'}
{team: 'humans', rank: 13, userID: '55164512de8bc032059758dc', sessionID: '55184ea8e3ae3a3505f7c811', name: 'Geoboardman', wins: 87, losses: 21, playtime: 11451, codeLanguage: 'python'}
{team: 'humans', rank: 13, userID: '551750ca81389437054def79', sessionID: '5517545fe3ae3a3505f7ac1f', name: 'the real Carl Maxwell', wins: 87, losses: 21, playtime: 6242, codeLanguage: 'python'}
{team: 'humans', rank: 15, userID: '537d01f484c54c6e05c05989', sessionID: '55164e9f4e83223605e7942a', name: 'Serg', wins: 85, losses: 23, playtime: 8176, codeLanguage: 'javascript'}
{team: 'humans', rank: 16, userID: '5478a26cc439b9160865b3b3', sessionID: '550ceb075834237a05acda6e', name: 'Tutor Delphia', wins: 83, losses: 25, playtime: 30978, codeLanguage: 'javascript'}
{team: 'humans', rank: 17, userID: '551d78734c73053505267238', sessionID: '551dc7046cc7da101fe5fe52', name: 'Kongou', wins: 79, losses: 29, playtime: 561, codeLanguage: 'python'}
{team: 'humans', rank: 18, userID: '53f4cb0cfda22e3305be24e7', sessionID: '5515ecc4b8185a5205e1a304', name: 'Dothgar', wins: 74, losses: 34, playtime: 4129, codeLanguage: 'javascript'}
{team: 'humans', rank: 19, userID: '5520a4caabdb444805e85da1', sessionID: '5522f7ae801bea3905cb6fd9', name: 'Bazhan', wins: 73, losses: 34, playtime: 4466, codeLanguage: 'python'}
{team: 'humans', rank: 20, userID: '5506e39e84ce5b7905627e41', sessionID: '551bed505afdf532055862e4', name: 'pixx', wins: 73, losses: 35, playtime: 12182, codeLanguage: 'javascript'}
{team: 'humans', rank: 21, userID: '5516aa174d24212f05944d19', sessionID: '5517065858852c3405f48e9d', name: 'Haruna', wins: 70, losses: 38, playtime: 19377, codeLanguage: 'javascript'}
{team: 'humans', rank: 22, userID: '54a800d3a487d53f056c0bb7', sessionID: '5519bac15cd6bae40951f460', name: 'Mensian', wins: 69, losses: 39, playtime: 17817, codeLanguage: 'javascript'}
{team: 'humans', rank: 23, userID: '5519d8885afdf5320557df5c', sessionID: '5519d88e8328b9020debf019', name: 'StabGun', wins: 69, losses: 39, playtime: 3710, codeLanguage: 'python'}
{team: 'humans', rank: 23, userID: '55187c2c5afdf532055782df', sessionID: '55187c63d69ccd3305a2c33b', name: 'shakesoda', wins: 69, losses: 39, playtime: 19789, codeLanguage: 'lua'}
{team: 'humans', rank: 25, userID: '5429a6d19f9fa2bf2c5a4e4e', sessionID: '55173873d69ccd3305a2918c', name: 'FadingSun32', wins: 65, losses: 43, playtime: 113, codeLanguage: 'javascript'}
{team: 'humans', rank: 25, userID: '54f9e344d12cf54d069d92dc', sessionID: '550d0ba29e22dc56056be1dd', name: 'xianjin', wins: 65, losses: 43, playtime: 693, codeLanguage: 'python'}
{team: 'humans', rank: 27, userID: '54fd36429ae7221e06728f3b', sessionID: '550d001c9e22dc56056be0d8', name: 'Jasmine49', wins: 59, losses: 49, playtime: 5222, codeLanguage: 'python'}
{team: 'humans', rank: 27, userID: '551b205cc13fa03205254f3e', sessionID: '551c04088328b9020dec9ef1', name: 'my98olds', wins: 59, losses: 49, playtime: 22943, codeLanguage: 'javascript'}
{team: 'humans', rank: 29, userID: '54bbb1016ef7bdde07101c4e', sessionID: '551807d0c13fa03205249b3d', name: 'PSL', wins: 57, losses: 51, playtime: 2523, codeLanguage: 'javascript'}
{team: 'humans', rank: 30, userID: '539ea2ba64edb6fa0bf584f7', sessionID: '5516a002b77a16330597e208', name: 'MarS explorer', wins: 55, losses: 53, playtime: 5966, codeLanguage: 'lua'}
{team: 'humans', rank: 30, userID: '548e1d2d24446d3d050281ed', sessionID: '55080c49039bafd50cb92fae', name: 'J_F_B_M', wins: 55, losses: 53, playtime: 7951, codeLanguage: 'javascript'}
{team: 'humans', rank: 32, userID: '5489342042443b56076a1ad8', sessionID: '55173465e3ae3a3505f7a7ff', name: 'AnnieHall', wins: 54, losses: 52, playtime: 5576, codeLanguage: 'python'}
{team: 'humans', rank: 33, userID: '54f1ddf248724e7d052b75fd', sessionID: '551a7f0e5cd6bae409522148', name: 'TiboW', wins: 54, losses: 54, playtime: 942, codeLanguage: 'python'}
{team: 'humans', rank: 34, userID: '55169c96aedec33205e8acfc', sessionID: '55169df0aedec33205e8ad47', name: 'funny_falcon', wins: 51, losses: 57, playtime: 4486, codeLanguage: 'python'}
{team: 'humans', rank: 35, userID: '54feee1ae950f7a707ec3461', sessionID: '5515d0933bdee5954e8246df', name: 'Khorrok', wins: 50, losses: 57, playtime: 387, codeLanguage: 'python'}
{team: 'humans', rank: 36, userID: '54f7e04e8ec032e705abbcd8', sessionID: '550cc8eba605ba5a05120514', name: 'Tan Chuan Jie', wins: 50, losses: 58, playtime: 4787, codeLanguage: 'python'}
{team: 'humans', rank: 36, userID: '551743df079f2833053c723a', sessionID: '5517582bd69ccd3305a29676', name: 'Majestik', wins: 50, losses: 58, playtime: 15118, codeLanguage: 'javascript'}
{team: 'humans', rank: 38, userID: '55005eb295a4228105f6bfc3', sessionID: '550d0e699e22dc56056be203', name: '2SHAWN4YOU', wins: 49, losses: 59, playtime: 935, codeLanguage: 'python'}
{team: 'humans', rank: 39, userID: '54dcf4ec7b4e13500586e511', sessionID: '5518609b5afdf53205577d82', name: 'Da5id', wins: 46, losses: 61, playtime: 711, codeLanguage: 'python'}
{team: 'humans', rank: 40, userID: '550420ad2c5fbf7b056a18a0', sessionID: '550cffa5a605ba5a051205d4', name: 'YuanFeng', wins: 46, losses: 62, playtime: 5471, codeLanguage: 'python'}
{team: 'humans', rank: 41, userID: '54f9cad2d12cf54d069d8465', sessionID: '550cc77d9e22dc56056bdfe8', name: 'abcdefguan', wins: 45, losses: 61, playtime: 4873, codeLanguage: 'python'}
{team: 'humans', rank: 42, userID: '54f914455cb508f3058b6553', sessionID: '550d00759e22dc56056be0e0', name: 'KaiXFlare', wins: 45, losses: 63, playtime: 5578, codeLanguage: 'python'}
{team: 'humans', rank: 42, userID: '5443c3609792f94d06eecbb3', sessionID: '5515afd91c3bb18709e26474', name: 'PiedotTaste', wins: 45, losses: 63, playtime: 503, codeLanguage: 'javascript'}
{team: 'humans', rank: 44, userID: '53b9fa0194abf748058ea71a', sessionID: '55160678b77a16330597d2ea', name: 'UMD_Liquidator', wins: 44, losses: 64, playtime: 16710, codeLanguage: 'javascript'}
{team: 'humans', rank: 45, userID: '550db4c425b6f688062cb7f5', sessionID: '5515bc21fd67e4b4095aae6e', name: 'coderg0d', wins: 43, losses: 65, playtime: 17150, codeLanguage: 'javascript'}
{team: 'humans', rank: 46, userID: '53e5783d6c59f5340504a6e9', sessionID: '55162c77de8bc03205975793', name: 'YodaEater', wins: 42, losses: 66, playtime: 15075, codeLanguage: 'python'}
{team: 'humans', rank: 47, userID: '539cec01ab5dfc3a05cbf2d2', sessionID: '55158457b8185a5205e172a7', name: 'matthewd', wins: 41, losses: 67, playtime: 2166, codeLanguage: 'javascript'}
{team: 'humans', rank: 48, userID: '535d52e90f77a8470b782df4', sessionID: '551676f64d24212f059446b6', name: 'binarychase', wins: 40, losses: 68, playtime: 13148, codeLanguage: 'javascript'}
{team: 'humans', rank: 49, userID: '54f9064ad12cf54d069d54a4', sessionID: '550cc7469e22dc56056bdfe3', name: 'hussain1998', wins: 39, losses: 69, playtime: 5516, codeLanguage: 'python'}
{team: 'humans', rank: 49, userID: '539bb5b7ab5dfc3a05cae063', sessionID: '55216e3885fca531055424d6', name: 'DollarAkshay', wins: 39, losses: 69, playtime: 965, codeLanguage: 'javascript'}
{team: 'humans', rank: 49, userID: '54cf001ead25d155057ec294', sessionID: '550cc77c9e22dc56056bdfe7', name: 'jasmineseah-17', wins: 39, losses: 69, playtime: 5260, codeLanguage: 'python'}
{team: 'humans', rank: 52, userID: '55159c561a93145605a65360', sessionID: '55159cde99d51b55053609b4', name: 'PaulT', wins: 37, losses: 71, playtime: 2635, codeLanguage: 'python'}
{team: 'humans', rank: 53, userID: '54ce3426c39305530570368c', sessionID: '5516a6c74d24212f05944ca9', name: 'Zenador', wins: 36, losses: 70, playtime: 6894, codeLanguage: 'javascript'}
{team: 'humans', rank: 54, userID: '550493e2836beb81054ad1c2', sessionID: '551822bc81389437054e089d', name: 'MrWooly', wins: 36, losses: 72, playtime: 440, codeLanguage: 'python'}
{team: 'humans', rank: 54, userID: '5501d57b3d219b9808c098e9', sessionID: '5517f863079f2833053c8413', name: 'Mouschti', wins: 36, losses: 72, playtime: 532, codeLanguage: 'javascript'}
{team: 'humans', rank: 54, userID: '535a3dd5f3c9943f0a4ed434', sessionID: '551f45faed37b43005e2c85d', name: 'Chankorinman', wins: 36, losses: 72, playtime: 581, codeLanguage: 'javascript'}
{team: 'humans', rank: 54, userID: '55156d00d792f354050c61ea', sessionID: '5519fe8c5afdf5320557e679', name: 'Pavel87', wins: 36, losses: 72, playtime: 1900, codeLanguage: 'python'}
{team: 'humans', rank: 54, userID: '5305df9df269d92647ceb862', sessionID: '551a81c2c13fa03205251429', name: 'TimTam', wins: 36, losses: 72, playtime: 1773, codeLanguage: 'python'}
{team: 'humans', rank: 59, userID: '54be1a95cfb4e9ab16128314', sessionID: '5516e7f758852c3405f487d2', name: 'Albertos', wins: 35, losses: 73, playtime: 335, codeLanguage: 'python'}
{team: 'humans', rank: 59, userID: '54dddee006024d52057534a0', sessionID: '551a4d835cd6bae409521577', name: 'ryu.dy', wins: 35, losses: 73, playtime: 4247, codeLanguage: 'python'}
{team: 'humans', rank: 61, userID: '548cf2d0457ef12c0922ce04', sessionID: '551680efc79ef1fd062b57df', name: 'Lee87', wins: 34, losses: 74, playtime: 243, codeLanguage: 'javascript'}
{team: 'humans', rank: 61, userID: '54f00e7cbc22bc830591b375', sessionID: '55222ad6d06c12300505737e', name: 'Fame7', wins: 34, losses: 74, playtime: 880, codeLanguage: 'python'}
{team: 'humans', rank: 61, userID: '527ba37865a2cf8339000efd', sessionID: '5516ea7758852c3405f48871', name: 'Elinus', wins: 34, losses: 74, playtime: 75, codeLanguage: 'javascript'}
{team: 'humans', rank: 61, userID: '54b87f637755bf5405a600f7', sessionID: '551dfedc4c73053505269424', name: 'Anonymous', wins: 34, losses: 74, playtime: 531, codeLanguage: 'javascript'}
{team: 'humans', rank: 61, userID: '55127452ee12f7580524512f', sessionID: '551c83fb4c73053505263a4e', name: 'Oxidis', wins: 34, losses: 74, playtime: 1526, codeLanguage: 'python'}
{team: 'humans', rank: 61, userID: '54f94d77802189c30518184c', sessionID: '550cffdfa605ba5a051205d6', name: 'OblivionOverlord', wins: 34, losses: 74, playtime: 4130, codeLanguage: 'python'}
{team: 'humans', rank: 67, userID: '5515d99c9f2b9c5305f56d5c', sessionID: '551ac6e24c7305350525bb5b', name: 'Anonymous', wins: 32, losses: 76, playtime: 5641, codeLanguage: 'javascript'}
{team: 'humans', rank: 67, userID: '540219a742fb9c380ad1eae3', sessionID: '551618f253665d3005251ae0', name: 'shadowfire', wins: 32, losses: 76, playtime: 1105, codeLanguage: 'javascript'}
{team: 'humans', rank: 69, userID: '5477bd26c439b9160865589f', sessionID: '5518a3a74c73053505253acf', name: 'anodinia', wins: 31, losses: 77, playtime: 231, codeLanguage: 'python'}
{team: 'humans', rank: 69, userID: '53346ca2523566907564fd53', sessionID: '5522edd6c9e6fd33051496ed', name: 'Ksionc', wins: 31, losses: 77, playtime: 289, codeLanguage: 'javascript'}
{team: 'humans', rank: 69, userID: '54f945e463684f2409c7d498', sessionID: '550cffe1a605ba5a051205d7', name: 'ItsBlitz', wins: 31, losses: 77, playtime: 4889, codeLanguage: 'python'}
{team: 'humans', rank: 72, userID: '5513153fad27c0b705c2829e', sessionID: '5516bb70b77a16330597e6e0', name: 'iPick', wins: 30, losses: 78, playtime: 1109, codeLanguage: 'javascript'}
{team: 'humans', rank: 72, userID: '53049f3350b42adb6e86e046', sessionID: '550cfffba605ba5a051205d8', name: 'gph004', wins: 30, losses: 78, playtime: 4679, codeLanguage: 'python'}
{team: 'humans', rank: 74, userID: '536eeed3bf08a63905efdd8a', sessionID: '550c79bca98c9c7f05ab05be', name: 'basicer', wins: 28, losses: 77, playtime: 1199, codeLanguage: 'lua'}
{team: 'humans', rank: 75, userID: '54623591da18079109220097', sessionID: '5518e6c01f12482609b47160', name: 'FreeKrad', wins: 29, losses: 79, playtime: 1384, codeLanguage: 'javascript'}
{team: 'humans', rank: 75, userID: '54cc2cf2f20cb05105c3d036', sessionID: '551bf5b24c73053505260370', name: 'Mihai2015', wins: 29, losses: 79, playtime: 1415, codeLanguage: 'python'}
{team: 'humans', rank: 75, userID: '550fe04ceb13a37c05136498', sessionID: '551ad43f5cd6bae409525096', name: 'FuzzicalLogic', wins: 29, losses: 79, playtime: 88327, codeLanguage: 'javascript'}
{team: 'humans', rank: 75, userID: '52d81ab38133efa92e0000d0', sessionID: '5515a7091a93145605a65bd9', name: 'DamienG002', wins: 29, losses: 79, playtime: 1619, codeLanguage: 'python'}
{team: 'humans', rank: 75, userID: '550161abf94fb5820504d6d9', sessionID: '551eb864d69ccd3305a4b4bd', name: 'Ien Clack', wins: 29, losses: 79, playtime: 610, codeLanguage: 'python'}
{team: 'humans', rank: 75, userID: '55080de2190b7bba08dab219', sessionID: '551f459f9800ae33059c539f', name: 'Anonymous', wins: 29, losses: 79, playtime: 651, codeLanguage: 'python'}
{team: 'humans', rank: 81, userID: '550fc1b5e8cfa180057bcf9a', sessionID: '551dbdb95afdf5320559095b', name: 'Jython', wins: 28, losses: 80, playtime: 965, codeLanguage: 'python'}
{team: 'humans', rank: 81, userID: '551711f88bdcbad0074fb07e', sessionID: '552189d56a8b7c22069934a6', name: 'GJW1', wins: 28, losses: 80, playtime: 320, codeLanguage: 'python'}
{team: 'humans', rank: 81, userID: '5391144f82f0bc470523c8ab', sessionID: '550cc74bca461f5705f3d144', name: 'Polaricicle', wins: 28, losses: 80, playtime: 5801, codeLanguage: 'python'}
{team: 'humans', rank: 84, userID: '548ae4ddaed203880bc9d72d', sessionID: '55213f1c85fca53105541f99', name: 'Brizar', wins: 26, losses: 82, playtime: 1827, codeLanguage: 'python'}
{team: 'humans', rank: 85, userID: '54fc40f847b0892406d7428c', sessionID: '550d004bca461f5705f3d1ed', name: 'galaxypantz', wins: 25, losses: 82, playtime: 3444, codeLanguage: 'python'}
{team: 'humans', rank: 86, userID: '55196a90e3ae3a3505f7fdcd', sessionID: '55196b035afdf5320557b249', name: 'zbx', wins: 24, losses: 84, playtime: 4048, codeLanguage: 'python'}
{team: 'humans', rank: 86, userID: '550d599a03cae479055c02bd', sessionID: '5516a33f58852c3405f47767', name: 'Ben0225', wins: 24, losses: 84, playtime: 6580, codeLanguage: 'python'}
{team: 'humans', rank: 88, userID: '55158827bf2bea55051d0a0a', sessionID: '551da49c5afdf532055901df', name: 'kpbochenek', wins: 22, losses: 82, playtime: 798, codeLanguage: 'python'}
{team: 'humans', rank: 89, userID: '547b41f59325e43e052e02a8', sessionID: '5515c3871a93145605a66b4b', name: 'Tamerlan4', wins: 23, losses: 84, playtime: 196, codeLanguage: 'javascript'}
{team: 'humans', rank: 90, userID: '53880299d06c503805fdd57e', sessionID: '5519aeb2c13fa0320524f401', name: 'Дор', wins: 22, losses: 85, playtime: 177, codeLanguage: 'python'}
{team: 'humans', rank: 91, userID: '550701ea75c0eb4a10ac7f4a', sessionID: '550d00bb9e22dc56056be0e5', name: 'Loh June Yong', wins: 22, losses: 86, playtime: 5443, codeLanguage: 'python'}
{team: 'humans', rank: 92, userID: '52a8f5e7a9fb033114000f08', sessionID: '55158154f43d375105fbdcf8', name: 'Mpgod1234', wins: 21, losses: 86, playtime: 558, codeLanguage: 'javascript'}
{team: 'humans', rank: 93, userID: '546e523a047aaa780854a46b', sessionID: '55087d90e810e35a09e335e8', name: 'Popey456963', wins: 21, losses: 87, playtime: 4724, codeLanguage: 'python'}
{team: 'humans', rank: 94, userID: '54f114d51022997e054ea52b', sessionID: '551d30c8e3ae3a3505f91191', name: 'F_Deity_Link', wins: 20, losses: 87, playtime: 1324, codeLanguage: 'lua'}
{team: 'humans', rank: 94, userID: '5515ffd982ee611b05aeb6f7', sessionID: '5516008aa174c81b05465604', name: 'Anonymous', wins: 20, losses: 87, playtime: 113, codeLanguage: 'javascript'}
{team: 'humans', rank: 96, userID: '54fad0665d8ec82806aad9bf', sessionID: '550d00129e22dc56056be0d7', name: 'vanessatan', wins: 19, losses: 89, playtime: 5013, codeLanguage: 'python'}
{team: 'humans', rank: 96, userID: '5487a14a4647d2b006cf7edd', sessionID: '551854a1e3ae3a3505f7c904', name: 'Endercore79', wins: 19, losses: 89, playtime: 769, codeLanguage: 'python'}
{team: 'humans', rank: 96, userID: '53416ac43a7f7a6358b7029a', sessionID: '55166a794d24212f05944596', name: 'Lylo', wins: 19, losses: 89, playtime: 359, codeLanguage: 'python'}
{team: 'humans', rank: 99, userID: '5470e99829e4c0710587df4c', sessionID: '5521b204abdb444805e883bb', name: 'Sthomas', wins: 18, losses: 89, playtime: 396, codeLanguage: 'javascript'}
{team: 'humans', rank: 99, userID: '54f240e481f9a6750530ed0c', sessionID: '5516db0153665d3005253b4d', name: 'Bosdet', wins: 18, losses: 89, playtime: 5448, codeLanguage: 'python'}
{team: 'humans', rank: 101, userID: '5495c837fcf9386d07d30866', sessionID: '550c73e1a98c9c7f05ab03ac', name: 'awesome3000', wins: 18, losses: 90, playtime: 2586, codeLanguage: 'python'}
{team: 'humans', rank: 102, userID: '54c3a0aa83b9575305363fbd', sessionID: '5515f79482ee611b05aeb454', name: 'ninja_star', wins: 17, losses: 90, playtime: 423, codeLanguage: 'javascript'}
{team: 'humans', rank: 103, userID: '54cbaa236947cf5405dab7e4', sessionID: '550cd526ca461f5705f3d1a0', name: 'hannie', wins: 16, losses: 90, playtime: 2400, codeLanguage: 'python'}
{team: 'humans', rank: 104, userID: '550cc15b0f7e6ece0684a2d4', sessionID: '550cc195fa4ee5e0062ed84d', name: 'The AI', wins: 15, losses: 92, playtime: 385, codeLanguage: 'clojure'}
{team: 'humans', rank: 105, userID: '546656d5f5522b9805dfd4ca', sessionID: '551f09a0921f633305d4d7d8', name: '1234526', wins: 15, losses: 93, playtime: 909, codeLanguage: 'javascript'}
{team: 'humans', rank: 106, userID: '5500418620b2567d0514b763', sessionID: '550d0037a605ba5a051205da', name: 'luxedlyani', wins: 14, losses: 94, playtime: 5487, codeLanguage: 'python'}
{team: 'humans', rank: 106, userID: '532795fcd2a31ed46c63770e', sessionID: '55205354be496e310568fe50', name: 'kevbot', wins: 14, losses: 94, playtime: 2118, codeLanguage: 'python'}
{team: 'humans', rank: 108, userID: '53de2603f9fd4335057a50ca', sessionID: '551632595ab8ae3105cad77a', name: 'Sunnyau', wins: 12, losses: 96, playtime: 9882, codeLanguage: 'python'}
{team: 'humans', rank: 109, userID: '550aa9c9c731583007335e14', sessionID: '55158850d792f354050c7eb5', name: 'Anonymous', wins: 11, losses: 97, playtime: 137, codeLanguage: 'python'}
{team: 'humans', rank: 110, userID: '551095ad3ab45d7d05d0290f', sessionID: '55209eeed06c123005054edf', name: 'The Dark Slayer 365', wins: 10, losses: 98, playtime: 695, codeLanguage: 'python'}
{team: 'humans', rank: 111, userID: '5519cb4ae3ae3a3505f828fe', sessionID: '551a570dd69ccd3305a34080', name: 'weiyun', wins: 9, losses: 99, playtime: 2574, codeLanguage: 'python'}
{team: 'humans', rank: 112, userID: '551285ff39ac155805f4aa0a', sessionID: '551e83bee311d80c07b35d46', name: 'M. Vincent', wins: 8, losses: 100, playtime: 6127, codeLanguage: 'javascript'}
{team: 'humans', rank: 113, userID: '54f62d7d50181a8505f97e1d', sessionID: '551f1f29526d5e3505807c00', name: 'Oscha Esservic', wins: 0, losses: 108, playtime: 386, codeLanguage: 'javascript'}
{team: 'humans', rank: 113, userID: '52cfa17ec0e2ea5d17001149', sessionID: '551716f258852c3405f49269', name: 'DarkLynk', wins: 0, losses: 108, playtime: 189, codeLanguage: 'python'}
{team: 'humans', rank: 113, userID: '54f1f2f89bc914830528271f', sessionID: '550cc75a9e22dc56056bdfe4', name: 'Reilord', wins: 0, losses: 108, playtime: 4752, codeLanguage: 'python'}
{team: 'humans', rank: 113, userID: '54aab1313feb6a3f05f20854', sessionID: '551c82a25cd6bae409530abb', name: 'Flamethowerland', wins: 0, losses: 108, playtime: 506, codeLanguage: 'python'}
]
results['zero-sum'].ogres = [
{team: 'ogres', rank: 1, userID: '532dbc73a622924444b68ed9', sessionID: '551599a499d51b55053606f2', name: 'Wizard Dude', wins: 162, losses: 1, playtime: 43223, codeLanguage: 'javascript'}
{team: 'ogres', rank: 2, userID: '54ffc9b8a043d19b0a457309', sessionID: '551e1a2ae3ae3a3505f96090', name: 'xxx987654321', wins: 157, losses: 5, playtime: 11649, codeLanguage: 'python'}
{team: 'ogres', rank: 3, userID: '531c8c3ccf439d790a23af04', sessionID: '550c2f5c1cd64bb7050fc8e5', name: 'nemoyatpeace', wins: 153, losses: 9, playtime: 4171, codeLanguage: 'python'}
{team: 'ogres', rank: 4, userID: '54ccc53ab4fb475505282187', sessionID: '55168b6353665d3005252860', name: 'ImDev', wins: 152, losses: 10, playtime: 4842, codeLanguage: 'python'}
{team: 'ogres', rank: 5, userID: '551995ec5cd6bae40951dec9', sessionID: '5519969e5cd6bae40951df59', name: 'wrdctr', wins: 151, losses: 11, playtime: 22070, codeLanguage: 'python'}
{team: 'ogres', rank: 5, userID: '54d9a5d2a9bd9f57050fe822', sessionID: '550c48b13045b78405c96775', name: 'Vlevo', wins: 151, losses: 11, playtime: 41246, codeLanguage: 'clojure'}
{team: 'ogres', rank: 7, userID: '548b8c556c32e64e1c437957', sessionID: '551625ac5ab8ae3105cad65f', name: 'Nisha Noire', wins: 150, losses: 12, playtime: 6977, codeLanguage: 'python'}
{team: 'ogres', rank: 7, userID: '551824a31f12482609b44fad', sessionID: '55185272d69ccd3305a2ba9d', name: 'Ovrmrrw', wins: 150, losses: 12, playtime: 4139, codeLanguage: 'python'}
{team: 'ogres', rank: 9, userID: '5503ac1a2c5fbf7b056a0eff', sessionID: '551766524c7305350525145f', name: 'David C Ellis', wins: 147, losses: 14, playtime: 5804, codeLanguage: 'javascript'}
{team: 'ogres', rank: 10, userID: '5452ed0623ef1e5d089d31e2', sessionID: '55164ac8de8bc0320597591e', name: 'Vivaldi', wins: 146, losses: 15, playtime: 20229, codeLanguage: 'python'}
{team: 'ogres', rank: 11, userID: '52debd1b5432dcf11c000164', sessionID: '55162fc358852c3405f468f6', name: 'Agathanar', wins: 144, losses: 18, playtime: 13849, codeLanguage: 'python'}
{team: 'ogres', rank: 12, userID: '55169fb1de8bc03205976007', sessionID: '5516bbd7aedec33205e8b3c4', name: 'XN137', wins: 140, losses: 22, playtime: 44494, codeLanguage: 'javascript'}
{team: 'ogres', rank: 13, userID: '55172d51502e8b37053130b2', sessionID: '551840461f12482609b45518', name: 'Sean653', wins: 140, losses: 23, playtime: 5501, codeLanguage: 'javascript'}
{team: 'ogres', rank: 14, userID: '5512ef72a4febf5905274f07', sessionID: '551851355afdf53205577ac4', name: 'Vincent Maths', wins: 137, losses: 25, playtime: 2812, codeLanguage: 'javascript'}
{team: 'ogres', rank: 15, userID: '526a4e91c0a4f3a21f000c50', sessionID: '5516728baedec33205e8a5b9', name: 'spicydog', wins: 133, losses: 28, playtime: 3790, codeLanguage: 'python'}
{team: 'ogres', rank: 16, userID: '5511931af72de65605f988b4', sessionID: '5515a3e3bf2bea55051d2225', name: 'Deipablo', wins: 130, losses: 32, playtime: 9331, codeLanguage: 'javascript'}
{team: 'ogres', rank: 17, userID: '5515b4e1fd67e4b4095aaa5f', sessionID: '5515c034d792f354050ca69e', name: 'i_eet_leefs', wins: 128, losses: 34, playtime: 158, codeLanguage: 'python'}
{team: 'ogres', rank: 18, userID: '54ab32c13feb6a3f05f55e94', sessionID: '5515debc64a969570abf9827', name: 'SaladTongs', wins: 125, losses: 37, playtime: 26496, codeLanguage: 'python'}
{team: 'ogres', rank: 19, userID: '54cb0fbd0623615605392f74', sessionID: '550cc86d9e22dc56056bdfec', name: 'Dragernix', wins: 124, losses: 38, playtime: 4365, codeLanguage: 'python'}
{team: 'ogres', rank: 19, userID: '54f609476b0774880597fdd4', sessionID: '55171a58b77a16330597f89e', name: 'Danylo Dubinin', wins: 124, losses: 38, playtime: 6951, codeLanguage: 'python'}
{team: 'ogres', rank: 21, userID: '549aed0eb530133e053a3acc', sessionID: '55159f149f2b9c5305f551a0', name: 'Spencer Tachick', wins: 123, losses: 39, playtime: 12292, codeLanguage: 'python'}
{team: 'ogres', rank: 21, userID: '5507e6c8e7d9907b054a61e9', sessionID: '55167b894d24212f05944728', name: 'NormannK', wins: 123, losses: 39, playtime: 9490, codeLanguage: 'python'}
{team: 'ogres', rank: 23, userID: '549b8ead1880d5b7065ff57c', sessionID: '55161e90aedec33205e89e2c', name: 'Dymarr', wins: 116, losses: 46, playtime: 281, codeLanguage: 'python'}
{team: 'ogres', rank: 24, userID: '53ca0f0aaa30cd860586b24f', sessionID: '5521bf1b32954a32050203ba', name: 'Edgar', wins: 115, losses: 47, playtime: 4999, codeLanguage: 'javascript'}
{team: 'ogres', rank: 25, userID: '54fdacd0106d15f105fc7d4c', sessionID: '550cfffbca461f5705f3d1ec', name: 'Midhat', wins: 114, losses: 48, playtime: 5557, codeLanguage: 'python'}
{team: 'ogres', rank: 26, userID: '54f5d94c9be7567905e95099', sessionID: '551d1124d69ccd3305a43329', name: 's_a_n_d', wins: 114, losses: 49, playtime: 3905, codeLanguage: 'javascript'}
{team: 'ogres', rank: 27, userID: '5515ff2482ee611b05aeb6c6', sessionID: '5515ff641bfec61c050245ab', name: 'Carl Maxwell', wins: 113, losses: 48, playtime: 10208, codeLanguage: 'python'}
{team: 'ogres', rank: 28, userID: '55066eefce02078605ce9126', sessionID: '550d29999e22dc56056be28a', name: 'Koyint', wins: 113, losses: 49, playtime: 524, codeLanguage: 'python'}
{team: 'ogres', rank: 29, userID: '5517d8b0e3ae3a3505f7b73d', sessionID: '5517de29079f2833053c8108', name: 'aknopf', wins: 113, losses: 50, playtime: 203, codeLanguage: 'python'}
{team: 'ogres', rank: 30, userID: '54ffbe6ee950f7a707ec9577', sessionID: '550cc7bc9e22dc56056bdfe9', name: 'chuayupeng', wins: 112, losses: 50, playtime: 4602, codeLanguage: 'python'}
{team: 'ogres', rank: 31, userID: '5506889b127bb387059def67', sessionID: '550d001435dc145905672238', name: 'En Hao', wins: 111, losses: 51, playtime: 5234, codeLanguage: 'python'}
{team: 'ogres', rank: 32, userID: '527bdd42dd3c4aea37001500', sessionID: '55159c75b8185a5205e183dc', name: 'Makaze', wins: 109, losses: 53, playtime: 2213, codeLanguage: 'coffeescript'}
{team: 'ogres', rank: 33, userID: '55181575e3ae3a3505f7bf46', sessionID: '55191d035afdf532055796d6', name: 'Satellite One', wins: 106, losses: 51, playtime: 16911, codeLanguage: 'python'}
{team: 'ogres', rank: 34, userID: '54b1773a75e3055205e5a449', sessionID: '550c3a98535111cc0539a3f9', name: 'Catsync', wins: 107, losses: 54, playtime: 381, codeLanguage: 'javascript'}
{team: 'ogres', rank: 35, userID: '53a0bc84610a6b3505561811', sessionID: '5516185f53665d3005251ac7', name: 'Fluzzarn', wins: 102, losses: 60, playtime: 1835, codeLanguage: 'javascript'}
{team: 'ogres', rank: 36, userID: '5516bf048bdcbad0074f9c29', sessionID: '5516d0a258852c3405f481da', name: 'Expote2', wins: 100, losses: 62, playtime: 4576, codeLanguage: 'python'}
{team: 'ogres', rank: 37, userID: '54ff5e1a00e4485a07e76bb9', sessionID: '5515ac67fd67e4b4095aa52f', name: 'Twonky', wins: 96, losses: 66, playtime: 6772, codeLanguage: 'python'}
{team: 'ogres', rank: 38, userID: '550003910f91fab20b3624e8', sessionID: '550d00439e22dc56056be0de', name: 'CyanLycan', wins: 95, losses: 66, playtime: 5482, codeLanguage: 'python'}
{team: 'ogres', rank: 39, userID: '54f2b39381f9a6750530f893', sessionID: '550ccb259e22dc56056be001', name: 'Flamanta', wins: 95, losses: 68, playtime: 4734, codeLanguage: 'python'}
{team: 'ogres', rank: 40, userID: '54f0775615cde87c05d6caf4', sessionID: '550cc7ef35dc1459056721be', name: 'Jericho Chua', wins: 93, losses: 69, playtime: 4409, codeLanguage: 'python'}
{team: 'ogres', rank: 41, userID: '539bfd0930a67c3b05d93f2a', sessionID: '55158a0bf43d375105fbe4ec', name: 'reezer', wins: 93, losses: 70, playtime: 5116, codeLanguage: 'javascript'}
{team: 'ogres', rank: 42, userID: '54d2c4024e4a08550556e01c', sessionID: '550cd3269e22dc56056be050', name: 'Alaete', wins: 92, losses: 69, playtime: 1804, codeLanguage: 'python'}
{team: 'ogres', rank: 43, userID: '53e2f1046c59f534050411f3', sessionID: '550d000e35dc145905672236', name: 'Live2Deliver', wins: 91, losses: 71, playtime: 5362, codeLanguage: 'python'}
{team: 'ogres', rank: 44, userID: '5517858d079f2833053c79a7', sessionID: '551786c7e3ae3a3505f7b0f6', name: 'Kevbot5000', wins: 88, losses: 72, playtime: 93126, codeLanguage: 'javascript'}
{team: 'ogres', rank: 45, userID: '54f3527fde0f173f14215f40', sessionID: '552210f33ffd333305f2e9b5', name: 'Anonymous', wins: 88, losses: 73, playtime: 346, codeLanguage: 'javascript'}
{team: 'ogres', rank: 46, userID: '55184fc7c13fa0320524a596', sessionID: '5518516ac13fa0320524a5f1', name: 'Mossan', wins: 85, losses: 77, playtime: 11842, codeLanguage: 'python'}
{team: 'ogres', rank: 47, userID: '539bb1d46e1d92310506ec6b', sessionID: '551ab214d69ccd3305a35b6c', name: 'Belackarad', wins: 84, losses: 78, playtime: 2921, codeLanguage: 'python'}
{team: 'ogres', rank: 48, userID: '534316e1d9a976607ace1f48', sessionID: '5518c7954c73053505253f23', name: 'Racksickle', wins: 84, losses: 79, playtime: 4118, codeLanguage: 'javascript'}
{team: 'ogres', rank: 49, userID: '5515b3d599d51b55053619e3', sessionID: '5517f05fe3ae3a3505f7b998', name: 'Crul', wins: 82, losses: 81, playtime: 99, codeLanguage: 'javascript'}
{team: 'ogres', rank: 50, userID: '548bc8bdcb1a6d754f530ef9', sessionID: '5518c2a0d69ccd3305a2cd93', name: 'MonkeykingZach1', wins: 80, losses: 82, playtime: 2904, codeLanguage: 'javascript'}
{team: 'ogres', rank: 51, userID: '54ea122b6a05ed9a0803f20b', sessionID: '55181b4de3ae3a3505f7c028', name: 'All3ck', wins: 77, losses: 84, playtime: 16269, codeLanguage: 'python'}
{team: 'ogres', rank: 52, userID: '5522bf863ffd333305f30ef5', sessionID: '5522c90cabdb444805e8c0b9', name: 'Fireball42', wins: 73, losses: 87, playtime: 3431, codeLanguage: 'javascript'}
{team: 'ogres', rank: 53, userID: '5514a8636b06ed1e09bc4c01', sessionID: '55204b8ffe6383360550157d', name: 'Hephaestus2', wins: 73, losses: 89, playtime: 1162, codeLanguage: 'python'}
{team: 'ogres', rank: 54, userID: '539189a2ff512d470582a570', sessionID: '550cc7f3a605ba5a0512050f', name: 'Lee Yang Peng', wins: 72, losses: 88, playtime: 5743, codeLanguage: 'python'}
{team: 'ogres', rank: 55, userID: '546e31004a0dd3de07f2b3f0', sessionID: '5515a3c3518f4e1409379f15', name: 'Blauelf', wins: 72, losses: 91, playtime: 119, codeLanguage: 'python'}
{team: 'ogres', rank: 56, userID: '55125f4ba4febf590526f3ea', sessionID: '551a8d32d69ccd3305a34b40', name: 'Yan4ik', wins: 71, losses: 91, playtime: 7018, codeLanguage: 'python'}
{team: 'ogres', rank: 57, userID: '54cba6498a09e554056f7e83', sessionID: '550cce4f9e22dc56056be01a', name: 'Skyhawk5', wins: 70, losses: 92, playtime: 1415, codeLanguage: 'python'}
{team: 'ogres', rank: 58, userID: '550126ad9201157c05677836', sessionID: '550d003ba605ba5a051205dc', name: 'ohichimaru', wins: 66, losses: 96, playtime: 5118, codeLanguage: 'python'}
{team: 'ogres', rank: 59, userID: '54ea04ede90cfb5005381cae', sessionID: '550d00bc9e22dc56056be0e6', name: 'fafazirah', wins: 65, losses: 98, playtime: 2494, codeLanguage: 'python'}
{team: 'ogres', rank: 60, userID: '54fd92b847b0892406d76ee6', sessionID: '550d015d9e22dc56056be0f2', name: 'Soon Wei', wins: 64, losses: 98, playtime: 5270, codeLanguage: 'python'}
{team: 'ogres', rank: 61, userID: '537d66783dcf67c40571fce9', sessionID: '550976d42e7f640f07bf7cbc', name: 'ProfBoesch', wins: 61, losses: 101, playtime: 2684, codeLanguage: 'python'}
{team: 'ogres', rank: 62, userID: '54ef1ac12fd7dd55052b4bf6', sessionID: '550cc78ea605ba5a0512050d', name: 'ZoppyOS', wins: 60, losses: 101, playtime: 5566, codeLanguage: 'python'}
{team: 'ogres', rank: 63, userID: '54f1e0f2d2969f8405ef6e93', sessionID: '550d03639e22dc56056be123', name: 'hongyue1995', wins: 57, losses: 104, playtime: 3402, codeLanguage: 'python'}
{team: 'ogres', rank: 64, userID: '53b54cda7e17883a0575767a', sessionID: '5515bde81c3bb18709e26cd3', name: 'JavaCaster1', wins: 55, losses: 106, playtime: 91, codeLanguage: 'javascript'}
{team: 'ogres', rank: 65, userID: '54e3389fab6f345305701d9d', sessionID: '550d003a35dc145905672239', name: 'Aswin A', wins: 54, losses: 108, playtime: 4830, codeLanguage: 'python'}
{team: 'ogres', rank: 66, userID: '53b8f7ab3a63df45051b31d6', sessionID: '551ff648b10646310516d109', name: 'Elizabeth20', wins: 46, losses: 117, playtime: 3329, codeLanguage: 'javascript'}
{team: 'ogres', rank: 67, userID: '54f82214cebfc7450c16bbe9', sessionID: '550cc7f49e22dc56056bdfeb', name: 'Chaoxide', wins: 45, losses: 118, playtime: 5296, codeLanguage: 'python'}
{team: 'ogres', rank: 68, userID: '54fa96ce9ae7221e0672306b', sessionID: '550cc7e29e22dc56056bdfea', name: 'Xyrilyn', wins: 41, losses: 121, playtime: 5516, codeLanguage: 'python'}
{team: 'ogres', rank: 69, userID: '54f420d8c43ea77705a27522', sessionID: '550cc78535dc1459056721bd', name: 'WangLu', wins: 40, losses: 122, playtime: 5460, codeLanguage: 'python'}
{team: 'ogres', rank: 70, userID: '54a976c1b9a10c3e059a5414', sessionID: '551c623f8328b9020decd431', name: 'germ13', wins: 35, losses: 127, playtime: 4753, codeLanguage: 'python'}
{team: 'ogres', rank: 71, userID: '52619a3e99987a8a0900009e', sessionID: '5516d34058852c3405f48284', name: 'iTruth', wins: 35, losses: 128, playtime: 5077, codeLanguage: 'javascript'}
{team: 'ogres', rank: 72, userID: '55197724e3ae3a3505f80446', sessionID: '551977265cd6bae40951cafb', name: 'Stowned', wins: 30, losses: 133, playtime: 1594, codeLanguage: 'python'}
{team: 'ogres', rank: 73, userID: '546e6be7da3c21f30848d235', sessionID: '551c5c0c1794dc1a112c536c', name: 'Jonas5', wins: 28, losses: 135, playtime: 663, codeLanguage: 'python'}
{team: 'ogres', rank: 73, userID: '55098a62a2012e800a354350', sessionID: '551dae53e3ae3a3505f94fb9', name: 'coding6', wins: 28, losses: 135, playtime: 66, codeLanguage: 'python'}
{team: 'ogres', rank: 73, userID: '54f160550ac4bd7c05458e7e', sessionID: '55166f02aedec33205e8a541', name: 'Dima2006', wins: 28, losses: 135, playtime: 61, codeLanguage: 'javascript'}
{team: 'ogres', rank: 76, userID: '54f8b3237a5c02820877960e', sessionID: '551aec19e3ae3a3505f87585', name: 'JakeWolt20', wins: 28, losses: 136, playtime: 34, codeLanguage: 'python'}
{team: 'ogres', rank: 77, userID: '54c6a197c597612313ca1520', sessionID: '5519a05c4c7305350525799e', name: 'maxx89', wins: 27, losses: 136, playtime: 408, codeLanguage: 'javascript'}
{team: 'ogres', rank: 78, userID: '550cc15b0f7e6ece0684a2d4', sessionID: '550cc20735f6fd2807b877ae', name: 'The AI', wins: 24, losses: 135, playtime: 128, codeLanguage: 'clojure'}
{team: 'ogres', rank: 79, userID: '54903e1e6f2e23450a4fd7ac', sessionID: '5519e6bc8328b9020debf33f', name: 'kev0', wins: 15, losses: 147, playtime: 3459, codeLanguage: 'python'}
{team: 'ogres', rank: 80, userID: '54fe734f834d927905307753', sessionID: '5515cefa1a93145605a66f9e', name: 'Anonymous', wins: 14, losses: 149, playtime: 1002, codeLanguage: 'python'}
]

View file

@ -20,6 +20,7 @@ ShareProgressModal = require 'views/play/modal/ShareProgressModal'
UserPollsRecord = require 'models/UserPollsRecord' UserPollsRecord = require 'models/UserPollsRecord'
Poll = require 'models/Poll' Poll = require 'models/Poll'
PollModal = require 'views/play/modal/PollModal' PollModal = require 'views/play/modal/PollModal'
storage = require 'core/storage'
trackedHourOfCode = false trackedHourOfCode = false
@ -148,10 +149,11 @@ module.exports = class CampaignView extends RootView
@render() @render()
@preloadTopHeroes() unless me.get('heroConfig')?.thangType @preloadTopHeroes() unless me.get('heroConfig')?.thangType
@$el.find('#campaign-status').delay(4000).animate({top: "-=58"}, 1000) unless @terrain is 'dungeon' @$el.find('#campaign-status').delay(4000).animate({top: "-=58"}, 1000) unless @terrain is 'dungeon'
if @terrain and me.get('name') and me.get('lastLevel') in ['forgetful-gemsmith', 'signs-and-portents'] if @terrain and me.get('anonymous') and me.get('lastLevel') is 'shadow-guard' and me.level() < 4
@openModalView new AuthModal supermodel: @supermodel, showSignupRationale: true, mode: 'signup'
else if @terrain and me.get('name') and me.get('lastLevel') in ['forgetful-gemsmith', 'signs-and-portents'] and me.level() < 5 and not (me.get('ageRange') in ['18-24', '25-34', '35-44', '45-100']) and not storage.load('sent-parent-email')
@openModalView new ShareProgressModal() @openModalView new ShareProgressModal()
setCampaign: (@campaign) -> setCampaign: (@campaign) ->
@render() @render()
@ -198,7 +200,7 @@ module.exports = class CampaignView extends RootView
if @campaigns if @campaigns
context.campaigns = {} context.campaigns = {}
for campaign in @campaigns.models for campaign in @campaigns.models when campaign.get('slug') isnt 'auditions'
context.campaigns[campaign.get('slug')] = campaign context.campaigns[campaign.get('slug')] = campaign
if @sessions.loaded if @sessions.loaded
levels = _.values($.extend true, {}, campaign.get('levels') ? {}) levels = _.values($.extend true, {}, campaign.get('levels') ? {})
@ -276,10 +278,14 @@ module.exports = class CampaignView extends RootView
countLevels: (levels) -> countLevels: (levels) ->
count = total: 0, completed: 0 count = total: 0, completed: 0
for level in levels for level, levelIndex in levels
@annotateLevel level unless level.locked? # Annotate if we haven't already. @annotateLevel level unless level.locked? # Annotate if we haven't already.
unless level.disabled unless level.disabled
++count.total unlockedInSameCampaign = levelIndex < 5 # First few are always counted (probably unlocked in previous campaign)
for otherLevel in levels when not unlockedInSameCampaign and otherLevel isnt level
for reward in (otherLevel.rewards ? []) when reward.level
unlockedInSameCampaign ||= reward.level is level.original
++count.total if unlockedInSameCampaign or not level.locked
++count.completed if @levelStatusMap[level.slug] is 'complete' ++count.completed if @levelStatusMap[level.slug] is 'complete'
count count
@ -295,7 +301,12 @@ module.exports = class CampaignView extends RootView
unless foundNext unless foundNext
for nextLevelOriginal in level.nextLevels for nextLevelOriginal in level.nextLevels
nextLevel = _.find levels, original: nextLevelOriginal nextLevel = _.find levels, original: nextLevelOriginal
if nextLevel and not nextLevel.locked and @levelStatusMap[nextLevel.slug] isnt 'complete' and (me.isPremium() or not nextLevel.requiresSubscription or nextLevel.slug is 'apocalypse') if nextLevel and not nextLevel.locked and @levelStatusMap[nextLevel.slug] isnt 'complete' and (
me.isPremium() or
not nextLevel.requiresSubscription or
nextLevel.slug is 'apocalypse' or
(nextLevel.slug is 'favorable-odds' and not @levelStatusMap['the-raised-sword'])
)
nextLevel.next = true nextLevel.next = true
foundNext = true foundNext = true
break break
@ -334,7 +345,7 @@ module.exports = class CampaignView extends RootView
@particleMan ?= new ParticleMan() @particleMan ?= new ParticleMan()
@particleMan.removeEmitters() @particleMan.removeEmitters()
@particleMan.attach @$el.find('.map') @particleMan.attach @$el.find('.map')
for level in @campaign.renderedLevels ? {} when level.hidden or level.slug is 'apocalypse' for level in @campaign.renderedLevels ? {} when level.hidden or (level.slug is 'apocalypse' and @levelStatusMap[level.slug] isnt 'complete')
particleKey = ['level', @terrain] particleKey = ['level', @terrain]
particleKey.push level.type if level.type and level.type isnt 'hero' particleKey.push level.type if level.type and level.type isnt 'hero'
particleKey.push 'premium' if level.requiresSubscription particleKey.push 'premium' if level.requiresSubscription
@ -433,7 +444,7 @@ module.exports = class CampaignView extends RootView
level = _.find _.values(@campaign.get('levels')), slug: levelSlug level = _.find _.values(@campaign.get('levels')), slug: levelSlug
requiresSubscription = level.requiresSubscription or (me.get('chinaVersion') and not (level.slug in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'signs-and-portents', 'true-names'])) requiresSubscription = level.requiresSubscription or (me.get('chinaVersion') and not (level.slug in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'signs-and-portents', 'true-names']))
canPlayAnyway = not @requiresSubscription or level.adventurer canPlayAnyway = not @requiresSubscription or level.adventurer or @levelStatusMap[level.slug]
if requiresSubscription and not canPlayAnyway if requiresSubscription and not canPlayAnyway
@openModalView new SubscribeModal() @openModalView new SubscribeModal()
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'map level clicked', level: levelSlug window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'map level clicked', level: levelSlug

View file

@ -26,6 +26,7 @@ module.exports = class HeroVictoryModal extends ModalView
'click .leaderboard-button': 'onClickLeaderboard' 'click .leaderboard-button': 'onClickLeaderboard'
'click .return-to-ladder-button': 'onClickReturnToLadder' 'click .return-to-ladder-button': 'onClickReturnToLadder'
'click .sign-up-button': 'onClickSignupButton' 'click .sign-up-button': 'onClickSignupButton'
'click .continue-from-offer-button': 'onClickContinueFromOffer'
constructor: (options) -> constructor: (options) ->
super(options) super(options)
@ -339,7 +340,11 @@ module.exports = class HeroVictoryModal extends ModalView
justBeatLevel: @level justBeatLevel: @level
supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel
_.merge options, extraOptions if extraOptions _.merge options, extraOptions if extraOptions
Backbone.Mediator.publish 'router:navigate', route: nextLevelLink, viewClass: require('views/play/CampaignView'), viewArgs: [options, @getNextLevelCampaign()] navigationEvent = route: nextLevelLink, viewClass: require('views/play/CampaignView'), viewArgs: [options, @getNextLevelCampaign()]
if @level.get('slug') is 'lost-viking' and not (me.get('age') in ['0-13', '14-17'])
@showOffer navigationEvent
else
Backbone.Mediator.publish 'router:navigate', navigationEvent
onClickLeaderboard: (e) -> onClickLeaderboard: (e) ->
@onClickContinue e, showLeaderboard: true @onClickContinue e, showLeaderboard: true
@ -355,3 +360,14 @@ module.exports = class HeroVictoryModal extends ModalView
e.preventDefault() e.preventDefault()
window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Hero Victory Modal', level: @level.get('slug') window.tracker?.trackEvent 'Started Signup', category: 'Play Level', label: 'Hero Victory Modal', level: @level.get('slug')
@openModalView new AuthModal {mode: 'signup'} @openModalView new AuthModal {mode: 'signup'}
showOffer: (@navigationEventUponCompletion) ->
@$el.find('.modal-footer > *').hide()
@$el.find(".modal-footer > .offer.#{@level.get('slug')}").show()
onClickContinueFromOffer: (e) ->
url = {
'lost-viking': 'http://www.vikingcodeschool.com/codecombat?utm_source=codecombat&utm_medium=viking_level&utm_campaign=affiliate&ref=Code+Combat+Elite'
}[@level.get('slug')]
Backbone.Mediator.publish 'router:navigate', @navigationEventUponCompletion
window.open url, '_blank' if url

View file

@ -154,7 +154,7 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
break break
$codearea = $('#code-area') $codearea = $('#code-area')
$codearea.on transitionListener, => $codearea.on transitionListener, =>
$codearea.css 'z-index', 1 unless $('html').hasClass 'fullscreen-editor' $codearea.css 'z-index', 2 unless $('html').hasClass 'fullscreen-editor'
destroy: -> destroy: ->

View file

@ -19,7 +19,9 @@ module.exports = class LeaderboardModal extends ModalView
constructor: (options) -> constructor: (options) ->
super options super options
@levelSlug = @options.levelSlug @levelSlug = @options.levelSlug
@level = @supermodel.loadModel(new Level({_id: @levelSlug}, {project: ['name', 'i18n', 'scoreType', 'original']}), 'level').model level = new Level({_id: @levelSlug})
level.project = ['name', 'i18n', 'scoreType', 'original']
@level = @supermodel.loadModel(level, 'level').model
getRenderData: (c) -> getRenderData: (c) ->
c = super c c = super c

View file

@ -1,5 +1,6 @@
ModalView = require 'views/core/ModalView' ModalView = require 'views/core/ModalView'
template = require 'templates/play/modal/share-progress-modal' template = require 'templates/play/modal/share-progress-modal'
storage = require 'core/storage'
module.exports = class SubscribeModal extends ModalView module.exports = class SubscribeModal extends ModalView
id: 'share-progress-modal' id: 'share-progress-modal'
@ -8,33 +9,9 @@ module.exports = class SubscribeModal extends ModalView
closesOnClickOutside: false closesOnClickOutside: false
events: events:
'click .back-link': 'onBackClick'
'click .close-btn': 'hide' 'click .close-btn': 'hide'
'click .continue-link': 'hide' 'click .continue-link': 'hide'
'click .send-btn': 'onClickSend' 'click .send-btn': 'onClickSend'
'click .tell-friend-btn': 'onClickTellFriend'
'click .tell-parent-btn': 'onClickTellParent'
onBackClick: (e) ->
$('.email-input').val('')
$('.send-container').hide()
$('.friend-blurb').hide()
$('.parent-blurb').hide()
$('.btn-picker-container').show()
$('.email-input').parent().removeClass('has-error')
$('.email-invalid').hide()
onClickTellFriend: (e) ->
@emailType = 'share progress modal friend'
$('.btn-picker-container').hide()
$('.friend-blurb').show()
$('.send-container').show()
onClickTellParent: (e) ->
@emailType = 'share progress modal parent'
$('.btn-picker-container').hide()
$('.parent-blurb').show()
$('.send-container').show()
onClickSend: (e) -> onClickSend: (e) ->
email = $('.email-input').val() email = $('.email-input').val()
@ -45,9 +22,10 @@ module.exports = class SubscribeModal extends ModalView
request = @supermodel.addRequestResource 'send_one_time_email', { request = @supermodel.addRequestResource 'send_one_time_email', {
url: '/db/user/-/send_one_time_email' url: '/db/user/-/send_one_time_email'
data: {email: email, type: @emailType} data: {email: email, type: 'share progress modal parent'}
method: 'POST' method: 'POST'
}, 0 }, 0
request.load() request.load()
storage.save 'sent-parent-email', true
@hide() @hide()

View file

@ -199,6 +199,8 @@ exports.config =
sass: sass:
mode: 'ruby' mode: 'ruby'
allowCache: true allowCache: true
bless:
cacheBuster: false
modules: modules:
definition: (path, data) -> definition: (path, data) ->

View file

@ -206,13 +206,15 @@ module.exports = class Handler
return @sendForbiddenError(res) return @sendForbiddenError(res)
getById: (req, res, id) -> getById: (req, res, id) ->
# return @sendNotFoundError(res) # for testing
return @sendForbiddenError(res) unless @hasAccess(req) return @sendForbiddenError(res) unless @hasAccess(req)
if req.query.project
@getDocumentForIdOrSlug id, (err, document) => projection = {}
projection[field] = 1 for field in req.query.project.split(',')
@getDocumentForIdOrSlug id, projection, (err, document) =>
return @sendDatabaseError(res, err) if err return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless document? return @sendNotFoundError(res) unless document?
return @sendForbiddenError(res) unless @hasAccessToDocument(req, document) return @sendForbiddenError(res) unless @hasAccessToDocument(req, document)
res.setHeader 'Cache-Control', 'no-cache' unless Handler.isID(id + '') # Don't cache if it's a slug instead of an ID
@sendSuccess(res, @formatEntity(req, document)) @sendSuccess(res, @formatEntity(req, document))
getByRelationship: (req, res, args...) -> getByRelationship: (req, res, args...) ->
@ -490,14 +492,18 @@ module.exports = class Handler
@isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24 @isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24
getDocumentForIdOrSlug: (idOrSlug, done) -> getDocumentForIdOrSlug: (idOrSlug, projection, done) ->
unless done
done = projection # projection is optional argument
projection = null
idOrSlug = idOrSlug+'' idOrSlug = idOrSlug+''
if Handler.isID(idOrSlug) if Handler.isID(idOrSlug)
@modelClass.findById(idOrSlug).exec (err, document) -> query = @modelClass.findById(idOrSlug)
done(err, document)
else else
@modelClass.findOne {slug: idOrSlug}, (err, document) -> query = @modelClass.findOne {slug: idOrSlug}
done(err, document) query.select projection if projection
query.exec (err, document) ->
done(err, document)
doWaterfallChecks: (req, document, done) -> doWaterfallChecks: (req, document, done) ->
return done(null, document) unless @waterfallFunctions.length return done(null, document) unless @waterfallFunctions.length

View file

@ -158,7 +158,8 @@ LevelHandler = class LevelHandler extends Handler
majorVersion: level.version.major majorVersion: level.version.major
creator: req.user._id+'' creator: req.user._id+''
query = Session.find(sessionQuery).select('-screenshot') query = Session.find(sessionQuery).select('-screenshot -transpiledCode')
# TODO: take out "code" as well, since that can get huge containing the transpiled code for the lat hero, and find another way of having the LadderSubmissionViews in the MyMatchesTab determine rankin readiness
query.exec (err, results) => query.exec (err, results) =>
if err then @sendDatabaseError(res, err) else @sendSuccess res, results if err then @sendDatabaseError(res, err) else @sendSuccess res, results

View file

@ -18,7 +18,7 @@ class LevelSessionHandler extends Handler
if req.user?.isAdmin() or if req.user?.isAdmin() or
req.user?.id is document.creator or req.user?.id is document.creator or
('employer' in (req.user?.get('permissions') ? [])) or ('employer' in (req.user?.get('permissions') ? [])) or
!document.submittedCode # TODO: only allow leaderboard access to non-top-5 solutions not 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
@ -53,6 +53,7 @@ class LevelSessionHandler extends Handler
get = (method ? req.method).toLowerCase() is 'get' get = (method ? req.method).toLowerCase() is 'get'
return true if get and document.get('submitted') return true if get and document.get('submitted')
return true if get and ('employer' in (req.user?.get('permissions') ? [])) return true if get and ('employer' in (req.user?.get('permissions') ? []))
return true if get and not document.get('submittedCode') # Allow leaderboard access to non-multiplayer sessions
super(arguments...) super(arguments...)
getCodeLanguageCounts: (req, res) -> getCodeLanguageCounts: (req, res) ->

View file

@ -1,6 +1,7 @@
# Not paired with a document in the DB, just handles coordinating between # Not paired with a document in the DB, just handles coordinating between
# the stripe property in the user with what's being stored in Stripe. # the stripe property in the user with what's being stored in Stripe.
mongoose = require 'mongoose'
async = require 'async' async = require 'async'
config = require '../../server_config' config = require '../../server_config'
Handler = require '../commons/Handler' Handler = require '../commons/Handler'
@ -23,9 +24,70 @@ class SubscriptionHandler extends Handler
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'" console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
getByRelationship: (req, res, args...) -> getByRelationship: (req, res, args...) ->
return @getSubscribers(req, res) if args[1] is 'subscribers'
return @getSubscriptions(req, res) if args[1] is 'subscriptions' return @getSubscriptions(req, res) if args[1] is 'subscriptions'
super(arguments...) super(arguments...)
getSubscribers: (req, res) ->
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
maxReturnCount = req.body.maxCount or 20
# @subscribers ?= []
# return @sendSuccess(res, @subscribers) unless _.isEmpty(@subscribers)
@subscribers = []
subscriberIDs = []
customersProcessed = 0
nextBatch = (starting_after, done) =>
options = limit: 100
options.starting_after = starting_after if starting_after
stripe.customers.list options, (err, customers) =>
return done(err) if err
customersProcessed += customers.data.length
for customer in customers.data
break unless @subscribers.length < maxReturnCount
continue unless customer?.subscriptions?.data?.length > 0
for subscription in customer.subscriptions.data
continue unless subscription.plan.id is 'basic'
amount = subscription.plan.amount
if subscription?.discount?.coupon?
if subscription.discount.coupon.percent_off
amount = amount * (100 - subscription.discount.coupon.percent_off) / 100;
else if subscription.discount.coupon.amount_off
amount -= subscription.discount.coupon.amount_off
else if customer.discount?.coupon?
if customer.discount.coupon.percent_off
amount = amount * (100 - customer.discount.coupon.percent_off) / 100
else if customer.discount.coupon.amount_off
amount -= customer.discount.coupon.amount_off
continue unless amount > 0
subscriber = start: new Date(subscription.start * 1000)
if subscription.metadata?.id?
subscriber.userID = subscription.metadata.id
subscriberIDs.push subscription.metadata.id
if subscription.cancel_at_period_end
subscriber.cancel = new Date(subscription.canceled_at * 1000)
subscriber.end = new Date(subscription.current_period_end * 1000)
@subscribers.push(subscriber)
if customers.has_more and @subscribers.length < maxReturnCount
return nextBatch(customers.data[customers.data.length - 1].id, done)
else
return done()
nextBatch null, (err) =>
return @sendDatabaseError(res, err) if err
User.find {_id: {$in: subscriberIDs}}, (err, users) =>
return @sendDatabaseError(res, err) if err
for user in users
subscriber.user = user for subscriber in @subscribers when subscriber.userID is user.id
@sendSuccess(res, @subscribers)
getSubscriptions: (req, res) -> getSubscriptions: (req, res) ->
# Returns a list of active subscriptions # Returns a list of active subscriptions
# TODO: does not handle customers with 11+ active subscriptions # TODO: does not handle customers with 11+ active subscriptions
@ -54,7 +116,6 @@ class SubscriptionHandler extends Handler
for subscription in customer.subscriptions.data for subscription in customer.subscriptions.data
continue unless subscription.plan.id is 'basic' continue unless subscription.plan.id is 'basic'
amount = subscription.plan.amount amount = subscription.plan.amount
if subscription?.discount?.coupon? if subscription?.discount?.coupon?
if subscription.discount.coupon.percent_off if subscription.discount.coupon.percent_off
@ -72,7 +133,7 @@ class SubscriptionHandler extends Handler
sub = start: new Date(subscription.start * 1000) sub = start: new Date(subscription.start * 1000)
if subscription.cancel_at_period_end if subscription.cancel_at_period_end
sub.cancel = new Date(subscription.canceled_at * 1000) sub.cancel = new Date(subscription.canceled_at * 1000)
sub.end = new Date(sub.current_period_end * 1000) sub.end = new Date(subscription.current_period_end * 1000)
@subs.push(sub) @subs.push(sub)
# Can't fetch all the test Stripe data # Can't fetch all the test Stripe data
@ -84,9 +145,6 @@ class SubscriptionHandler extends Handler
return @sendDatabaseError(res, err) if err return @sendDatabaseError(res, err) if err
@sendSuccess(res, @subs) @sendSuccess(res, @subs)
subscribeUser: (req, user, done) -> subscribeUser: (req, user, done) ->
if (not req.user) or req.user.isAnonymous() or user.isAnonymous() if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
return done({res: 'You must be signed in to subscribe.', code: 403}) return done({res: 'You must be signed in to subscribe.', code: 403})

View file

@ -527,11 +527,11 @@ updateMatchesInSession = (matchObject, sessionID, callback) ->
opponentsArray = _.toArray opponentsClone opponentsArray = _.toArray opponentsClone
currentMatchObject.opponents = opponentsArray currentMatchObject.opponents = opponentsArray
currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage
currentMatchObject.simulator = @clientResponseObject.simulator #currentMatchObject.simulator = @clientResponseObject.simulator # Uncomment when actively debugging simulation mismatches
currentMatchObject.randomSeed = parseInt(@clientResponseObject.randomSeed or 0, 10) #currentMatchObject.randomSeed = parseInt(@clientResponseObject.randomSeed or 0, 10) # Uncomment when actively debugging simulation mismatches
LevelSession.findOne {'_id': sessionID}, (err, session) -> LevelSession.findOne {'_id': sessionID}, (err, session) ->
session = session.toObject() session = session.toObject()
currentMatchObject.playtime = session.playtime ? 0 #currentMatchObject.playtime = session.playtime ? 0 # Not useful if we can only see last 200
sessionUpdateObject = sessionUpdateObject =
$push: {matches: {$each: [currentMatchObject], $slice: -200}} $push: {matches: {$each: [currentMatchObject], $slice: -200}}
#log.info "Updating session #{sessionID}" #log.info "Updating session #{sessionID}"

View file

@ -727,8 +727,9 @@ sendNextStepsEmail = (user, now, daysAgo) ->
'maker-square': isAdult and isFast 'maker-square': isAdult and isFast
'the-firehose-project': isAdult and isFast 'the-firehose-project': isAdult and isFast
#'mv-code-club': isKid # TODO: geodetect, get landing page URL #'mv-code-club': isKid # TODO: geodetect, get landing page URL
'breakout-mentors': isKid
nAdditionalOffers = Math.max 0, 4 - _.filter(offers).length nAdditionalOffers = Math.max 0, 4 - _.filter(offers).length
possibleAdditionalOffers = ['code-school', 'one-month', 'learnable', 'pluralsight'] possibleAdditionalOffers = ['ostraining', 'code-school', 'one-month', 'learnable', 'pluralsight']
for offer in _.sample possibleAdditionalOffers, nAdditionalOffers for offer in _.sample possibleAdditionalOffers, nAdditionalOffers
offers[offer] = true offers[offer] = true
if user.isPremium() if user.isPremium()

File diff suppressed because one or more lines are too long