mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-12 08:41:46 -05:00
Merge hero selector and misc fixes
This commit is contained in:
commit
79de50126d
29 changed files with 503 additions and 146 deletions
|
@ -3,4 +3,9 @@ ThangType = require 'models/ThangType'
|
|||
|
||||
module.exports = class ThangTypeCollection extends CocoCollection
|
||||
url: '/db/thang.type'
|
||||
model: ThangType
|
||||
model: ThangType
|
||||
|
||||
fetchHeroes: ->
|
||||
@fetch {
|
||||
url: '/db/thang.type?view=heroes'
|
||||
}
|
||||
|
|
|
@ -168,6 +168,13 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@consolidateFlagHistory() if @opponentSession?.loaded
|
||||
else if session is @opponentSession
|
||||
@consolidateFlagHistory() if @session.loaded
|
||||
if @level.get('type', true) in ['course'] # course-ladder is hard to handle because there's 2 sessions
|
||||
heroConfig = me.get('heroConfig')
|
||||
return if not heroConfig
|
||||
url = "/db/thang.type/#{heroConfig.thangType}/version"
|
||||
if heroResource = @maybeLoadURL(url, ThangType, 'thang')
|
||||
@worldNecessities.push heroResource
|
||||
return
|
||||
return unless @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']
|
||||
heroConfig = session.get('heroConfig')
|
||||
heroConfig ?= me.get('heroConfig') if session is @session and not @headless
|
||||
|
|
|
@ -156,6 +156,8 @@ module.exports =
|
|||
courseProgress[levelID][userID] = { completed: true, started: false } # These don't matter, will always be set
|
||||
session = _.find classroom.sessions.models, (session) ->
|
||||
session.get('creator') is userID and session.get('level').original is levelID
|
||||
|
||||
courseProgress[levelID][userID].session = session
|
||||
|
||||
if not session # haven't gotten to this level yet, but might have completed others before
|
||||
courseProgress.started ||= false #no-op
|
||||
|
|
|
@ -1314,6 +1314,9 @@
|
|||
sent_verification: "We've sent a verification email to:"
|
||||
you_can_edit: "You can edit your email address in "
|
||||
account_settings: "Account Settings"
|
||||
select_your_hero: "Select Your Hero"
|
||||
select_your_hero_description: "You can always change your hero by going to your Courses page and clicking \"Select Hero\""
|
||||
select_this_hero: "Select this Hero"
|
||||
|
||||
teacher:
|
||||
teacher_dashboard: "Teacher Dashboard" # Navbar
|
||||
|
|
|
@ -5,8 +5,8 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
no_mobile: "CodeCombat is niet gemaakt voor mobiele apparaten en werkt misschien niet!" # Warning that shows up on mobile devices
|
||||
play: "Speel" # The big play button that opens up the campaign view.
|
||||
play_campaign_version: "Speel de Verhaallijn" # Shows up under big play button if you only play /courses
|
||||
old_browser: "Oh oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari
|
||||
old_browser_suffix: "Je kan toch proberen, maar het zal waarschijnlijk niet werken!"
|
||||
old_browser: "uh-oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" # Warning that shows up on really old Firefox/Chrome/Safari
|
||||
old_browser_suffix: "Je kan alsnog proberen, maar het zal waarschijnlijk niet werken!"
|
||||
ipad_browser: "Slecht nieuws: CodeCombat draait niet in je browser op de iPad. Goed nieuws: onze iPad-app wordt op het moment beoordeeld door Apple."
|
||||
campaign: "Verhaallijn"
|
||||
for_beginners: "Voor Beginners"
|
||||
|
@ -24,25 +24,25 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
im_a_teacher: "Ik ben een leraar"
|
||||
im_a_student: "Ik ben een leerling"
|
||||
learn_more: "Lees verder"
|
||||
classroom_in_a_box: "Een kant-en-klare digitale klas voor programmeerlessen."
|
||||
classroom_in_a_box: "Kant-en-klare programmeerlessen."
|
||||
codecombat_is: "CodeCombat is een platform waarmee leerlingen leren programmeren door het spelen van een spel." # {change}
|
||||
our_courses: "Onze lessen zijn specifiek ontwikkeld voor een klasomgeving, zelfs voor leraren zonder programmeerervaring." # {change}
|
||||
top_screenshots_hint: "Leerlingen schrijven code en zien direct het resultaat van de verandering."
|
||||
top_screenshots_hint: "Leerlingen schrijven code en zien direct resultaat."
|
||||
designed_with: "Gemaakt voor leraren"
|
||||
real_code: "Echte, getypte code"
|
||||
from_the_first_level: "vanaf het eerste level"
|
||||
getting_students: "Leerlingen zo snel mogelijk echte code laten schrijven is noodzakelijk voor het leren van programmeer syntax en correcte structuur."
|
||||
getting_students: "Doordat leerlingen code schrijven in 'echte programmeertaal', leren ze niet alleen hoe computers denken, maar kunnen ze het ook echt toepassen."
|
||||
educator_resources: "Lesbrieven voor docenten"
|
||||
course_guides: "en ondersteuningsmateriaal"
|
||||
teaching_computer_science: "Je hebt geen informatica diploma nodig om te kunnen programmeren, wij verschaffen de materialen waarmee elke docent programmeerles kan geven."
|
||||
accessible_to: "Bereikbaar voor"
|
||||
accessible_to: "Toegankelijk voor"
|
||||
everyone: "iedereen"
|
||||
democratizing: "Programmeerles toegankelijk maken is onze filosofie. Iedereen moet de kans krijgen om te leren programmeren."
|
||||
forgot_learning: "Volgens mij hadden ze niet meer door dat ze eigenlijk bezig waren met leren."
|
||||
wanted_to_do: " Ik wilde altijd al leren programmeren, maar op school was hier nooit aandacht voor."
|
||||
why_games: "Waarom is spelenderwijs leren belangrijk?"
|
||||
games_reward: "Games vergroten de productiviteit."
|
||||
encourage: "Gaming is een middel dat interactie, nieuwschierigheid, en trial-and-error aanmoedigt. Een goed spel daagt de speler uit zijn vaardigheden te perfectioneren, wat hetzelfde noodzakelijke proces is waar leerlingen doorheen gaan wanneer zij iets leren."
|
||||
encourage: "Iedereen wordt geboren als klein onderzoekertje dat de wereld ontdekt door vallen en opstaan. Deze intrinsieke motivatie om te leren ziet men ook terug in een spelomgeving. Voor de speler wordt 'leren' een middel om het spel te winnen, in plaats van een doel op zich."
|
||||
excel: "Games helpen bij de"
|
||||
struggle: "productiviteit-strijd"
|
||||
kind_of_struggle: "het soort worsteling dat uitmondt in een leerproces dat uitdagend is en "
|
||||
|
@ -58,7 +58,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
great_game: "Een goed spel is meer dan alleen medailles en prestaties - het gaat om een reis, nauwkeurig ontworpen puzzels, en de mogelijkheid om uitdagingen vol zelfvertrouwen aan te pakken."
|
||||
agency: "CodeCombat is een game die de mogelijkheid en het zelfvertrouwen geeft om met echte code te werken, wat zowel beginners als gevorderde helpt bij het schrijven van goede, valide code."
|
||||
request_demo_title: "Laat je leerlingen vandaag nog starten!"
|
||||
request_demo_subtitle: "Vraag een demo aan en start binnen een uur met programmeerlessen."
|
||||
request_demo_subtitle: "Vraag een demo aan en start met programmeerlessen."
|
||||
get_started_title: "Maak vandaag nog een klas aan!"
|
||||
get_started_subtitle: "Maak een klas aan, voeg je leerlingen toe, en monitor hun vooruitgang."
|
||||
request_demo: "Vraag een demo aan"
|
||||
|
@ -74,7 +74,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
coming_soon: "Binnenkort beschikbaar!"
|
||||
courses_available_in: "Lessen zijn beschikbaar in JavaScript, Python, en Java (Java is binnenkort beschikbaar!)"
|
||||
boast: "Uitdagende raadsels die zowel gamers als fanatieke programmeurs weten te prikkelen."
|
||||
winning: "Een gouden combinatie van spel-elementen en programmeerhuiswerk, dat samen zorgt voor kind-vriendelijk en oprecht aangenaam onderwijs."
|
||||
winning: "Een gouden combinatie van spel-elementen en programmeerhuiswerk, dat samen zorgt voor kindvriendelijk en oprecht aangenaam onderwijs."
|
||||
run_class: "Alles wat je nodig hebt om vandaag nog programmeerlessen in jouw klas te geven, geen voorkennis vereist."
|
||||
teachers: "Docenten!"
|
||||
teachers_and_educators: "Docenten & Mentoren"
|
||||
|
@ -92,7 +92,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
check_out_wiki: "Bekijk onze nieuwe leraren Wiki"
|
||||
want_coco: "Wil je CodeCombat op jouw school?"
|
||||
form_select_role: "Selecteer je rol"
|
||||
form_select_range: "Selecteer klassengrootte"
|
||||
form_select_range: "Selecteer klasomvang"
|
||||
|
||||
nav:
|
||||
play: "Levels" # The top nav bar entry where players choose which levels to play
|
||||
|
@ -221,19 +221,19 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
"/": "gedeeld door"
|
||||
"+": "plus"
|
||||
"-": "min"
|
||||
# "+=": "add and assign"
|
||||
# "-=": "subtract and assign"
|
||||
"+=": "tel op en wijs toe"
|
||||
"-=": "trek af en wijs toe"
|
||||
True: "Waar"
|
||||
true: "waar"
|
||||
False: "onwaar"
|
||||
false: "onwaar"
|
||||
undefined: "ongedefinieerd"
|
||||
# null: "null"
|
||||
# nil: "nil"
|
||||
null: "nul"
|
||||
nil: "nihil"
|
||||
None: "Geen"
|
||||
|
||||
share_progress_modal:
|
||||
blurb: "Je gaat snel vooruit! Vertel aan je ouders hoeveel je geleerd hebt van CodeCombat."
|
||||
blurb: "Je gaat snel vooruit! Vertel je ouders hoeveel je geleerd hebt van CodeCombat."
|
||||
email_invalid: "E-mailadres klopt niet."
|
||||
form_blurb: "Vul het e-mailadres van je ouders hieronder in en we zullen het ze laten zien!"
|
||||
form_label: "E-mailadres"
|
||||
|
@ -472,19 +472,19 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
tip_reticulating: "Paden aan het verknopen."
|
||||
tip_harry: "Je bent een tovenaar, "
|
||||
tip_great_responsibility: "Met een groots talent voor programmeren komt een grootse debug verantwoordelijkheid."
|
||||
tip_munchkin: "Als je je groentjes niet opeet zal een munchkin je ontvoeren terwijl je slaapt."
|
||||
tip_munchkin: "Als je je groenten niet opeet zal een munchkin je ontvoeren terwijl je slaapt."
|
||||
tip_binary: "Er zijn 10 soorten mensen in de wereld: Mensen die binair kunnen tellen en mensen die dat niet kunnen."
|
||||
tip_commitment_yoda: "Een programmeur moet de grootste inzet hebben, een meest serieuze geest. ~ Yoda"
|
||||
tip_no_try: "Doe het. Of doe het niet. Je kunt niet proberen. - Yoda"
|
||||
tip_patience: "Geduld moet je hebben, jonge Padawan. - Yoda"
|
||||
tip_documented_bug: "Een gedocumenteerde fout is geen fout; het is deel van het programma."
|
||||
tip_impossible: "Het lijkt altijd onmogelijk tot het gedaan wordt. - Nelson Mandela"
|
||||
tip_impossible: "Het lijkt altijd onmogelijk totdat iemand het doet. - Nelson Mandela"
|
||||
tip_talk_is_cheap: "Je kunt het goed uitleggen, maar toon me de code. - Linus Torvalds"
|
||||
tip_first_language: "Het ergste dat je kan leren is je eerste programmeertaal. - Alan Kay"
|
||||
tip_hardware_problem: "Q: Hoeveel programmeurs heb je nodig om een lampje te vervangen? A: Nul, het is een a hardware probleem."
|
||||
tip_hofstadters_law: "De Wet van Hofstadter: Het duurt altijd langer dan je verwacht, zelfs wanneer je rekening houdt met de Wet van Hofstadter."
|
||||
tip_premature_optimization: "vroegtijdig optimaliseren is de wortel van al het kwaad. - Donald Knuth"
|
||||
tip_brute_force: "Wanneer je twijfelt, gebruik brute force. - Ken Thompson"
|
||||
tip_brute_force: "Wanneer je twijfelt, gebruik dan brute force. - Ken Thompson"
|
||||
tip_extrapolation: "Er zijn twee soorten mensen: Zij die iets kunnen afleiden van onvolledige gegevens..."
|
||||
tip_superpower: "Van alle dingen komt programmeren het dichtst in de buurt van een superkracht."
|
||||
tip_control_destiny: "In echte open source, hebt je het recht om je eigen toekomst te bepalen. - Linus Torvalds"
|
||||
|
@ -820,8 +820,8 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
teachers_quote:
|
||||
name: "Demo Formulier"
|
||||
title: "Demo aanvragen"
|
||||
subtitle: "Haal CodeCombat jouw klaslokaal of club!"
|
||||
email_exists: "Er bestaat al een gebruiker met dit email adres."
|
||||
subtitle: "Gebruik CodeCombat voor jouw klas of programmeerclub!"
|
||||
email_exists: "Er bestaat al een gebruiker met dit emailadres."
|
||||
phone_number: "Telefoonnummer"
|
||||
phone_number_help: "Waarop kunnen we je bereiken tijdens kantooruren?"
|
||||
primary_role_label: "Uw primaire rol"
|
||||
|
|
|
@ -686,21 +686,21 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
|
||||
# read_only: "read-only"
|
||||
action: "Aкција"
|
||||
# spell: "Spell"
|
||||
# action_name: "name"
|
||||
# action_cooldown: "Takes"
|
||||
# action_specific_cooldown: "Cooldown"
|
||||
# action_damage: "Damage"
|
||||
# action_range: "Range"
|
||||
# action_radius: "Radius"
|
||||
# action_duration: "Duration"
|
||||
# example: "Example"
|
||||
# ex: "ex" # Abbreviation of "example"
|
||||
# current_value: "Current Value"
|
||||
# default_value: "Default value"
|
||||
# parameters: "Parameters"
|
||||
# returns: "Returns"
|
||||
# granted_by: "Granted by"
|
||||
spell: "Магија"
|
||||
action_name: "име"
|
||||
action_cooldown: "Потребно"
|
||||
action_specific_cooldown: "Хлађење"
|
||||
action_damage: "Штета"
|
||||
action_range: "Домет"
|
||||
action_radius: "Опсег"
|
||||
action_duration: "Трајање"
|
||||
example: "Пример"
|
||||
ex: "нпр." # Abbreviation of "example"
|
||||
current_value: "Тренутна вредност"
|
||||
default_value: "Подразумевана вредност"
|
||||
parameters: "Параметри"
|
||||
returns: "Враћа"
|
||||
granted_by: "Додељено од"
|
||||
|
||||
save_load:
|
||||
granularity_saved_games: "Сачувано"
|
||||
|
@ -860,12 +860,12 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
finish_signup_p: "Направи налог да оснујеш разред, додаш своје ученике и пратиш њихов напредак док уче компјутерске науке."
|
||||
signup_with: "Пријави се са:"
|
||||
connect_with: "Повежи се са:"
|
||||
# conversion_warning: "WARNING: Your current account is a <em>Student Account</em>. Once you submit this form, your account will be updated to a Teacher Account."
|
||||
# learn_more_modal: "Teacher accounts on CodeCombat have the ability to monitor student progress, assign enrollments and manage classrooms. Teacher accounts cannot be a part of a classroom - if you are currently enrolled in a class using this account, you will no longer be able to access it once you update to a Teacher Account."
|
||||
conversion_warning: "УПОЗОРЕЊЕ: Твој тренутни налог је <em>Студентски Налог</em>. Након што пошаљеш овај формулар, твој налог ће бити надограђен у Учитељски Налог."
|
||||
learn_more_modal: "Учитељски налози на CodeCombat-у имају могућност посматрања напретка ученика, додељивања уписа и управљања учионицама. Учитељски налози не могу бити део учионице - ако си тренутно уписан у разред преко овог налога, нећеш више моћи да му приступиш кад ажурираш у Учитељски Налог."
|
||||
create_account: "Направи учитељски налог"
|
||||
# create_account_subtitle: "Get access to teacher-only tools for using CodeCombat in the classroom. <strong>Set up a class</strong>, add your students, and <strong>monitor their progress</strong>!"
|
||||
# convert_account_title: "Update to Teacher Account"
|
||||
# not: "Not"
|
||||
create_account_subtitle: "Добиј приступ алатима само за учитеље за коришћење CodeCombat-а у учионици. <strong>Подеси разред</strong>, додај своје ученике, и <strong>посматрај њихов напредак</strong>!"
|
||||
convert_account_title: "Ажурирај у Учитељски Налог"
|
||||
not: "Није"
|
||||
setup_a_class: "Подеси разред"
|
||||
|
||||
versions:
|
||||
|
@ -880,18 +880,18 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
|
||||
contact:
|
||||
contact_us: "Контактирај CodeCombat"
|
||||
welcome: "Драго нам је што нас контактираш! Искористи ову форму да нам пошаљеш мејл. "
|
||||
welcome: "Драго нам је што нас контактираш! Искористи овај формулар да нам пошаљеш мејл. "
|
||||
forum_prefix: "За било шта јавно, посети "
|
||||
forum_page: "наш форум."
|
||||
# forum_suffix: " instead."
|
||||
# faq_prefix: "There's also a"
|
||||
# faq: "FAQ"
|
||||
# subscribe_prefix: "If you need help figuring out a level, please"
|
||||
# subscribe: "buy a CodeCombat subscription"
|
||||
# subscribe_suffix: "and we'll be happy to help you with your code."
|
||||
# subscriber_support: "Since you're a CodeCombat subscriber, your email will get our priority support."
|
||||
# screenshot_included: "Screenshot included."
|
||||
# where_reply: "Where should we reply?"
|
||||
forum_page: "наш форум"
|
||||
forum_suffix: " уместо тога."
|
||||
faq_prefix: "Такође, ту је"
|
||||
faq: "FAQ"
|
||||
subscribe_prefix: "Ако ти треба помоћ да разумеш ниво, молимо да"
|
||||
subscribe: "купиш CodeCombat претплату"
|
||||
subscribe_suffix: "и радо ћемо ти помоћи у твом коду."
|
||||
subscriber_support: "Пошто си CodeCombat претплатник, твој мејл ће имати приоритет у нашој подршци."
|
||||
screenshot_included: "Снимак екрана укључен."
|
||||
where_reply: "Где треба да одговоримо?"
|
||||
send: "Пошаљи повратну информацију"
|
||||
|
||||
account_settings:
|
||||
|
@ -910,24 +910,24 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# god_mode: "God Mode"
|
||||
password_tab: "Шифра"
|
||||
emails_tab: "Мејлови"
|
||||
# admin: "Admin"
|
||||
admin: "Администратор"
|
||||
manage_subscription: "Кликни овде да би управљао својом претплатом."
|
||||
new_password: "Нова Шифра"
|
||||
new_password_verify: "Потврди"
|
||||
# type_in_email: "Type in your email to confirm account deletion."
|
||||
# type_in_email_progress: "Type in your email to confirm deleting your progress."
|
||||
# type_in_password: "Also, type in your password."
|
||||
type_in_email: "Упиши свој мејл да потврдиш брисање налога."
|
||||
type_in_email_progress: "Упиши свој мејл да потврдиш брисање свог напретка."
|
||||
type_in_password: "Такође, упиши своју шифру."
|
||||
email_subscriptions: "Мејл претплате"
|
||||
# email_subscriptions_none: "No Email Subscriptions."
|
||||
email_subscriptions_none: "Без мејл претплата."
|
||||
email_announcements: "Обавештења"
|
||||
email_announcements_description: "Прими мејл за најновије вести и достигнућа на CodeCombat-у"
|
||||
# email_notifications: "Notifications"
|
||||
# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity."
|
||||
# email_any_notes: "Any Notifications"
|
||||
# email_any_notes_description: "Disable to stop all activity notification emails."
|
||||
email_notifications: "Обавештења"
|
||||
email_notifications_summary: "Контроле за персонализована, аутоматска мејл обавештења вазана за твоју CodeCombat активност."
|
||||
email_any_notes: "Сва обавештења"
|
||||
email_any_notes_description: "Онемогући да би прекинуо сва мејл обавештења о активности."
|
||||
email_news: "Вести"
|
||||
email_recruit_notes: "Пословне могућности"
|
||||
# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job."
|
||||
email_recruit_notes_description: "Ако играш јако добро, можда ћемо те контактирати о томе да добијеш (бољи) посао."
|
||||
contributor_emails: "Мејлови реда сарадника"
|
||||
contribute_prefix: "Тражимо људе који би нам се придружили! Погледај "
|
||||
contribute_page: "страницу за сарадњу"
|
||||
|
@ -961,13 +961,13 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
|
||||
community:
|
||||
main_title: "CodeCombat Заједница"
|
||||
# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!"
|
||||
# level_editor_prefix: "Use the CodeCombat"
|
||||
# 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!"
|
||||
# thang_editor_prefix: "We call units within the game 'thangs'. Use the"
|
||||
# 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."
|
||||
# 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_suffix: "and help CodeCombat players get the most out of their playtime."
|
||||
introduction: "Погледај испод како можеш да се укључиш и одлучи шта звучи најзанимљивије. Радујемо се прилици да радимо са тобом!"
|
||||
level_editor_prefix: "Користи CodeCombat"
|
||||
level_editor_suffix: "да правиш и уређујеш нивое. Корисници су направили нивое за њихове разреде, пријатеље, хакатоне, ученике и браћу и сестре. Ако прављење новог нивоа звучи застрашујуће, можеш да почнеш форковањем једног од наших!"
|
||||
thang_editor_prefix: "Ми зовемо јединице у игри 'thangs'. Користи"
|
||||
thang_editor_suffix: "да модификујеш CodeCombat изворне илустрације. Дозволи јединицама да бацају пројектиле, измени дирекцију анимације, промени хит поене јединице или отпреми сопствене векторске спрајтове."
|
||||
article_editor_prefix: "Видиш грешку у неком од наших докумената? Желиш да направиш инструкције за сопствене креације? Погледај"
|
||||
article_editor_suffix: "и помози CodeCombat играчима да добију највише од свог играња."
|
||||
find_us: "Нађи нас на овим сајтовима"
|
||||
social_github: "Погледај цео наш код на GitHub-у"
|
||||
social_blog: "Читај CodeCombat блог на Sett-у"
|
||||
|
@ -986,57 +986,57 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
make_private: "Направи клан приватним"
|
||||
subs_only: "само за претплатнике"
|
||||
create_clan: "Направи нови клан"
|
||||
private_preview: "Preview"
|
||||
private_preview: "Приказ"
|
||||
private_clans: "Приватни кланови"
|
||||
public_clans: "Јавни кланови"
|
||||
my_clans: "Моји кланови"
|
||||
clan_name: "Име клана"
|
||||
name: "Име"
|
||||
# chieftain: "Chieftain"
|
||||
# type: "Type"
|
||||
chieftain: "Поглавица"
|
||||
type: "Врста"
|
||||
edit_clan_name: "Измени име клана"
|
||||
edit_clan_description: "Измени опис клана"
|
||||
edit_name: "измени име"
|
||||
edit_description: "измени опис"
|
||||
private: "(приватан)"
|
||||
# summary: "Summary"
|
||||
# average_level: "Average Level"
|
||||
# average_achievements: "Average Achievements"
|
||||
summary: "Преглед"
|
||||
average_level: "Просечни ниво"
|
||||
average_achievements: "Просечна достигнућа"
|
||||
delete_clan: "Избриши клан"
|
||||
leave_clan: "Напусти клан"
|
||||
join_clan: "Придружи се клану"
|
||||
invite_1: "Позови:"
|
||||
# invite_2: "*Invite players to this Clan by sending them this link."
|
||||
invite_2: "*Позови играче у овај Клан тако што ћеш им послати овај линк."
|
||||
members: "Чланови"
|
||||
progress: "Напредак"
|
||||
# not_started_1: "not started"
|
||||
# started_1: "started"
|
||||
# complete_1: "complete"
|
||||
# exp_levels: "Expand levels"
|
||||
# rem_hero: "Remove Hero"
|
||||
not_started_1: "није започето"
|
||||
started_1: "започето"
|
||||
complete_1: "заврши"
|
||||
exp_levels: "Прошири нивое"
|
||||
rem_hero: "Уклони Хероја"
|
||||
status: "Статус"
|
||||
# complete_2: "Complete"
|
||||
# started_2: "Started"
|
||||
# not_started_2: "Not Started"
|
||||
# view_solution: "Click to view solution."
|
||||
# view_attempt: "Click to view attempt."
|
||||
# latest_achievement: "Latest Achievement"
|
||||
# playtime: "Playtime"
|
||||
# last_played: "Last played"
|
||||
# leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances."
|
||||
# track_concepts1: "Track concepts"
|
||||
# track_concepts2a: "learned by each student"
|
||||
# track_concepts2b: "learned by each member"
|
||||
# track_concepts3a: "Track levels completed for each student"
|
||||
# track_concepts3b: "Track levels completed for each member"
|
||||
# track_concepts4a: "See your students'"
|
||||
# track_concepts4b: "See your members'"
|
||||
# track_concepts5: "solutions"
|
||||
# track_concepts6a: "Sort students by name or progress"
|
||||
# track_concepts6b: "Sort members by name or progress"
|
||||
# track_concepts7: "Requires invitation"
|
||||
# track_concepts8: "to join"
|
||||
# private_require_sub: "Private clans require a subscription to create or join."
|
||||
complete_2: "Заврши"
|
||||
started_2: "Започето"
|
||||
not_started_2: "Није започето"
|
||||
view_solution: "Кликни да видиш решење."
|
||||
view_attempt: "Кликни да видиш покушај."
|
||||
latest_achievement: "Последње достигнуће"
|
||||
playtime: "Време игања"
|
||||
last_played: "Последњи пут играно"
|
||||
leagues_explanation: "Играј у лиги против других чланова клана у овим мултиплејер инстанцама арене."
|
||||
track_concepts1: "Прати концепте"
|
||||
track_concepts2a: "научене од сваког ученика"
|
||||
track_concepts2b: "научене од сваког члана"
|
||||
track_concepts3a: "Прати завршене нивое за сваког ученика"
|
||||
track_concepts3b: "Прати завршене нивое за сваког члана"
|
||||
track_concepts4a: "Види од својих ученика"
|
||||
track_concepts4b: "Види од својих чланова"
|
||||
track_concepts5: "решења"
|
||||
track_concepts6a: "Сортирај ученике према имену или напретку"
|
||||
track_concepts6b: "Сортирај чланове према имену или напретку"
|
||||
track_concepts7: "Захтева позив"
|
||||
track_concepts8: "за придруживање"
|
||||
private_require_sub: "Приватни кланови захтевају претплату да би могао да их направиш или да им се придружиш."
|
||||
|
||||
# courses:
|
||||
# course: "Course"
|
||||
|
@ -1488,12 +1488,12 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# add_system_title: "Add Systems to Level"
|
||||
# done_adding: "Done Adding"
|
||||
|
||||
# article:
|
||||
# edit_btn_preview: "Preview"
|
||||
# edit_article_title: "Edit Article"
|
||||
article:
|
||||
edit_btn_preview: "Приказ"
|
||||
edit_article_title: "Измени Чланак"
|
||||
|
||||
# polls:
|
||||
# priority: "Priority"
|
||||
polls:
|
||||
priority: "Приоритет"
|
||||
|
||||
# contribute:
|
||||
# page_title: "Contributing"
|
||||
|
@ -1645,17 +1645,17 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# favorite_postfix: "."
|
||||
# not_member_of_clans: "Not a member of any clans yet."
|
||||
|
||||
# achievements:
|
||||
# last_earned: "Last Earned"
|
||||
# amount_achieved: "Amount"
|
||||
# achievement: "Achievement"
|
||||
# current_xp_prefix: ""
|
||||
# current_xp_postfix: " in total"
|
||||
# new_xp_prefix: ""
|
||||
# new_xp_postfix: " earned"
|
||||
# left_xp_prefix: ""
|
||||
# left_xp_infix: " until level "
|
||||
# left_xp_postfix: ""
|
||||
achievements:
|
||||
last_earned: "Последње стечено"
|
||||
amount_achieved: "Количина"
|
||||
achievement: "Достигнуће"
|
||||
current_xp_prefix: ""
|
||||
current_xp_postfix: " укупно"
|
||||
new_xp_prefix: ""
|
||||
new_xp_postfix: " стечено"
|
||||
left_xp_prefix: ""
|
||||
left_xp_infix: " до нивоа "
|
||||
left_xp_postfix: ""
|
||||
|
||||
account:
|
||||
payments: "Уплате"
|
||||
|
|
|
@ -368,6 +368,7 @@ class CocoModel extends Backbone.Model
|
|||
return if _.isString @url then @url else @url()
|
||||
|
||||
@pollAchievements: ->
|
||||
return if application.testing
|
||||
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
EarnedAchievement = require 'models/EarnedAchievement'
|
||||
|
|
|
@ -145,6 +145,11 @@ module.exports = class Level extends CocoModel
|
|||
for original, placeholderComponent of placeholders when not placeholdersUsed[original]
|
||||
levelThang.components.push placeholderComponent
|
||||
|
||||
# Load the user's chosen hero AFTER getting stats from default char
|
||||
if /Hero Placeholder/.test(levelThang.id) and @get('type', true) in ['course', 'course-ladder']
|
||||
heroThangType = me.get('heroConfig')?.thangType
|
||||
levelThang.thangType = heroThangType if heroThangType
|
||||
|
||||
sortSystems: (levelSystems, systemModels) ->
|
||||
[sorted, originalsSeen] = [[], {}]
|
||||
visit = (system) ->
|
||||
|
|
|
@ -239,6 +239,25 @@ module.exports = class ThangType extends CocoModel
|
|||
portraitOnly = !!options.portraitOnly
|
||||
"#{@get('name')} - #{options.resolutionFactor} - #{colorConfigs} - #{portraitOnly}"
|
||||
|
||||
getHeroShortName: ->
|
||||
map = {
|
||||
"Assassin": "Ritic"
|
||||
"Captain": "Anya"
|
||||
"Forest Archer": "Naria"
|
||||
"Goliath": "Okar"
|
||||
"Guardian": "Illia"
|
||||
"Knight": "Tharin"
|
||||
"Librarian": "Hushbaum"
|
||||
"Necromancer": "Nalfar"
|
||||
"Ninja": "Amara"
|
||||
"Potion Master": "Omarn"
|
||||
"Raider": "Arryn"
|
||||
"Samurai": "Hattori"
|
||||
"Sorcerer": "Pender"
|
||||
"Trapper": "Senick"
|
||||
}
|
||||
map[@get('name')]
|
||||
|
||||
getPortraitImage: (spriteOptionsOrKey, size=100) ->
|
||||
src = @getPortraitSource(spriteOptionsOrKey, size)
|
||||
return null unless src
|
||||
|
|
|
@ -41,3 +41,22 @@
|
|||
#join-class-form
|
||||
.alert, .progress
|
||||
margin-top: 20px
|
||||
|
||||
// Hero display
|
||||
.current-hero-container
|
||||
display: flex
|
||||
justify-content: center
|
||||
|
||||
.current-hero-text
|
||||
font-size: 16pt
|
||||
|
||||
.hero-avatar
|
||||
background-color: #f8f8f8
|
||||
box-shadow: 0 0 0 1px gray
|
||||
margin-right: 25px
|
||||
|
||||
.current-hero-right-col
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: space-between
|
||||
align-items: flex-start
|
||||
|
|
40
app/styles/courses/hero-select-modal.sass
Normal file
40
app/styles/courses/hero-select-modal.sass
Normal file
|
@ -0,0 +1,40 @@
|
|||
@import "app/styles/style-flat-variables"
|
||||
|
||||
#hero-select-modal
|
||||
.modal-dialog
|
||||
width: auto
|
||||
max-width: 900px
|
||||
|
||||
.modal-header, .modal-body:not(.secret), .modal-footer
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
|
||||
.modal-footer
|
||||
margin: 30px
|
||||
|
||||
h4
|
||||
max-width: 500px
|
||||
|
||||
.hero-list
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
justify-content: center
|
||||
margin-bottom: -50px
|
||||
|
||||
.hero-option
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
margin: 0 50px 50px
|
||||
|
||||
.hero-avatar
|
||||
margin: 6px
|
||||
background-color: #f8f8f8
|
||||
box-shadow: 0 0 0 1px gray
|
||||
|
||||
.current .hero-avatar
|
||||
box-shadow: 0 0 0 6px gray
|
||||
|
||||
.selected .hero-avatar
|
||||
box-shadow: 0 0 0 6px $gold
|
|
@ -7,3 +7,7 @@
|
|||
font-family: Arial, Geneva, sans-serif
|
||||
padding: 20px
|
||||
font-weight: bold
|
||||
|
||||
.alert-report
|
||||
font-size: 20px
|
||||
|
|
@ -39,6 +39,18 @@ block content
|
|||
|
||||
.text-center
|
||||
h1(data-i18n="courses.welcome_to_page") Welcome to your Courses page!
|
||||
|
||||
.current-hero-container.text-center.row
|
||||
.hero-avatar
|
||||
img(src=view.hero.getPortraitURL())
|
||||
.current-hero-right-col
|
||||
.semibold.current-hero-text
|
||||
span.spr(data-i18n="TODO")
|
||||
| Current Hero:
|
||||
span.current-hero-name= view.hero.getHeroShortName()
|
||||
button.change-hero-btn.btn.btn-lg.btn-forest
|
||||
span(data-i18n="TODO")
|
||||
| Change Hero
|
||||
|
||||
if view.classrooms.size()
|
||||
h3.text-uppercase(data-i18n="courses.my_classes")
|
||||
|
|
27
app/templates/courses/hero-select-modal.jade
Normal file
27
app/templates/courses/hero-select-modal.jade
Normal file
|
@ -0,0 +1,27 @@
|
|||
extends /templates/core/modal-base-flat
|
||||
|
||||
block modal-header-content
|
||||
.text-center
|
||||
h3(data-i18n="courses.select_your_hero")
|
||||
h4(data-i18n="courses.select_your_hero_description")
|
||||
|
||||
block modal-body-content
|
||||
.hero-list
|
||||
if view.heroes.loaded
|
||||
each hero in view.heroes.models
|
||||
if hero.get('heroClass') === 'Warrior'
|
||||
+heroOption(hero)
|
||||
|
||||
mixin heroOption(hero)
|
||||
- var heroID = hero.id
|
||||
- var selectedState = (state.get('selectedHeroID') === heroID ? 'selected' : (state.get('currentHeroID') === heroID ? 'current' : ''))
|
||||
.hero-option(data-hero-id=heroID class=selectedState)
|
||||
.hero-avatar
|
||||
img(src=hero.getPortraitURL())
|
||||
.text-h5.hero-name
|
||||
span= hero.getHeroShortName()
|
||||
|
||||
block modal-footer-content
|
||||
.select-hero-btn.btn.btn-lg.btn-forest
|
||||
span(data-i18n="courses.select_this_hero")
|
||||
|
|
@ -321,7 +321,7 @@ mixin studentLevelsRow(student)
|
|||
- var levels = view.classroom.getLevels({courseID: course.id, withoutLadderLevels: true}).models
|
||||
each level, index in levels
|
||||
- var progress = state.get('progressData').get({ classroom: view.classroom, course: course, level: level, user: student })
|
||||
+studentLevelProgressDot(progress, level, index+1)
|
||||
+studentLevelProgressDot(progress, level, index+1, session)
|
||||
|
||||
mixin studentCourseProgressDot(progress, levelsTotal, level, label)
|
||||
//- TODO: Refactor with TeacherClassesView jade
|
||||
|
@ -342,7 +342,7 @@ mixin studentLevelProgressDot(progress, level, levelNumber)
|
|||
//- TODO: Refactor with TeacherClassesView jade
|
||||
- dotClass = progress.completed ? 'forest' : (progress.started ? 'gold' : '');
|
||||
- levelName = level.get('name')
|
||||
- context = _.merge(progress, { levelName: levelName, levelNumber: levelNumber })
|
||||
- context = _.merge(progress, { levelName: levelName, levelNumber: levelNumber, moment: moment })
|
||||
.progress-dot.level-progress-dot(class=dotClass, data-html='true', data-title=view.singleStudentLevelProgressDotTemplate(context))
|
||||
+progressDotLabel(levelNumber)
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
mixin timePlayed()
|
||||
if session.get('playtime') > 0
|
||||
.small-details.nowrap
|
||||
span.spr(data-i18n='teacher.time_played')
|
||||
| Played for
|
||||
span= moment.duration({ seconds: session.get('playtime') }).humanize()
|
||||
|
||||
if completed
|
||||
.small-details.nowrap
|
||||
span= levelNumber
|
||||
|
@ -7,6 +14,7 @@ if completed
|
|||
span.spr(data-i18n='teacher.completed')
|
||||
| Completed
|
||||
span= new Date(dateFirstCompleted).toLocaleString()
|
||||
+timePlayed
|
||||
//- .small-details
|
||||
//- i(data-i18n='teacher.click_to_view_solution')
|
||||
//- | click to view solution
|
||||
|
@ -19,6 +27,7 @@ else if started
|
|||
span.spr(data-i18n='teacher.last_played')
|
||||
| Last played
|
||||
span= new Date(lastPlayed).toLocaleString()
|
||||
+timePlayed
|
||||
//- .small-details
|
||||
//- i(data-i18n='teacher.click_to_view_progress')
|
||||
//- | click to view progress
|
||||
|
|
|
@ -11,6 +11,20 @@ ol.breadcrumb
|
|||
.container-fluid
|
||||
.row
|
||||
.col-md-8
|
||||
#failure-reports
|
||||
for report in view.failureReports
|
||||
.alert.alert-danger.alert-report
|
||||
ul.suite-list
|
||||
for description in report.suiteDescriptions
|
||||
li= description
|
||||
li
|
||||
strong ... #{report.testDescription}
|
||||
hr
|
||||
ol.error-list
|
||||
for message in report.failMessages
|
||||
li
|
||||
strong= message
|
||||
|
||||
#test-wrapper.well
|
||||
#testing-area
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = TestView = class TestView extends RootView
|
|||
id: 'test-view'
|
||||
template: template
|
||||
reloadOnClose: true
|
||||
loadedFileIDs: []
|
||||
className: 'style-flat'
|
||||
|
||||
events:
|
||||
'click #show-demos-btn': 'onClickShowDemosButton'
|
||||
|
@ -24,11 +24,13 @@ module.exports = TestView = class TestView extends RootView
|
|||
initialize: (options, @subPath='') ->
|
||||
@subPath = @subPath[1..] if @subPath[0] is '/'
|
||||
@demosOn = storage.load('demos-on')
|
||||
@failureReports = []
|
||||
@loadedFileIDs = []
|
||||
|
||||
afterInsert: ->
|
||||
@initSpecFiles()
|
||||
@render()
|
||||
TestView.runTests(@specFiles, @demosOn)
|
||||
TestView.runTests(@specFiles, @demosOn, @)
|
||||
window.runJasmine()
|
||||
|
||||
# EVENTS
|
||||
|
@ -59,7 +61,30 @@ module.exports = TestView = class TestView extends RootView
|
|||
prefix = TEST_REQUIRE_PREFIX + @subPath
|
||||
@specFiles = (f for f in @specFiles when _.string.startsWith f, prefix)
|
||||
|
||||
@runTests: (specFiles, demosOn=false) ->
|
||||
@runTests: (specFiles, demosOn=false, view) ->
|
||||
|
||||
jasmine.getEnv().addReporter({
|
||||
suiteStack: []
|
||||
|
||||
specDone: (result) ->
|
||||
if result.status is 'failed'
|
||||
console.log 'result', result
|
||||
report = {
|
||||
suiteDescriptions: _.clone(@suiteStack)
|
||||
failMessages: (fe.message for fe in result.failedExpectations)
|
||||
testDescription: result.description
|
||||
}
|
||||
view.failureReports.push(report)
|
||||
view.renderSelectors('#failure-reports')
|
||||
|
||||
suiteStarted: (result) ->
|
||||
@suiteStack.push(result.description)
|
||||
|
||||
suiteDone: (result) ->
|
||||
@suiteStack.pop()
|
||||
|
||||
})
|
||||
|
||||
application.testing = true
|
||||
specFiles ?= @getAllSpecFiles()
|
||||
if demosOn
|
||||
|
@ -71,23 +96,22 @@ module.exports = TestView = class TestView extends RootView
|
|||
jasmine.demoEl = _.noop
|
||||
jasmine.demoModal = _.noop
|
||||
|
||||
describe 'CodeCombat Client', =>
|
||||
jasmine.Ajax.install()
|
||||
beforeEach ->
|
||||
jasmine.Ajax.requests.reset()
|
||||
Backbone.Mediator.init()
|
||||
Backbone.Mediator.setValidationEnabled false
|
||||
spyOn(application.tracker, 'trackEvent')
|
||||
# TODO Stubbify more things
|
||||
# * document.location
|
||||
# * firebase
|
||||
# * all the services that load in main.html
|
||||
jasmine.Ajax.install()
|
||||
beforeEach ->
|
||||
jasmine.Ajax.requests.reset()
|
||||
Backbone.Mediator.init()
|
||||
Backbone.Mediator.setValidationEnabled false
|
||||
spyOn(application.tracker, 'trackEvent')
|
||||
# TODO Stubbify more things
|
||||
# * document.location
|
||||
# * firebase
|
||||
# * all the services that load in main.html
|
||||
|
||||
afterEach ->
|
||||
# TODO Clean up more things
|
||||
# * Events
|
||||
afterEach ->
|
||||
# TODO Clean up more things
|
||||
# * Events
|
||||
|
||||
require f for f in specFiles # runs the tests
|
||||
require f for f in specFiles # runs the tests
|
||||
|
||||
@getAllSpecFiles = ->
|
||||
allFiles = window.require.list()
|
||||
|
|
|
@ -4,6 +4,7 @@ template = require 'templates/courses/courses-view'
|
|||
AuthModal = require 'views/core/AuthModal'
|
||||
CreateAccountModal = require 'views/core/CreateAccountModal'
|
||||
ChangeCourseLanguageModal = require 'views/courses/ChangeCourseLanguageModal'
|
||||
HeroSelectModal = require 'views/courses/HeroSelectModal'
|
||||
ChooseLanguageModal = require 'views/courses/ChooseLanguageModal'
|
||||
JoinClassModal = require 'views/courses/JoinClassModal'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
|
@ -13,6 +14,7 @@ Classroom = require 'models/Classroom'
|
|||
Classrooms = require 'collections/Classrooms'
|
||||
LevelSession = require 'models/LevelSession'
|
||||
Campaign = require 'models/Campaign'
|
||||
ThangType = require 'models/ThangType'
|
||||
utils = require 'core/utils'
|
||||
|
||||
# TODO: Test everything
|
||||
|
@ -24,6 +26,7 @@ module.exports = class CoursesView extends RootView
|
|||
events:
|
||||
'click #log-in-btn': 'onClickLogInButton'
|
||||
'click #start-new-game-btn': 'openSignUpModal'
|
||||
'click .change-hero-btn': 'onClickChangeHeroButton'
|
||||
'click #join-class-btn': 'onClickJoinClassButton'
|
||||
'submit #join-class-form': 'onSubmitJoinClassForm'
|
||||
'click #change-language-link': 'onClickChangeLanguageLink'
|
||||
|
@ -43,6 +46,16 @@ module.exports = class CoursesView extends RootView
|
|||
@courses = new CocoCollection([], { url: "/db/course", model: Course})
|
||||
@supermodel.loadCollection(@courses)
|
||||
|
||||
# TODO: Trim this section for only what's necessary
|
||||
@hero = new ThangType
|
||||
defaultHeroOriginal = ThangType.heroes.captain
|
||||
heroOriginal = me.get('heroConfig')?.thangType or defaultHeroOriginal
|
||||
@hero.url = "/db/thang.type/#{heroOriginal}/version"
|
||||
# @hero.setProjection ['name','slug','soundTriggers','featureImages','gems','heroClass','description','components','extendedName','unlockLevelName','i18n']
|
||||
@supermodel.loadModel(@hero, 'hero')
|
||||
@listenTo @hero, 'all', ->
|
||||
@render()
|
||||
|
||||
onCourseInstancesLoaded: ->
|
||||
map = {}
|
||||
for courseInstance in @courseInstances.models
|
||||
|
@ -76,6 +89,16 @@ module.exports = class CoursesView extends RootView
|
|||
@openModalView(modal)
|
||||
application.tracker?.trackEvent 'Started Student Signup', category: 'Courses'
|
||||
|
||||
onClickChangeHeroButton: ->
|
||||
modal = new HeroSelectModal({ currentHeroID: @hero.id })
|
||||
@openModalView(modal)
|
||||
@listenTo modal, 'hero-select:success', (newHero) =>
|
||||
# @hero.url = "/db/thang.type/#{me.get('heroConfig').thangType}/version"
|
||||
# @hero.fetch()
|
||||
@hero.set(newHero.attributes)
|
||||
@listenTo modal, 'hide', ->
|
||||
@stopListening modal
|
||||
|
||||
onSubmitJoinClassForm: (e) ->
|
||||
e.preventDefault()
|
||||
@joinClass()
|
||||
|
@ -136,7 +159,7 @@ module.exports = class CoursesView extends RootView
|
|||
classroomCourseInstances.fetch({ data: {classroomID: newClassroom.id} })
|
||||
@listenToOnce classroomCourseInstances, 'sync', ->
|
||||
# TODO: Smoother system for joining a classroom and course instances, without requiring page reload,
|
||||
# and showing which class was just joined.
|
||||
# and showing which class was just joined.
|
||||
document.location.search = '' # Using document.location.reload() causes an infinite loop of reloading
|
||||
|
||||
onClickChangeLanguageLink: ->
|
||||
|
|
42
app/views/courses/HeroSelectModal.coffee
Normal file
42
app/views/courses/HeroSelectModal.coffee
Normal file
|
@ -0,0 +1,42 @@
|
|||
ModalView = require 'views/core/ModalView'
|
||||
template = require 'templates/courses/hero-select-modal'
|
||||
Classroom = require 'models/Classroom'
|
||||
ThangTypes = require 'collections/ThangTypes'
|
||||
State = require 'models/State'
|
||||
ThangType = require 'models/ThangType'
|
||||
User = require 'models/User'
|
||||
|
||||
module.exports = class HeroSelectModal extends ModalView
|
||||
id: 'hero-select-modal'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .select-hero-btn': 'onClickSelectHeroButton'
|
||||
'click .hero-option': 'onClickHeroOption'
|
||||
|
||||
initialize: ({ currentHeroID }) ->
|
||||
@debouncedRender = _.debounce @render, 0
|
||||
|
||||
@state = new State({
|
||||
currentHeroID
|
||||
selectedHeroID: currentHeroID
|
||||
})
|
||||
|
||||
@heroes = new ThangTypes({}, { project: ['original', 'name', 'heroClass'] })
|
||||
@supermodel.trackRequest @heroes.fetchHeroes()
|
||||
|
||||
@listenTo @state, 'all', -> @debouncedRender()
|
||||
@listenTo @heroes, 'all', -> @debouncedRender()
|
||||
|
||||
onClickHeroOption: (e) ->
|
||||
heroID = $(e.currentTarget).data('hero-id')
|
||||
@state.set selectedHeroID: heroID
|
||||
hero = @heroes.get(heroID)
|
||||
me.set(heroConfig: {}) unless me.get('heroConfig')
|
||||
heroConfig = _.assign me.get('heroConfig'), { thangType: hero.get('original') }
|
||||
me.set({ heroConfig })
|
||||
me.save().then =>
|
||||
@trigger 'hero-select:success', hero
|
||||
|
||||
onClickSelectHeroButton: () ->
|
||||
@hide()
|
|
@ -316,7 +316,7 @@ module.exports = class SpellView extends CocoView
|
|||
|
||||
xstart = startOfRow(row)
|
||||
if language is 'python'
|
||||
requiredIndent = new RegExp '^' + new Array(xstart / 4 + 2).join '( |\t)' + '(\\S|\\s*$)'
|
||||
requiredIndent = new RegExp '^' + new Array(xstart / 4 + 1).join('( |\t)') + '( |\t)+(\\S|\\s*$)'
|
||||
for crow in [docRange.start.row+1..docRange.end.row]
|
||||
unless requiredIndent.test lines[crow]
|
||||
docRange.end.row = crow - 1
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
"esper.js": "http://files.codecombat.com/esper.tar.gz",
|
||||
"algoliasearch": "^3.13.1",
|
||||
"algolia-autocomplete.js": "^0.17.0",
|
||||
"algolia-autocomplete-no-conflict": "1.0.0"
|
||||
"algolia-autocomplete-no-conflict": "1.0.0",
|
||||
"bluebird": "^3.4.0"
|
||||
},
|
||||
"overrides": {
|
||||
"algolia-autocomplete.js": {
|
||||
|
|
|
@ -84,7 +84,7 @@ ClassroomHandler = class ClassroomHandler extends Handler
|
|||
return @sendDatabaseError(res, err) if err
|
||||
return @sendSuccess(res, (@formatEntity(req, classroom) for classroom in classrooms))
|
||||
else if code = req.query.code
|
||||
code = code.toLowerCase()
|
||||
code = code.toLowerCase().replace(/ /g, '')
|
||||
Classroom.findOne {code: code}, (err, classroom) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res) unless classroom
|
||||
|
|
|
@ -65,7 +65,7 @@ module.exports =
|
|||
activities = JSON.parse(body)
|
||||
return done("Unexpected activities format: " + body) unless activities.data?
|
||||
for activity in activities.data when activity._type is 'Email'
|
||||
if /@codecombat\.com/ig.test(activity.sender) and not activity.sender?.indexOf(config.mail.username) >= 0
|
||||
if /@codecombat\.(?:com)|(?:nl)$/ig.test(activity.sender) and not activity.sender?.indexOf(config.mail.username) >= 0
|
||||
return done(null, activity.sender, lead.id)
|
||||
return done(null, config.mail.supportSchools, lead.id)
|
||||
catch error
|
||||
|
|
|
@ -22,7 +22,7 @@ module.exports =
|
|||
fetchByCode: wrap (req, res, next) ->
|
||||
code = req.query.code
|
||||
return next() unless code
|
||||
classroom = yield Classroom.findOne({ code: code.toLowerCase() }).select('name ownerID aceConfig')
|
||||
classroom = yield Classroom.findOne({ code: code.toLowerCase().replace(/ /g, '') }).select('name ownerID aceConfig')
|
||||
if not classroom
|
||||
log.debug("classrooms.fetchByCode: Couldn't find Classroom with code: #{code}")
|
||||
throw new errors.NotFound('Classroom not found.')
|
||||
|
@ -170,7 +170,7 @@ module.exports =
|
|||
if req.user.isTeacher()
|
||||
log.debug("classrooms.join: Cannot join a classroom as a teacher: #{req.user.id}")
|
||||
throw new errors.Forbidden('Cannot join a classroom as a teacher')
|
||||
code = req.body.code.toLowerCase()
|
||||
code = req.body.code.toLowerCase().replace(/ /g, '')
|
||||
classroom = yield Classroom.findOne({code: code})
|
||||
if not classroom
|
||||
log.debug("classrooms.join: Classroom not found with code #{code}")
|
||||
|
|
|
@ -60,6 +60,18 @@ describe 'GET /db/classroom/:id', ->
|
|||
expect(body._id).toBe(classroomID = body._id)
|
||||
done()
|
||||
|
||||
describe 'GET /db/classroom by classCode', ->
|
||||
it 'Returns the class if you include spaces', utils.wrap (done) ->
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
teacher = yield utils.initUser()
|
||||
classroom = new Classroom({ name: "some class", ownerID: teacher.id, camelCode: "FooBarBaz", code: "foobarbaz" })
|
||||
yield classroom.save()
|
||||
[res, body] = yield request.getAsync(getURL('/db/classroom?code=foo bar baz'), { json: true })
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body.data?.name).toBe(classroom.get('name'))
|
||||
done()
|
||||
|
||||
describe 'POST /db/classroom', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
|
@ -295,6 +307,18 @@ describe 'POST /db/classroom/-/members', ->
|
|||
fail('student should be added to the free course instance.')
|
||||
done()
|
||||
|
||||
it 'joins the class even with spaces in the classcode', utils.wrap (done) ->
|
||||
yield utils.loginUser(@student)
|
||||
url = getURL("/db/classroom/anything-here/members")
|
||||
code = @classroom.get('code')
|
||||
codeWithSpaces = code.split("").join(" ")
|
||||
[res, body] = yield request.postAsync { uri: url, json: { code: codeWithSpaces } }
|
||||
expect(res.statusCode).toBe(200)
|
||||
classroom = yield Classroom.findById(@classroom.id)
|
||||
if classroom.get('members').length isnt 1
|
||||
fail 'expected classCode with spaces to work too'
|
||||
done()
|
||||
|
||||
it 'returns 403 if the user is a teacher', utils.wrap (done) ->
|
||||
yield utils.loginUser(@teacher)
|
||||
url = getURL("/db/classroom/~/members")
|
||||
|
|
32
test/app/views/courses/CoursesView.spec.coffee
Normal file
32
test/app/views/courses/CoursesView.spec.coffee
Normal file
|
@ -0,0 +1,32 @@
|
|||
CoursesView = require 'views/courses/CoursesView'
|
||||
HeroSelectModal = require 'views/courses/HeroSelectModal'
|
||||
Classrooms = require 'collections/Classrooms'
|
||||
CourseInstances = require 'collections/CourseInstances'
|
||||
Courses = require 'collections/Courses'
|
||||
auth = require 'core/auth'
|
||||
factories = require 'test/app/factories'
|
||||
|
||||
describe 'CoursesView', ->
|
||||
|
||||
modal = null
|
||||
view = null
|
||||
|
||||
describe 'Change Hero button', ->
|
||||
beforeEach (done) ->
|
||||
view = new CoursesView()
|
||||
classrooms = new Classrooms([factories.makeClassroom()])
|
||||
courseInstances = new CourseInstances([factories.makeCourseInstance()])
|
||||
courses = new Courses([factories.makeCourse()])
|
||||
view.classrooms.fakeRequests[0].respondWith({ status: 200, responseText: classrooms.stringify() })
|
||||
view.ownedClassrooms.fakeRequests[0].respondWith({ status: 200, responseText: classrooms.stringify() })
|
||||
view.courseInstances.fakeRequests[0].respondWith({ status: 200, responseText: courseInstances.stringify() })
|
||||
view.render()
|
||||
jasmine.demoEl(view.$el)
|
||||
done()
|
||||
|
||||
it 'opens the modal when you click Change Hero', ->
|
||||
spyOn(view, 'openModalView')
|
||||
view.$('.change-hero-btn').click()
|
||||
expect(view.openModalView).toHaveBeenCalled()
|
||||
args = view.openModalView.calls.argsFor(0)
|
||||
expect(args[0] instanceof HeroSelectModal).toBe(true)
|
38
test/app/views/courses/HeroSelectModal.spec.coffee
Normal file
38
test/app/views/courses/HeroSelectModal.spec.coffee
Normal file
|
@ -0,0 +1,38 @@
|
|||
HeroSelectModal = require 'views/courses/HeroSelectModal'
|
||||
auth = require 'core/auth'
|
||||
factories = require 'test/app/factories'
|
||||
|
||||
describe 'HeroSelectModal', ->
|
||||
|
||||
modal = null
|
||||
coursesView = null
|
||||
user = null
|
||||
|
||||
hero1 = factories.makeThangType({ original: "hero1original", _id: "hero1id", heroClass: "Warrior", name: "Hero 1" })
|
||||
hero2 = factories.makeThangType({ original: "hero2original", _id: "hero2id", heroClass: "Warrior", name: "Hero 2" })
|
||||
heroesResponse = JSON.stringify([hero1, hero2])
|
||||
|
||||
beforeEach (done) ->
|
||||
window.me = user = factories.makeUser({ heroConfig: { thangType: hero1.get('original') } })
|
||||
auth.loginUser(user.attributes)
|
||||
modal = new HeroSelectModal({ currentHeroID: hero1.id })
|
||||
modal.heroes.fakeRequests[0].respondWith({ status: 200, responseText: heroesResponse })
|
||||
jasmine.demoModal(modal)
|
||||
_.defer ->
|
||||
modal.render()
|
||||
done()
|
||||
|
||||
afterEach ->
|
||||
modal.stopListening()
|
||||
|
||||
it 'highlights the current hero', ->
|
||||
expect(modal.$(".hero-option[data-hero-id='#{hero1.id}']")[0].className.split(" ")).toContain('selected')
|
||||
|
||||
it 'saves when you change heroes', (done) ->
|
||||
modal.$(".hero-option[data-hero-id='#{hero2.id}']").click()
|
||||
_.defer ->
|
||||
expect(user.fakeRequests.length).toBe(1)
|
||||
request = user.fakeRequests[0]
|
||||
expect(request.method).toBe("PUT")
|
||||
expect(JSON.parse(request.params).heroConfig?.thangType).toBe(hero2.get('original'))
|
||||
done()
|
|
@ -40,6 +40,12 @@ describe 'CourseVictoryModal', ->
|
|||
modal.classroom.fakeRequests[0].respondWith({
|
||||
status: 200, responseText: factories.makeClassroom().stringify()
|
||||
})
|
||||
if me.fakeRequests
|
||||
lastRequest = _.last(me.fakeRequests)
|
||||
if not lastRequest.response
|
||||
lastRequest.respondWith({
|
||||
status: 200, responseText: factories.makeUser().stringify()
|
||||
})
|
||||
nextLevelRequest = modal.nextLevel.fakeRequests[0]
|
||||
|
||||
describe 'given a course level with a next level and no item or hero rewards', ->
|
||||
|
|
Loading…
Reference in a new issue