Third batch of swedish translations. (#6123)

--- app/locale/sv.coco.coffee
+++ app/locale/sv.coco.coffee
@@ -581,30 +581,30 @@

   play_level:
     back_to_map: "Tillbaka till kartan"
-#    directions: "Directions"
-#    edit_level: "Edit Level"
-#    keep_learning: "Keep Learning"
+    directions: "Instruktioner"
+    edit_level: "Redigera nivå"
+    keep_learning: "Fortsätt lära"
     explore_codecombat: "Utforska CodeCombat"
     finished_hoc: "Jag är färdig med min Hour of Code"
-#    get_certificate: "Get your certificate!"
-#    level_complete: "Level Complete"
+    get_certificate: "Få ditt certifikat!"
+    level_complete: "Nivå avklarad"
     completed_level: "Avklarad nivå:"
     course: "Lektion:"
     done: "Klar"
     next_level: "Nästa nivå"
-#    combo_challenge: "Combo Challenge"
-#    concept_challenge: "Concept Challenge"
-#    challenge_unlocked: "Challenge Unlocked"
-#    combo_challenge_unlocked: "Combo Challenge Unlocked"
-#    concept_challenge_unlocked: "Concept Challenge Unlocked"
-#    concept_challenge_complete: "Concept Challenge Complete!"
-#    combo_challenge_complete: "Combo Challenge Complete!"
-#    combo_challenge_complete_body: "Great job, it looks like you're well on your way to understanding __concept__!"
-#    replay_level: "Replay Level"
-#    combo_concepts_used: "__complete__/__total__ Concepts Used"
-#    combo_all_concepts_used: "You used all concepts possible to solve the challenge. Great job!"
-#    combo_not_all_concepts_used: "You used __complete__ out of the __total__ concepts possible to solve the challenge. Try to get all __total__ concepts next time!"
-#    start_challenge: "Start Challenge"
+    combo_challenge: "Kombinationsutmaning"
+    concept_challenge: "Konceptutmaning"
+    challenge_unlocked: "Utmaning upplåst"
+    combo_challenge_unlocked: "Kombinationsutmaning upplåst"
+    concept_challenge_unlocked: "Konceptutmaning upplåst"
+    concept_challenge_complete: "Kombinationsutmaning avklarad!"
+    combo_challenge_complete: "Konceptutmaning avklarad!"
+    combo_challenge_complete_body: "Bra jobbat, det ser ut som att du är på god väg att förstå __concept__!"
+    replay_level: "Spela om nivå"
+    combo_concepts_used: "__complete__/__total__ koncept har används"
+    combo_all_concepts_used: "Du använde alla möjliga koncept för att lösa utmaningen. Bra jobbat!"
+    combo_not_all_concepts_used: "Du använde __complete__ av __total__ koncept som var möjliga för att lösa utmaningen. Försök att använda alla __total__ koncept nästa gång!"
+    start_challenge: "Starta utmaningen"
     next_game: "Nästa spel"
     languages: "Språk"
     programming_language: "Programmeringsspråk"
@@ -616,12 +616,12 @@
     restart: "Börja om"
     goals: "Mål"
     goal: "Mål"
-#    challenge_level_goals: "Challenge Level Goals"
-#    challenge_level_goal: "Challenge Level Goal"
-#    concept_challenge_goals: "Concept Challenge Goals"
-#    combo_challenge_goals: "Challenge Level Goals"
-#    concept_challenge_goal: "Concept Challenge Goal"
-#    combo_challenge_goal: "Challenge Level Goal"
+    challenge_level_goals: "Utmaningsnivåmål"
+    challenge_level_goal: "Utmaningsnivåmål"
+    concept_challenge_goals: "Konceptutmaningsmål"
+    combo_challenge_goals: "Utmaningsnivåmål"
+    concept_challenge_goal: "Konceptutmaningsmål"
+    combo_challenge_goal: "Utmaningsnivåmål"
     running: "Kör..."
     success: "Du lyckades!"
     incomplete: "Ej färdig"
@@ -631,9 +631,9 @@
     reload_title: "Ladda om all kod?"
     reload_really: "Är du säker på att du vill ladda om nivån från början?"
     reload_confirm: "Ladda om allt"
-#    restart_really: "Are you sure you want to restart the level? You'll loose all the code you've written."
-#    restart_confirm: "Yes, Restart"
-#    test_level: "Test Level"
+    restart_really: "Är du säker på att du vill starta om nivån? Du förlorar all kod du har skrivit."
+    restart_confirm: "Ja, starta om"
+    test_level: "Testnivå"
     victory: "Seger"
     victory_title_prefix: ""
     victory_title_suffix: " Genomförd"
@@ -653,31 +653,31 @@
     victory_new_hero: "Ny hjälte"
     victory_viking_code_school: "Jösses vilken svår nivå du just klarade! Om du inte redan är en mjukvaruutvecklare så borde du vara det. Du bev precis fast-tracked för antagning vid Viking Code School, där du kan ta dina kunskaper till en ny nivå och bli en professionell webbutvecklare på 14 veckor."
     victory_become_a_viking: "Bli en Viking"
-#    victory_no_progress_for_teachers: "Progress is not saved for teachers. But, you can add a student account to your classroom for yourself."
+    victory_no_progress_for_teachers: "Framsteg sparas inte för lärare. Men du kan själv lägga till ett studentkonto i ditt klassrum."
     tome_cast_button_run: "Kör"
     tome_cast_button_running: "Kör..."
     tome_cast_button_ran: "Körde"
-#    tome_cast_button_update: "Update"
+    tome_cast_button_update: "Uppdatera"
     tome_submit_button: "Lämna in"
     tome_reload_method: "Ladda om den ursprungliga koden för den här metoden" # {change}
     tome_your_skills: "Dina färdigheter"
     hints: "Ledtrådar"
-#    videos: "Videos"
+    videos: "Videoklipp"
     hints_title: "Ledtråd {{number}}"
     code_saved: "Kod sparad"
     skip_tutorial: "Hoppa över (esc)"
     keyboard_shortcuts: "Kortkommandon"
     loading_start: "Starta Nivå"
-#    loading_start_combo: "Start Combo Challenge"
-#    loading_start_concept: "Start Concept Challenge"
+    loading_start_combo: "Starta kombinationsutmaningen"
+    loading_start_concept: "Starta konceptutmaningen"
     problem_alert_title: "Fixa din kod"
     time_current: "Nu:"
     time_total: "Max:"
     time_goto: "Gå till:"
     non_user_code_problem_title: "Kunde inte ladda nivå"
     infinite_loop_title: "Oändlig loop upptäckt"
-#    infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know."
-#    check_dev_console: "You can also open the developer console to see what might be going wrong."
+    infinite_loop_description: "Den ursprungliga koden för att bygga världen slutade aldrig köras. Det är antagligen riktigt långsamt eller har en oändlig loop. Eller det kan finnas ett fel. Du kan antingen försöka köra den här koden igen eller återställa koden till standardläget. Låt oss veta om det inte löser det."
+    check_dev_console: "Du kan också öppna utvecklarkonsolen för att se vad som kan gått fel."
     check_dev_console_link: "(instruktioner)"
     infinite_loop_try_again: "Försök igen"
     infinite_loop_reset_level: "Återställ Nivå"
@@ -691,80 +691,80 @@
     tip_think_solution: "Tänk på lösningen, inte problemet."
     tip_theory_practice: "Teoretiskt sett så är det ingen skillnad mellan teori och praktik. Men i praktiken så är det. - Yogi Berra"
     tip_error_free: "Det finns två sätt att skriva felfria program; endast det tredje fungerar. - Alan Perlis"
-#    tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra"
+    tip_debugging_program: "Om felsökning är processen för att ta bort buggar måste programmering vara processen för att skapa dem. - Edsger W. Dijkstra"
     tip_forums: "Gå till forumen och berätta för oss vad du tycker!"
     tip_baby_coders: "I framtiden är till och med bebisar ärkemagiker."
-#    tip_morale_improves: "Loading will continue until morale improves."
-#    tip_all_species: "We believe in equal opportunities to learn programming for all species."
-#    tip_reticulating: "Reticulating spines."
+    tip_morale_improves: "Laddningen fortsätter tills moral förbättras."
+    tip_all_species: "Vi tror på lika möjligheter att lära sig programmering för alla arter."
+    tip_reticulating: "Kornryggar."
     tip_harry: "Du e' en trollkarl, "
-#    tip_great_responsibility: "With great coding skill comes great debug responsibility."
-#    tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep."
+    tip_great_responsibility: "Med stor kodningsförmåga kommer ett stort felsökningsansvar."
+    tip_munchkin: "Om du inte äter dina grönsaker kommer en munchkin efter dig medan du sover."
     tip_binary: "Det finns 10 sorters människor på jorden, de som försår binära tal och de som inte gör det."
-#    tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda"
+    tip_commitment_yoda: "En programmerare måste ha det djupaste engagemanget, det allvarligaste sinnet. ~ Yoda"
     tip_no_try: "Gör. Eller gör inte. Försök finns inte. - Yoda"
     tip_patience: "Tålamod du måste ha, unge Padawan. - Yoda"
     tip_documented_bug: "En dokumenterad bugg är inte en bugg - det är en funktion."
     tip_impossible: "Allt verkar alltid omöjligt ända tills någon gör det. - Nelson Mandela"
     tip_talk_is_cheap: "Det är billigt att prata. Visa mig koden. - Linus Torvalds"
-#    tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay"
+    tip_first_language: "Det mest katastrofala du någonsin kan lära dig är ditt första programmeringsspråk. - Alan Kay"
     tip_hardware_problem: "Fråga: Hur många programmerare krävs för att byta en glödlampa?  Svar: Inga, det är ett hårdvaruproblem."
-#    tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
-#    tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
-#    tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
-#    tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
-#    tip_superpower: "Coding is the closest thing we have to a superpower."
-#    tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds"
+    tip_hofstadters_law: "Hofstadters lag: Det tar alltid längre tid än du förväntar dig, även när du tar hänsyn till Hofstadters lag."
+    tip_premature_optimization: "För tidig optimering är roten till allt ont. - Donald Knuth"
+    tip_brute_force: "Om du är osäker, använd brute force. - Ken Thompson"
+    tip_extrapolation: "Det finns bara två typer av människor: de som kan extrapolera från ofullständiga data ..."
+    tip_superpower: "Kodning är det närmaste vi har till en superkraft."
+    tip_control_destiny: "I verklig öppen källkod har du möjlighet att kontrollera ditt öde. - Linus Torvalds"
     tip_no_code: "Inge kod är snabbare än ingen kod."
     tip_code_never_lies: "Kod ljuger aldrig, kommentarer gör det ibland. — Ron Jeffries"
-#    tip_reusable_software: "Before software can be reusable it first has to be usable."
-#    tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’"
-#    tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates"
-#    tip_source_code: "I want to change the world but they would not give me the source code."
-#    tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann"
+    tip_reusable_software: "Innan programvara kan återanvändas måste den först vara användbar."
+    tip_optimization_operator: "Varje språk har en optimeringsoperatör. På de flesta språk är operatören ‘//’"
+    tip_lines_of_code: "Att mäta programmeringsframsteg med kodrader är som att mäta bygg framsteg för flygplan efter vikt. - Bill Gates"
+    tip_source_code: "Jag vill förändra världen men de ville inte ge mig källkoden."
+    tip_javascript_java: "Java är för JavaScript vad en fot är för fotografi. - Chris Heilmann"
     tip_move_forward: "Vad du än gör, fortsätt framåt. - Martin Luther King Jr."
     tip_google: "Ett problem du inte kan lösa? Googla det!"
     tip_adding_evil: "Också en nypa ondska."
     tip_hate_computers: "Det är det som är grejen med folk som tror att de hatar datorer. Det de egentligen hatar är dåliga programmerare. - Larry Niven"
     tip_open_source_contribute: "Du kan få CodeCombat att bli ännu bättre!"
     tip_recurse: "Iteration är mänskligt, rekursion är gudomligt. - L. Peter Deutsch"
-#    tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus"
-#    tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha"
-#    tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen."
-#    tip_solve_then_write: "First, solve the problem. Then, write the code. - John Johnson"
-#    tip_compiler_ignores_comments: "Sometimes I think that the compiler ignores my comments."
-#    tip_understand_recursion: "The only way to understand recursion is to understand recursion."
-#    tip_life_and_polymorphism: "Open Source is like a totally polymorphic heterogeneous structure: All types are welcome."
-#    tip_mistakes_proof_of_trying: "Mistakes in your code are just proof that you are trying."
-#    tip_adding_orgres: "Rounding up ogres."
-#    tip_sharpening_swords: "Sharpening the swords."
-#    tip_ratatouille: "You must not let anyone define your limits because of where you come from. Your only limit is your soul. - Gusteau, Ratatouille"
-#    tip_nemo: "When life gets you down, want to know what you've gotta do? Just keep swimming, just keep swimming. - Dory, Finding Nemo"
-#    tip_internet_weather: "Just move to the internet, it's great here. We get to live inside where the weather is always awesome. - John Green"
-#    tip_nerds: "Nerds are allowed to love stuff, like jump-up-and-down-in-the-chair-can't-control-yourself love it. - John Green"
-#    tip_self_taught: "I taught myself 90% of what I've learned. And that's normal! - Hank Green"
-#    tip_luna_lovegood: "Don't worry, you're just as sane as I am. - Luna Lovegood"
-#    tip_good_idea: "The best way to have a good idea is to have a lot of ideas. - Linus Pauling"
-#    tip_programming_not_about_computers: "Computer Science is no more about computers than astronomy is about telescopes. - Edsger Dijkstra"
-#    tip_mulan: "Believe you can, then you will. - Mulan"
-#    project_complete: "Project Complete!"
+    tip_free_your_mind: "Du måste släppa allt, Neo. Rädsla, tvivel och misstro. Frigör ditt sinne. - Morpheus"
+    tip_strong_opponents: "Även de starkaste motståndarna har alltid en svaghet. - Itachi Uchiha"
+    tip_paper_and_pen: "Innan du börjar koda kan du alltid planera med ett pappersark och en penna."
+    tip_solve_then_write: "Lös först problemet. Skriv sedan koden. - John Johnson"
+    tip_compiler_ignores_comments: "Ibland tror jag att kompilatorn ignorerar mina kommentarer."
+    tip_understand_recursion: "Det enda sättet att förstå rekursion är att förstå rekursion."
+    tip_life_and_polymorphism: "Open Source är som en helt polymorf heterogen struktur: Alla typer är välkomna."
+    tip_mistakes_proof_of_trying: "Fel i din kod är bara ett bevis på att du försöker."
+    tip_adding_orgres: "Avrundar ograr."
+    tip_sharpening_swords: "Slipa svärden."
+    tip_ratatouille: "Du får inte låta någon definiera dina gränser på grund av var du kommer ifrån. Din enda gräns är din själ. - Gusteau, Ratatouille"
+    tip_nemo: "När du känner dig nere, vill du veta vad du måste göra? Fortsätt bara simma, bara fortsätt simma. - Dory, Hitta Nemo"
+    tip_internet_weather: "Flytta till internet, det är fantastiskt här. Vi får bo inne där vädret alltid är fantastiskt. - John Green"
+    tip_nerds: "Nördar får älska saker, som att hoppa upp-och-ner-i-stolen-inte-kan-kontrollera-dig-själv kärlek. - John Green"
+    tip_self_taught: "Jag lärde mig själv 90% av det jag har lärt mig. Och det är normalt! - Hank Green"
+    tip_luna_lovegood: "Oroa dig inte, du är lika sund som jag. - Luna Lovegood"
+    tip_good_idea: "Det bästa sättet att få en bra idé är att ha många idéer. - Linus Pauling"
+    tip_programming_not_about_computers: "Datavetenskap handlar inte mer om datorer än astronomi om teleskop. - Edsger Dijkstra"
+    tip_mulan: "Tro att du kan, då kommer du att göra det. - Mulan"
+    project_complete: "Projektet är klart!"
     share_this_project: "Dela projektet med vänner eller familj:"
     ready_to_share: "Redo att publicera ditt projekt?"
-#    click_publish: "Click \"Publish\" to make it appear in the class gallery, then check out what your classmates built! You can come back and continue to work on this project. Any further changes will automatically be saved and shared with your classmates."
-#    already_published_prefix: "Your changes have been published to the class gallery."
-#    already_published_suffix: "Keep experimenting and making this project even better, or see what the rest of your class has built! Your changes will automatically be saved and shared with your classmates."
+    click_publish: "Klicka på \"Publicera\" så att det visas i klassgalleriet och kolla sedan vad dina klasskamrater byggde! Du kan komma tillbaka och fortsätta arbeta med projektet. Eventuella ytterligare ändringar sparas automatiskt och delas med dina klasskamrater."
+    already_published_prefix: "Dina ändringar har publicerats i klassgalleriet."
+    already_published_suffix: "Fortsätt experimentera och göra det här projektet ännu bättre, eller se vad resten av din klass har byggt! Dina ändringar sparas automatiskt och delas med dina klasskamrater."
     view_gallery: "Visa Galleri"
-#    project_published_noty: "Your level has been published!"
+    project_published_noty: "Din nivå har publicerats!"
     keep_editing: "Fortsätt redigera"
-#    learn_new_concepts: "Learn new concepts"
-#    watch_a_video: "Watch a video on __concept_name__"
-#    concept_unlocked: "Concept Unlocked"
-#    use_at_least_one_concept: "Use at least one concept: "
-#    command_bank: "Command Bank"
-#    learning_goals: "Learning Goals"
-#    start: "Start"
-#    vega_character: "Vega Character"
-#    click_to_continue: "Click to Continue"
+    learn_new_concepts: "Lär dig nya koncept"
+    watch_a_video: "Titta på en video om __concept_name__"
+    concept_unlocked: "Koncept upplåst"
+    use_at_least_one_concept: "Använd minst ett koncept: "
+    command_bank: "Kommandobank"
+    learning_goals: "Lärandemål"
+    start: "Start"
+    vega_character: "Vega karaktär"
+    click_to_continue: "Klicka för att fortsätta"

   apis:
     methods: "Metoder"


Update zh-HANT.coffee (#4063)

Update zh-HANT.coffee (#4063)

Update some # {change} and remove the # {change} comment.

--- app/locale/zh-HANT.coffee
+++ app/locale/zh-HANT.coffee
@@ -1,7 +1,7 @@
 module.exports = nativeDescription: "繁體中文", englishDescription: "Chinese (Traditional)", translation:
   new_home:
     slogan: "最迷人的程式設計學習遊戲"
-    classroom_edition: "課堂版本"
+    classroom_edition: "教室版本"
     learn_to_code: "學習寫程式碼"
     teacher: "老師"
     student: "學生"
@@ -10,7 +10,7 @@
     im_a_student: "我是學生"
     learn_more: "了解更多"
     classroom_in_a_box: "在一個盒子中的電腦科學教室。"
-    codecombat_is: "CodeCombat是一個讓學生透過進行遊戲來學習電腦科學的平台。"
+    codecombat_is: "CodeCombat是一個讓學生透過進行遊戲來學習計算機科學的平台。"
     our_courses: "我們的課程經過特別的遊戲測試來超越教室教學，甚至是超越一些先前沒有編程經驗的老師。"
     top_screenshots_hint: "學生們寫程式碼並且隨時觀看他們的更新。"
     designed_with: "在心中與老師們一起設計"
@@ -19,7 +19,7 @@
     getting_students: "讓學生們用最快的速度鍵入程式碼，這是學習程式設計語法和正確結構的關鍵。"
     educator_resources: "教學資源"
     course_guides: "和課程導引"
-    teaching_computer_science: "教電腦科學不需要一個昂貴的學位，因為我們提供了工具來支援各種背景的教學者。"
+    teaching_computer_science: "教授計算機科學不需要一個昂貴的學位，因為我們提供了工具來支援各種背景的教學者。"
     accessible_to: "提供給"
     everyone: "每一個人"
     democratizing: "使學習寫程式碼的過程普及化是我們的核心理念，每個人都應該有機會學習寫程式。"
@@ -56,7 +56,7 @@
     ffa: "學生免費"
     lesson_time: "課程時間："
     coming_soon: "敬請期待!"
-    courses_available_in: "包含JavaScript, Python,和Java的課程(敬請期待!)" # {change}
+    courses_available_in: "包含JavaScript、Python，以及網頁開發HTML、CSS、jQuery、和Bootstrap的課程。"
     boast: "擁有足夠複雜的關卡，同時能吸引玩家以及程式撰寫者。"
     winning: "成功的結合RPG遊戲和程式設計作業，造就了對孩子有善且能樂在其中的教育環境。"
     run_class: "如今想要在學校進行一堂電腦科學的課程，您不再需要有電腦科學的背景。"
@@ -99,7 +99,7 @@
     faqs: "FAQs常見問題"
     help_pref: "需要協助嗎? 寫封Email給我們"
     help_suff: "然後我們會與您接觸!"
-#    resource_hub: "Resource Hub"
+    resource_hub: "Resource Hub"

   modal:
     close: "關閉"
@@ -125,8 +125,8 @@
     items: "物品" # Tooltip on item shop button from /play
     unlock: "解鎖" # For purchasing items and heroes
     confirm: "確認"
-    owned: "擁有" # For items you own
-    locked: "鎖定"
+    owned: "擁有的物品" # For items you own
+    locked: "未解鎖裝備"
     purchasable: "可購買" # For a hero you unlocked but haven't purchased
     available: "可用"
     skills_granted: "將習得技能" # Property documentation details
@@ -142,7 +142,7 @@
     level_difficulty: "難度"
     play_classroom_version: "遊玩教室版本" # Choose a level in campaign version that you also can play in one of your courses
     campaign_beginner: "新手指南"
-    awaiting_levels_adventurer_prefix: "我們每週將釋出新的關卡。" # {change}
+    awaiting_levels_adventurer_prefix: "我們每週將釋出新的關卡。"
     awaiting_levels_adventurer: "註冊成為冒險家"
     awaiting_levels_adventurer_suffix: "成為第一個挑戰新關卡的冒險家吧！"
     adjust_volume: "調整音量"
@@ -209,7 +209,7 @@

   login:
     sign_up: "註冊"
-#    email_or_username: "Email or username"
+    email_or_username: "輸入Email帳號 或是 使用者名稱"
     log_in: "登入"
     logging_in: "登入中"
     log_out: "登出"
@@ -222,11 +222,11 @@
     signup_switch: "建立一個帳號"

   signup:
-    create_student_header: "建立學生帳戶"
-    create_teacher_header: "建立教師帳戶"
-    create_individual_header: "建立私人帳戶"
-    email_announcements: "通過郵件接收通知" # {change}
-    creating: "帳戶建立中…"
+    create_student_header: "建立學生帳號"
+    create_teacher_header: "建立教師帳號"
+    create_individual_header: "建立私人帳號"
+    email_announcements: "接收通知以了解CodeCombat中的新關卡和新功能!"
+    creating: "帳號建立中…"
     sign_up: "註冊"
     log_in: "登入"
     required: "在這麼做之前必須先登入。"
@@ -234,7 +234,7 @@
     school_name: "就讀學校及所在城市"
     optional: "選填"
     school_name_placeholder: "範例：XX中學，台北"
-#    connect_with: "Connect with:"
+    connect_with: "連結方式:"
     connected_gplus_header: "您已經成功與Google+連結!"
     connected_gplus_p: "完成註冊以使用Google+帳號登入。"
     gplus_exists: "您已經有一個與Google+連結的帳號!"
@@ -243,29 +243,29 @@
     facebook_exists: "您已經有一個與FB臉書連結的帳號!"
     hey_students: "學生們，請輸入老師所公佈的班級代碼"
     birthday: "生日"
-#    parent_email_blurb: "We know you can't wait to learn programming — we're excited too! Your parents will receive an email with further instructions on how to create an account for you. Email {{email_link}} if you have any questions."
-#    classroom_not_found: "No classes exist with this Class Code. Check your spelling or ask your teacher for help."
+    parent_email_blurb: "我們知道您已經迫不及待的想去學習編程 — 我們也同樣地感到興奮! 您的家長將會收到一封電子郵件，內容包含了如何為您更進一步的申請一個帳號。 如果您有任何問題，我們很高興收到您的詢問email。Email地址 {{email_link}} "
+    classroom_not_found: "這個班級代碼是無效的，請檢察您的拼字或是尋求老師的協助。"
     checking: "檢查中..."
-#    account_exists: "This email is already in use:"
+    account_exists: "此email帳號已經被使用過了："
     sign_in: "登入"
     email_good: "可用Email"
     name_taken: "此名稱已被註冊！試試{{suggestedName}}?"
-    name_available: "此名稱可用！Username available!"
-    name_is_email: "使用者名稱不可以同時是email"
+    name_available: "此名稱可用！"
+    name_is_email: "使用者名稱不可以使用email格式"
     choose_type: "選擇帳戶類型："
     teacher_type_1: "用CodeCombat教程式設計！"
-    teacher_type_2: "建立課堂"
-#    teacher_type_3: "Access Course Guides"
+    teacher_type_2: "建立班級"
+    teacher_type_3: "取得課程指引"
     teacher_type_4: "查看學生進度"
     signup_as_teacher: "註冊成為老師"
     student_type_1: "一邊玩遊戲一邊學程式設計！"
-    student_type_2: "和課堂同學一起玩！"
-#    student_type_3: "Compete in arenas"
+    student_type_2: "和班上同學一起玩！"
+    student_type_3: "在競技場中互相對抗"
     student_type_4: "選擇一個英雄！"
-#    student_type_5: "Have your Class Code ready!"
+    student_type_5: "準備好您的班級代碼！"
     signup_as_student: "註冊成為學生"
     individuals_or_parents: "私人帳戶＆家長"
-#    individual_type: "For players learning to code outside of a class. Parents should sign up for an account here."
+    individual_type: "家長們應該在此為每個於課外學習編程的玩家們申請一個帳號。"
     signup_as_individual: "創建私人帳戶"
     enter_class_code: "輸入你的課程代碼"
     enter_birthdate: "輸入生日："
@@ -273,7 +273,7 @@
     ask_teacher_1: "向老師詢問課程代碼為何"
     ask_teacher_2: "還不是課堂的一份子？創建一個"
     ask_teacher_3: "私人帳號"
-#    ask_teacher_4: " instead."
+    ask_teacher_4: " 而非反過來。"
     about_to_join: "您將會加入："
     enter_parent_email: "輸入家長的email："
     parent_email_error: "系統嘗試寄信時發生錯誤，請確認您的email並再次發送。"
@@ -304,7 +304,7 @@
     back: "向後瀏覽" # When used as an action verb, like "Navigate backward"
     coming_soon: "即將公布！"
     continue: "繼續前進" # When used as an action verb, like "Continue forward"
-#    default_code: "Default Code"
+    default_code: "預設程式碼"
     loading: "載入中…"
     overview: "總覽"
     solution: "解決方案"
@@ -325,10 +325,10 @@
     help: "求助"
     watch: "關注"
     unwatch: "取消關注"
-    submit_patch: "送出修補"
+    submit_patch: "送出補丁"
     submit_changes: "送出修改"
     save_changes: "保存更改"
-    required_field: "需要的部分" # {change}
+    required_field: "需求"

   general:
     and: "和"
@@ -382,7 +382,7 @@
     wizard: "巫師"
     first_name: "名"
     last_name: "姓"
-#    last_initial: "Last Initial"
+#    last_initial: "Last Initial" 中文似乎沒有這個習慣
     username: "使用者名稱"

   units:
@@ -434,7 +434,7 @@
     victory_title_suffix: "完成"
     victory_sign_up: "保存進度"
     victory_sign_up_poke: "想保存您的程式碼？建立一個免費帳號吧！"
-    victory_rate_the_level: "評估關卡：" # {change}
+    victory_rate_the_level: "這個關卡有多好玩?"
     victory_return_to_ladder: "返回升級比賽模式"
     victory_saving_progress: "儲存進度"
     victory_go_home: "返回首頁"
@@ -445,21 +445,21 @@
     victory_experience_gained: "取得經驗值"
     victory_gems_gained: "取得寶石"
     victory_new_item: "新的物品"
-#    victory_new_hero: "New Hero"
+    victory_new_hero: "新的英雄"
     victory_viking_code_school: "太厲害了，您剛完成了非常困難的關卡！如果您想成為一個軟體開發人員，您就應該去試一下Viking Code School。在這裡您可以把您的知識增長到另一個台階。只需要14個星期您就能成為一個專業的網頁開發人員。"
     victory_become_a_viking: "成為一個維京人。"
-    victory_no_progress_for_teachers: "老師們不能保存進度，但是您可以將自己的帳號加入班級作為學生來保存進度。"
+    victory_no_progress_for_teachers: "老師們不能儲存進度，但是您可以將自己的帳號加入班級作為學生來儲存進度。"
     guide_title: "指南"
     tome_cast_button_run: "運行"
     tome_cast_button_running: "運行中"
     tome_cast_button_ran: "已運行"
     tome_submit_button: "送出"
-    tome_reload_method: "重新載入該方法的原程式碼" # {change}
+    tome_reload_method: "重新載入原始程式碼來重啟關卡"
     tome_available_spells: "可用的法術"
     tome_your_skills: "您的技能"
     tome_current_method: "現在的方法"
-#    hints: "Hints"
-#    hints_title: "Hint {{number}}"
+    hints: "小提示"
+    hints_title: "提示{{number}} "
     code_saved: "程式碼已保存"
     skip_tutorial: "跳過 (esc)"
     keyboard_shortcuts: "快捷鍵"
@@ -551,13 +551,13 @@
     how_to_play_3: "點擊食人魔來攻擊他們。"
     restart: "重置關卡"
     play: "開始玩關卡"
-    play_more_codecombat: "玩更多的odeCombat"
+    play_more_codecombat: "玩更多的CodeCombat"
     default_student_instructions: "利用點擊來控制英雄，贏得遊戲！"
-#    back_to_coding: "Back to Coding"
+    back_to_coding: "返回編程"

   game_menu:
     inventory_tab: "道具欄"
-    save_load_tab: "保存/載入"
+    save_load_tab: "儲存/載入"
     options_tab: "選項"
     guide_tab: "導引"
     guide_video_tutorial: "影片教學"
@@ -588,8 +588,8 @@
   inventory:
     equipped_item: "已裝備"
     required_purchase_title: "必要的"
-    available_item: "可使用的"
-    restricted_title: "被限制的"
+    available_item: "可使用的裝備"
+    restricted_title: "關卡裝備限制"
     should_equip: "連點物品兩下可裝備"
     equipped: "(已裝備)"
     locked: "(需解鎖)"
@@ -729,8 +729,8 @@
     current_value: "現在數值"
     default_value: "預設數值"
     parameters: "參數"
-#    required_parameters: "Required Parameters"
-#    optional_parameters: "Optional Parameters"
+    required_parameters: "需要的參數"
+    optional_parameters: "選擇性參數"
     returns: "回傳"
     granted_by: "賦予"

@@ -754,27 +754,27 @@
     editor_config_behaviors_description: "自動填入小括號、大括號以及引號。"

   about:
-    main_title: "如果您想要學編程，你需要寫(一大堆)程式碼。"
+    main_title: "如果您想要學編程，您需要寫(一大堆)程式碼。"
     main_description: "在CodeCombat中，我們的職責是確保您笑著玩遊戲。"
     mission_link: "任務"
     team_link: "隊伍"
     story_link: "故事"
-    press_link: "按"
+    press_link: "點擊"
     mission_title: "我們的任務為：讓世上每個學生都有機會能學會編程。"
     mission_description_1: "編程是一種魔法。它是一種從純粹的想像中創造東西的能力。我們營運CodeCombat來讓學習者可以透過鍵入程式碼在指尖感覺到巫師般的力量。"
     mission_description_2: "它的出現也讓使用者學得更快，就像是與它對話而不是閱讀手冊一般。我們想將這種對話帶給每一個學校以及每一位學生，因為每個人都應該要有機會體會編程的魔力。 "
     team_title: "認識CodeCombat團隊。"
     team_values: "我們重視開放且互相尊重的對話，好的想法才會勝出。我們的抉擇建立於客戶調查上，且過程注重於將具體的結果交給客戶。從我們的CEO到我們的Github貢獻者，每一個人都會動手參與，因為我們傾向在團隊中學習成長。"
-    nick_title: "程式員" # {change}
+    nick_title: "共同創辦人，CEO"
     nick_blurb: "亢奮的咕嚕"
-    matt_title: "共同創辦人" # {change}
-    cat_title: "首席開卡設計師" # {change}
+    matt_title: "共同創辦人，CTO"
+    cat_title: "遊戲設計師"
     cat_blurb: "氣宗"
-    scott_title: "共同創辦人" # {change}
+    scott_title: "共同創辦人，軟體工程師"
     scott_blurb: "理性至上"
     maka_title: "客戶代言人"
     maka_blurb: "說書人"
-    rob_title: "編譯工程師" # {change}
+    rob_title: "軟體工程師"
     rob_blurb: "編寫一些的程式碼"
     josh_c_title: "遊戲設計師"
     josh_c_blurb: "設計遊戲"
@@ -785,18 +785,18 @@
     phoenix_title: "軟體工程師"
     nolan_title: "區域經理"
     elliot_title: "合作經裡"
-#    elliot_blurb: "Mindreader"
-#    lisa_title: "School Specialist"
-#    lisa_blurb: "A gritty one"
-#    sean_title: "Territory Manager"
+    elliot_blurb: "讀心師"
+    lisa_title: "校園專家"
+    lisa_blurb: "堅毅不拔的人"
+    sean_title: "區域經理"
     retrostyle_title: "插畫師"
     retrostyle_blurb: "復古風格的遊戲"
     jose_title: "音樂"
     jose_blurb: "放輕鬆"
     community_title: "...以及我們的開放原始碼社群"
     community_subtitle: "超過 450 位貢獻者協助建立 CodeCombat , 而且每週持續增加中！" # {change}
-#    community_description_3: "CodeCombat is a"
-#    community_description_link_2: "community project"
+    community_description_3: "CodeCombat 是一個"
+    community_description_link_2: "社群專案"
     community_description_1: "CodeCombat 是一個社群專案，由有數以百計的玩家志願來建立遊戲關卡，建構我們的程式碼來添加功能、修正 Bugs、執行測試、甚至翻譯此遊戲至超過 50 種語言。如同多數的開源社群一般，所有的員工、貢獻者們及 CodeCombat 都獲益於持續的互相分享靈感和彙整努力。CodeCombat 是建立於無數的開源專案之上，因此我們開源回饋給社群朋友，提供一個友善專案給對於原始碼有興趣的玩家來探索和實驗。任何人都可以加入 CodeCombat 社群！請查看我們的" # {change}
     community_description_link: "貢獻者頁"
     community_description_2: "來獲得更多資訊。"
@@ -815,27 +815,27 @@
     story_sketch_caption: "Nick 著手完成了一個初版的藍圖，描述一個撰寫程式的遊戲。"
     story_long_way_2: "然而在完成任務之前，我們仍然還有許多事情要做。所以..."
     jobs_title: "與我們一同工作，來協助撰寫 CodeCombat 的歷史吧！"
-    jobs_subtitle: "找不到符合的位置，但有興趣與我們保持連繫？查看『建立我的』清單吧。"
+    jobs_subtitle: "找不到符合的位置，但有興趣與我們保持連繫？查看『創造自己的工作』清單吧。"
     jobs_benefits: "員工福利"
     jobs_benefit_4: "無盡假期"
-#    jobs_benefit_5: "Professional development and continuing education support – free books and games!"
-#    jobs_benefit_6: "Medical (gold), dental, vision, commuter"
-#    jobs_benefit_7: "Sit-stand desks for all"
-#    jobs_benefit_9: "10-year option exercise window"
-#    jobs_benefit_10: "Maternity leave: 10 weeks paid, next 6 @ 55% salary"
-#    jobs_benefit_11: "Paternity leave: 10 weeks paid"
+    jobs_benefit_5: "專業的開發與持續的教育援助 – 免費的課本以及遊戲!"
+    jobs_benefit_6: "醫藥、牙科、視力治療"
+    jobs_benefit_7: "所有人都有坐式/站式辦公桌"
+    jobs_benefit_9: "十年的公司期權"
+    jobs_benefit_10: "產假:10週給薪以及接下來6週55%的給薪"
+    jobs_benefit_11: "陪產假:10週給薪"
     learn_more: "了解更多"
-    jobs_custom_title: "建立我的"
-#    jobs_custom_description: "Are you passionate about CodeCombat but don't see a job listed that matches your qualifications? Write us and show how you think you can contribute to our team. We'd love to hear from you!"
-#    jobs_custom_contact_1: "Send us a note at"
-#    jobs_custom_contact_2: "introducing yourself and we might get in touch in the future!"
+    jobs_custom_title: "創造自己的工作"
+    jobs_custom_description: "你對於CodeCombat有熱誠但是找不到一個職位符合您的條件嗎? 聯繫我們並且讓我們知道您如何為我們的團隊貢獻。 我們很希望能聽到您的意見!"
+    jobs_custom_contact_1: "寄給我們一個簡介至"
+    jobs_custom_contact_2: "介紹您自己，未來我們可能會與您接觸!"
     contact_title: "點擊 & 連繫"
     contact_subtitle: "需要更多資訊？透過以下與我們連繫"
     screenshots_title: "遊戲螢幕截圖"
     screenshots_hint: "(點擊查看完整尺寸)"
     downloads_title: "下載資源 & 資訊"
     about_codecombat: "關於 CodeCombat"
-#    logo: "Logo"
+    logo: "Logo"
     screenshots: "螢幕截圖"
     character_art: "角色美術作品"
     download_all: "下載全部"
@@ -850,58 +850,58 @@
     more_info_1: "我們的"
     more_info_2: "教師論壇"
     more_info_3: "是個與其他使用CodeCombat的教育工作者聯繫的良好平台。"
-#    licenses_needed: "Licenses needed"
-
-#  teachers_quote:
-#    name: "Demo Form"
-#    subtitle: "Get your students started in less than an hour. You'll be able to create a class, add students, and monitor their progress as they learn computer science."
-#    email_exists: "User exists with this email."
-#    phone_number: "Phone number"
-#    phone_number_help: "Where can we reach you during the workday?"
-#    primary_role_label: "Your Primary Role"
-#    role_default: "Select Role"
-#    primary_role_default: "Select Primary Role"
-#    purchaser_role_default: "Select Purchaser Role"
-#    tech_coordinator: "Technology coordinator"
-#    advisor: "Advisor"
-#    principal: "Principal"
-#    superintendent: "Superintendent"
-#    parent: "Parent"
-#    purchaser_role_label: "Your Purchaser Role"
-#    influence_advocate: "Influence/Advocate"
-#    evaluate_recommend: "Evaluate/Recommend"
-#    approve_funds: "Approve Funds"
-#    no_purchaser_role: "No role in purchase decisions"
-#    district_label: "District"
-#    district_na: "Enter N/A if not applicable"
-#    organization_label: "School"
-#    city: "City"
-#    state: "State"
-#    country: "Country"
-#    num_students_help: "How many do you anticipate enrolling in CodeCombat?"
-#    num_students_default: "Select Range"
-#    education_level_label: "Education Level of Students"
-#    education_level_help: "Choose as many as apply."
-#    elementary_school: "Elementary School"
-#    high_school: "High School"
-#    please_explain: "(please explain)"
-#    middle_school: "Middle School"
-#    college_plus: "College or higher"
-#    anything_else: "Anything else we should know?"
-#    thanks_header: "Request Received!"
-#    thanks_sub_header: "Thanks for expressing interest in CodeCombat for your school."
-#    thanks_p: "We'll be in touch soon! If you need to get in contact, you can reach us at:"
-#    back_to_classes: "Back to Classes"
-#    finish_signup: "Finish creating your teacher account:"
-#    finish_signup_p: "Create an account to set up a class, add your students, and monitor their progress as they learn computer science."
-#    signup_with: "Sign up with:"
-#    connect_with: "Connect with:"
-#    conversion_warning: "WARNING: Your current account is a Student Account. 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 licenses 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."
-#    create_account: "Create a Teacher Account"
-#    create_account_subtitle: "Get access to teacher-only tools for using CodeCombat in the classroom.  Set up a class, add your students, and monitor their progress!"
-#    convert_account_title: "Update to Teacher Account"
-#    not: "Not"
+    licenses_needed: "授權許可"
+
+  teachers_quote:
+    name: "Demo 形式"
+    subtitle: "讓您的學生在一個小時內上手。在他們學習計算機科學時您將會創建一個班級，加入您的學生，並且掌握他們的學習過程。"
+    email_exists: "已經存在使用這個email的使用者。"
+    phone_number: "電話號碼"
+    phone_number_help: "在工作天中我們能在哪裡找到您?"
+    primary_role_label: "您的主要角色"
+    role_default: "選擇角色"
+    primary_role_default: "選擇主要角色"
+    purchaser_role_default: "選擇買方角色"
+    tech_coordinator: "技術夥伴"
+    advisor: "顧問"
+    principal: "校長"
+    superintendent: "主管"
+    parent: "家長"
+    purchaser_role_label: "您的買方角色"
+    influence_advocate: "影響/倡導"
+    evaluate_recommend: "評估/推薦"
+    approve_funds: "批准資金"
+    no_purchaser_role: "不參與購買決策"
+    district_label: "地區"
+    district_na: "若不適用時請輸入 N/A "
+    organization_label: "學校"
+    city: "城市"
+    state: "州"
+    country: "國家"
+    num_students_help: "您預估將有多少人加入CodeCombat?"
+    num_students_default: "選擇範圍"
+    education_level_label: "學生們的教育程度"
+    education_level_help: "選擇將要教導的程度"
+    elementary_school: "國小"
+    high_school: "高中"
+    please_explain: "(請註明)"
+    middle_school: "中學"
+    college_plus: "大學以上"
+    anything_else: "還有甚麼我們需要知道的嗎?"
+    thanks_header: "已收到申請!"
+    thanks_sub_header: "感謝您表達您的學校對於CodeCombat的興趣。"
+    thanks_p: "我們不久將會與您接觸!如果需要與我們聯絡，您可以以下面方式找到我們:"
+    back_to_classes: "回到班級"
+    finish_signup: "完成您的教師帳號創建:"
+    finish_signup_p: "創建一個帳號以建立班級，加入您的學生，以及在他們學習計算機科學時掌握他們的學習過程。"
+    signup_with: "註冊方式:"
+    connect_with: "連結方式:"
+    conversion_warning: "注意:您現在的帳號是學生帳號。一旦您送出這個表格，您的帳號將會升級成教師帳號。"
+    learn_more_modal: "教師帳號在CodeCombat中有權力監控學生的學習過程、授予許可，以及管理教室。教師帳號不會是教室中的一份子 - 如果您正在用這個帳號申請進入班級，一旦您升級成教師帳號將不再能夠進入，"
+    create_account: "創建一個教師帳號"
+    create_account_subtitle: "取得教師專用工具來在教室中使用CodeCombat建立一個班級加入您的學生，並且掌握他們的學習過程!"
+    convert_account_title: "升級成教師帳號"
+    not: "取消"

   versions:
     save_version_title: "保存新版本"
@@ -1074,17 +1074,17 @@
   courses:
     course: "課程"
     courses: "課程"
-    create_new_class: "建立新的課堂"
-    not_enrolled: "您没有註冊這一節課。"
+    create_new_class: "建立新的班級"
+    not_enrolled: "您没有註冊這一個課程。"
     visit_pref: "請到這個"
     visit_suf: "網頁註冊。"
-    select_class: "請選其中一門課堂"
+    select_class: "請選其中一個班級"
     unnamed: "*未命名*"
     select: "選擇"
-    unnamed_class: "課堂未命名"
-    edit_settings: "編輯課堂設定"
-    edit_settings1: "編輯課堂設定"
-    progress: "課堂進度"
+    unnamed_class: "未命名的班級"
+    edit_settings: "編輯班級設定"
+    edit_settings1: "編輯班級設定"
+    progress: "班級進度"
     add_students: "添加學生"
     stats: "統計"
     total_students: "學生人數："
@@ -1100,32 +1100,32 @@
     played: "已通關"
     play_time: "遊戲時間："
     completed: "完成："
-    invite_students: "邀請學生加入此課堂。"
-    invite_link_header: "參與課堂的縺結"
-    invite_link_p_1: "把這個參與課堂的連結發給你的學生。"
+    invite_students: "邀請學生加入此班級。"
+    invite_link_header: "加入班級的連結"
+    invite_link_p_1: "把這個參與班級的連結發給你的學生。"
     invite_link_p_2: "或讓我們代你直接發送電郵："
-    capacity_used: "課堂插槽己用："
-    enter_emails: "輸入學生電郵地址來邀請，每行一個" # {change}
-    send_invites: "發送邀請" # {change}
-    creating_class: "課堂創建中…"
+    capacity_used: "己用班級空間："
+    enter_emails: "以斷行或是逗號分隔電郵地址"
+    send_invites: "邀請學生"
+    creating_class: "班級創建中…"
     purchasing_course: "購買課程中…"
     buy_course: "購買課程"
     buy_course1: "購買這個課程"
     select_all_courses: "可半價一次購買所有課程！"
     all_courses: "所有課程"
-#    number_programming_students: "Number of Programming Students"
-#    number_total_students: "Total Students in School/District"
-    enter_number_students: "輸入該課堂的學生上限人數"
-    name_class: "命名您的課堂"
+    number_programming_students: "正在編程的學生數量"
+    number_total_students: "所有在學區中的學生"
+    enter_number_students: "輸入該班級的學生上限人數"
+    name_class: "命名您的班級"
     displayed_course_page: "這將會在課程頁面顯示，可被修改。"
     buy: "購買"
     purchasing_for: "你正在購買許可證，課程為"
     creating_for: "您正在創建一個課程為"
     for: "，人數上限為" # Like in 'for 30 students'
-    receive_code: "然後您會收到一個解鎖碼，把它分發給你的學生用來註冊你的課堂。"
+    receive_code: "然後您會收到一個解鎖碼，把它分發給你的學生用來加入您的課程"
     free_trial: "老師可免費試用！"
     get_access: "獲得個人使用權在評估目的下來使用所有課程。"
-    questions: "有疑問？"
+    questions: "有疑問嗎？"
     teachers_click: "老師點擊這裡"
     students_click: "學生點擊這裡"
     courses_on_coco: "CodeCombat上的課程"


Update zh-HANT.coffee (#4130)

Update zh-HANT.coffee (#4130)

--- app/locale/zh-HANT.coffee
+++ app/locale/zh-HANT.coffee
@@ -1869,22 +1869,22 @@
   loading_error:
     could_not_load: "從伺服器載入失敗"
     connection_failure: "連線失敗。"
-#    connection_failure_desc: "It doesn’t look like you’re connected to the internet! Check your network connection and then reload this page."
+    connection_failure_desc: "看來您並沒有連接上網際網路! 請檢查您的網路連接狀態然後重新載入這個頁面。"
     login_required: "您需要先登入。"
     login_required_desc: "您要先登入才能查看此頁。"
     unauthorized: "您需要先登入。您把cookies禁用了嗎？"
     forbidden: "您沒有權限。"
-#    forbidden_desc: "Oh no, there’s nothing we can show you here! Make sure you’re logged into the correct account, or visit one of the links below to get back to programming!"
+    forbidden_desc: "喔不，這裡沒有東西可以顯示給您看! 請確認您登入的是正確的帳號，或是進入下方的其中一個連結以回到編程頁面!"
     not_found: "沒找到。"
-#    not_found_desc: "Hm, there’s nothing here. Visit one of the following links to get back to programming!"
+    not_found_desc: "嗯，這裡沒有任何東西。 請進入下方的其中一個連結以回到編程頁面!"
     not_allowed: "方法不被允許。"
-    timeout: "伺服器超時。" # {change}
+    timeout: "伺服器超時。"
     conflict: "資源衝突。"
     bad_input: "錯誤輸入。"
     server_error: "伺服器錯誤。"
-    unknown: "未知錯誤。" # {change}
+    unknown: "未知錯誤。"
     error: "錯誤"
-#    general_desc: "Something went wrong, and it’s probably our fault. Try waiting a bit and then refreshing the page, or visit one of the following links to get back to programming!"
+    general_desc: "發生了某些錯誤，它很有可能是我們造成的。 請試著等待一小段時間然後再刷新這個頁面，或是進入下方的其中一個連結以回到編程頁面!"

   resources:
     level: "等級"
@@ -1898,107 +1898,107 @@
     campaigns: "征戰"

   concepts:
-#    advanced_css_rules: "Advanced CSS Rules"
-#    advanced_css_selectors: "Advanced CSS Selectors"
-#    advanced_html_attributes: "Advanced HTML Attributes"
-#    advanced_html_tags: "Advanced HTML Tags"
-#    algorithm_average: "Algorithm Average"
-#    algorithm_find_minmax: "Algorithm Find Min/Max"
-#    algorithm_search_binary: "Algorithm Search Binary"
-#    algorithm_search_graph: "Algorithm Search Graph"
-#    algorithm_sort: "Algorithm Sort"
-#    algorithm_sum: "Algorithm Sum"
+    advanced_css_rules: "進階CSS規則"
+    advanced_css_selectors: "進階CSS選擇器"
+    advanced_html_attributes: "進階HTML屬性"
+    advanced_html_tags: "進階HTML標籤"
+    algorithm_average: "算術平均"
+    algorithm_find_minmax: "算數尋找 最小/最大"
+    algorithm_search_binary: "算術搜尋二進位"
+    algorithm_search_graph: "算術搜尋圖表"
+    algorithm_sort: "算數排列"
+    algorithm_sum: "算數總和"
     arguments: "引數"
     arithmetic: "四則運算"
-#    array_2d: "2D Array"
-#    array_index: "Array Indexing"
-#    array_iterating: "Iterating Over Arrays"
-#    array_literals: "Array Literals"
-#    array_searching: "Array Searching"
-#    array_sorting: "Array Sorting"
-    arrays: "陣列"
-#    basic_css_rules: "Basic CSS rules"
-#    basic_css_selectors: "Basic CSS selectors"
-#    basic_html_attributes: "Basic HTML Attributes"
-#    basic_html_tags: "Basic HTML Tags"
+    array_2d: "2維矩陣"
+    array_index: "矩陣索引"
+    array_iterating: "重複矩陣"
+    array_literals: "矩陣文字"
+    array_searching: "矩陣搜尋"
+    array_sorting: "矩陣排列"
+    arrays: "矩陣"
+    basic_css_rules: "基礎CSS規則"
+    basic_css_selectors: "基礎CSS選擇器"
+    basic_html_attributes: "基礎HTML屬性"
+    basic_html_tags: "基礎HTML標籤"
     basic_syntax: "基本語法"
-#    binary: "Binary"
-#    boolean_and: "Boolean And"
-#    boolean_equality: "Boolean Equality"
-#    boolean_greater_less: "Boolean Greater/Less"
-#    boolean_logic_shortcircuit: "Boolean Logic Shortcircuiting"
-#    boolean_not: "Boolean Not"
-#    boolean_operator_precedence: "Boolean Operator Precedence"
-#    boolean_or: "Boolean Or"
-#    bootstrap: "Bootstrap"
-    break_statements: "break敘述"
-    classes: "類"
-    continue_statements: "continue敘述"
-#    dom_events: "DOM Events"
-#    dynamic_styling: "Dynamic Styling"
-#    event_concurrency: "Event Concurrency"
-#    event_data: "Event Data"
-#    event_handlers: "Event Handlers"
-    for_loops: "for迴圈"
-#    for_loops_nested: "Nested For Loops"
-#    for_loops_range: "For Loops Range"
+    binary: "二進位"
+    boolean_and: "Boolean And(布林運算 且)"
+    boolean_equality: "Boolean equality(布林運算 等式)"
+    boolean_greater_less: "Boolean greater/less(布林運算 大於/小於)"
+    boolean_logic_shortcircuit: "Boolean logic shortcircuit(布林運算 邏輯短迴路)"
+    boolean_not: "Boolean Not(布林運算 非)"
+    boolean_operator_precedence: "Boolean Operator Precedence(布林運算 算符級別)"
+    boolean_or: "Boolean Or(布林運算 或)"
+    bootstrap: "引導程序"
+    break_statements: "break(終止) 敘述"
+    classes: "類別"
+    continue_statements: "continue(繼續) 敘述"
+    dom_events: "DOM(文件物件模型) 事件"
+    dynamic_styling: "動態式樣"
+    event_concurrency: "事件開發性"
+    event_data: "事件資料"
+    event_handlers: "事件操作者"
+    for_loops: "For迴圈"
+    for_loops_nested: "巢狀 For迴圈"
+    for_loops_range: "範圍中 For迴圈"
     functions: "函數"
-#    game_ai: "Game AI"
-#    game_goals: "Game Goals"
-#    game_spawn: "Game Spawn"
+    game_ai: "遊戲 AI(人工智能)"
+    game_goals: "遊戲目標"
+    game_spawn: "遊戲結果"
     graphics: "圖形"
-#    graphs: "Graphs"
-#    heaps: "Heaps"
-#    if_else_statements: "If/Else Statements"
-    if_statements: "if敘述"
-#    if_statements_nested: "Nested If Statemnts"
-#    indexing: "Array Indexes"
-#    input_handling_flags: "Input Handling - Flags"
-#    input_handling_keyboard: "Input Handling - Keyboard"
-#    input_handling_mouse: "Input Handling - Mouse"
-#    intermediate_css_rules: "Intermediate CSS Rules"
-#    intermediate_css_selectors: "Intermediate CSS Selectors"
-#    intermediate_html_attributes: "Intermediate HTML Attributes"
-#    intermediate_html_tags: "Intermediate HTML Tags"
-#    jquery: "jQuery"
-#    jquery_animations: "jQuery Animations"
-#    jquery_filtering: "jQuery Element Filtering"
-#    jquery_selectors: "jQuery Selectors"
-#    length: "Array Length"
-#    math_geometry: "Geometry"
+    graphs: "圖表"
+    heaps: "統計"
+    if_else_statements: "If/Else(若/其他) 敘述"
+    if_statements: "If(若) 敘述"
+    if_statements_nested: "巢狀 If(若) 敘述"
+    indexing: "矩陣索引"
+    input_handling_flags: "輸入方式 - 旗標"
+    input_handling_keyboard: "輸入方式 - 鍵盤"
+    input_handling_mouse: "輸入方式 - 滑鼠"
+    intermediate_css_rules: "中階CSS規則"
+    intermediate_css_selectors: "中階CSS選擇器"
+    intermediate_html_attributes: "中階HTML屬性"
+    intermediate_html_tags: "中階HTML標籤"
+    jquery: "jQuery"
+    jquery_animations: "jQuery 動畫"
+    jquery_filtering: "jQuery 元素篩選"
+    jquery_selectors: "jQuery 選擇器"
+    length: "矩陣長度"
+    math_geometry: "幾何性質"
     math_operations: "數學運算"
-#    math_trigonometry: "Trigonometry"
+    math_trigonometry: "三角運算"
     object_literals: "對象常量"
     parameters: "參數"
-#    property_access: "Accessing Properties"
-#    property_assignment: "Assigning Properties"
-#    queues: "Data Structures - Queues"
-#    reading_docs: "Reading the Docs"
+    property_access: "取數性質"
+    property_assignment: "取數分配"
+    queues: "資料結構 - 列隊"
+    reading_docs: "讀取文件"
     recursion: "遞歸"
-#    return_statements: "Return Statements"
-#    stacks: "Data Structures - Stacks"
+    return_statements: "Return(回傳) 敘述"
+    stacks: "資料結構 - 堆疊"
     strings: "字串"
-#    strings_concatenation: "String Concatenation"
-#    strings_substrings: "Substring"
-#    trees: "Data Structures - Trees"
+    strings_concatenation: "字串鏈結"
+    strings_substrings: "子字串"
+    trees: "資料結構 - 樹狀結構"
     variables: "變數"
     vectors: "向量"
-#    while_condition_loops: "While Loops with Conditionals"
-#    while_loops_simple: "While Loops"
-#    while_loops_nested: "Nested While Loops"
-#    xy_coordinates: "Cartesian Coordinates"
+    while_condition_loops: "While(當) 含條件迴圈 "
+    while_loops_simple: "While(當) 迴圈"
+    while_loops_nested: "巢狀 While(當) 迴圈"
+    xy_coordinates: "Cartesian Coordinates(笛卡爾座標) "
     advanced_strings: "進階字串" # Rest of concepts are deprecated
     algorithms: "算法"
     boolean_logic: "布林邏輯"
-#    basic_html: "Basic HTML"
-#    basic_css: "Basic CSS"
-#    basic_web_scripting: "Basic Web Scripting"
-#    intermediate_html: "Intermediate HTML"
-#    intermediate_css: "Intermediate CSS"
-#    intermediate_web_scripting: "Intermediate Web Scripting"
-#    advanced_html: "Advanced HTML"
-#    advanced_css: "Advanced CSS"
-#    advanced_web_scripting: "Advanced Web Scripting"
+    basic_html: "基礎HTML"
+    basic_css: "基礎CSS"
+    basic_web_scripting: "基礎Web腳本"
+    intermediate_html: "中階HTML"
+    intermediate_css: "中階CSS"
+    intermediate_web_scripting: "中階Web腳本"
+    advanced_html: "進階HTML"
+    advanced_css: "進階CSS"
+    advanced_web_scripting: "進階Web腳本"
     input_handling: "輸入處理"
     while_loops: "while迴圈"


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -99,6 +99,6 @@
   equals obj.I.age, 1, "should successfully update"

 # TODO: tests to make sure value isn't overwritten if the instance variable already exists
-# TODO: tests to make sure value is defaulted to 100, but only if the instance variable doesn't exist
+# TODO: tests to make sure value is defaulted to 0, but only if the instance variable doesn't exist

 module()


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -111,4 +111,6 @@
   5.times ->
     obj.update()

+
+
 module()


Site - Default classification count for an archive

--- app/models/Archive.coffee
+++ app/models/Archive.coffee
@@ -20,6 +20,8 @@
     else
       @all()

+  classification_count: 0
+
   addBadges: =>
     for badge in badgeDefinitions
       if badge.collection is @slug()


handle paste

handle paste

--- src/line.coffee
+++ src/line.coffee
@@ -149,7 +149,7 @@
     return true

   resetContent: ->
-    @node.id = @id unless @node.id
+    @node.id = @id unless @node.id == @id
     @outerHTML = @node.outerHTML
     @length = 1
     ops = _.map(@leaves.toArray(), (leaf) =>


focus editor when click

focus editor when click

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -163,17 +163,8 @@
 		$('body').delegate '#tooltip', 'mouseleave', (e)->
 			self.hideTooltip()

-		# insertion enter image url
-		$('#insertion').delegate 'input', 'keydown', (e)->
-			if e.keyCode == 13
-				url = $('#insertion input').val()
-				$('#editor .hovered').after '

'
-				console.log $('#insertion .hovered').html()
-				$('#insertion .toolbar').removeClass 'clicked'
-				$('#insertion input').val ''
-				self.hideInsertion()
-				e.preventDefault()
-				e.stopPropagation()
+		$('body').delegate '#editor', 'click', (e)->
+			$(@).focus()

 	update: ->
 		self = @


border matching done, basic game logic finished

--- src/main.coffee
+++ src/main.coffee
@@ -125,6 +125,13 @@
             if bottom.range.top >= absY and top.range.left >= absX
                 return true

+        [fooOuter, barOuter] = for item in [foo, bar]
+            getOuterRange item, @grid.width(), @grid.height()
+
+        for key, value of fooOuter
+            if value is barOuter[key] is true
+                return true
+
         [fooMarks, barMarks] = (getMarkBits item for item in [foo, bar])

         if result = getBitAdd fooMarks.x, barMarks.x
@@ -174,6 +181,12 @@

         list

+    getOuterRange = (item, width, height) ->
+        top: item.point.y is item.range.top
+        bottom: item.point.y + item.range.bottom + 1 is height
+        left: item.point.x is item.range.left
+        right: item.point.x + item.range.right + 1 is width
+
     getMarkBits = (item) ->
         x: item.range.markX item.point
         y: item.range.markY item.point


doc dev flag

doc dev flag

--- src/main.coffee
+++ src/main.coffee
@@ -24,6 +24,7 @@

     Usage: atom [options] [file ..]
   """
+  options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
   options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
   options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
   args = options.argv


Minor fix.

Minor fix.

--- src/main.coffee
+++ src/main.coffee
@@ -161,15 +161,15 @@
   webServers = []
   if $nconf.get 'http:enabled'
     webServers.push $createWebServer {
-      secure: false
       host: $nconf.get 'http:host'
       port: $nconf.get 'http:port'
     }
   if $nconf.get 'https:enabled'
     webServers.push $createWebServer {
-      secure: true
       host: $nconf.get 'https:host'
       port: $nconf.get 'https:port'
+      certificate: $nconf.get 'https:certificate'
+      key: $nconf.get 'https:key'
     }

   # Static file serving (e.g. for XO-Web).


Fix logout not actually restarting YakYak.

--- src/main.coffee
+++ src/main.coffee
@@ -57,16 +57,10 @@
     log.info 'Logging out...'
     promise = client.logout()
     promise.then (res) ->
-        argv = process.argv
-        spawn = require('child_process').spawn
         # remove electron cookies
         mainWindow?.webContents?.session?.clearStorageData([], (data) -> console.log(data))
-        spawn argv.shift(), argv,
-            cwd: process.cwd
-            env: process.env
-            detached: true
-            stdio: 'inherit'
-        quit()
+        app.relaunch()
+        app.quit()
     return promise # like it matters

 seqreq = require './seqreq'


Working with watchify

Working with watchify

--- src/main.coffee
+++ src/main.coffee
@@ -8,6 +8,7 @@

 {items} = require('./model')
 {editor} = require('./editor')
+

 # Import SVG Tags for reactive-coffee 1.2.2; should be upstreamed in 1.2.4 or so

@@ -19,7 +20,7 @@
 main = ->

   $('body').append(
-    T.h1 "Rohan's Teenage Robot Turtles v2"
+    T.h1 "Rohan's Teenage Robot Turtles v4"
     S.svg {class: 'graphics'}, bind ->[
       S.rect {x:10, y:20, height:100, width:100, fill:"blue", stroke:"red"}, bind -> [
         S.animateTransform {


Increase test timeout

Increase test timeout

--- test/src/test.coffee
+++ test/src/test.coffee
@@ -2,3 +2,4 @@
 #= require_tree ./modules

 QUnit.config.hidepassed = false
+QUnit.config.testTimeout = 10000


refactoring node -> cd

refactoring node -> cd

--- src/node.coffee
+++ src/node.coffee
@@ -2,9 +2,9 @@
 alight.ChangeDetector = (scope) ->
     root = new Root()

-    node = new ChangeDetector root, scope or {}
-    root.topNode = node
-    node
+    cd = new ChangeDetector root, scope or {}
+    root.topCD = cd
+    cd

 Root = () ->
@@ -22,7 +22,7 @@
     @.extraLoop = false
     @.finishBinding_lock = false
     @.lateScan = false
-    @.topNode = null
+    @.topCD = null

     @

@@ -32,8 +32,8 @@
     @.watchers.finishBinding.length = 0
     @.watchers.finishScan.length = 0
     @.watchers.finishScanOnce.length = 0
-    if @.topNode
-        @.topNode.destroy()
+    if @.topCD
+        @.topCD.destroy()

 ChangeDetector = (root, scope) ->
@@ -60,47 +60,47 @@

 ChangeDetector::new = (scope) ->
     parent = @
-    node = new ChangeDetector parent.root, scope or parent.scope
-    node.parent = parent
-    parent.children.push node
-
-    node
+    cd = new ChangeDetector parent.root, scope or parent.scope
+    cd.parent = parent
+    parent.children.push cd
+
+    cd

 ChangeDetector::destroy = ->
-    node = @
-    root = node.root
-
-    if node.parent
-        removeItem node.parent.children, node
-
-    for child in node.children.slice()
+    cd = @
+    root = cd.root
+
+    if cd.parent
+        removeItem cd.parent.children, cd
+
+    for child in cd.children.slice()
         child.destroy()

-    for fn in node.destroy_callbacks
+    for fn in cd.destroy_callbacks
         fn()

-    node.destroy_callbacks.length = 0
-    node.watchList.length = 0
-    watchers = node.watchers
-    node.watchers = {}
+    cd.destroy_callbacks.length = 0
+    cd.watchList.length = 0
+    watchers = cd.watchers
+    cd.watchers = {}
     # call watch.onStop
     for k, d of watchers
         if d.onStop.length
             for fn in d.onStop
                 fn()

-    for wa in node.rwatchers.any
+    for wa in cd.rwatchers.any
         removeItem root.watchers.any, wa
-    node.rwatchers.any.length = 0
-    for wa in node.rwatchers.finishScan
+    cd.rwatchers.any.length = 0
+    for wa in cd.rwatchers.finishScan
         removeItem root.watchers.finishScan, wa
-    node.rwatchers.finishScan.length = 0
-
-    if node.lineActive
-        node.lineActive = false
-        p = node.prevSibling
-        n = node.nextSibling
+    cd.rwatchers.finishScan.length = 0
+
+    if cd.lineActive
+        cd.lineActive = false
+        p = cd.prevSibling
+        n = cd.nextSibling
         if p
             p.nextSibling = n
         else
@@ -112,8 +112,8 @@
             # last scope
             root.nodeTail = p

-    if root.topNode is node
-        root.topNode = null
+    if root.topCD is cd
+        root.topCD = null
         root.destroy()

@@ -122,8 +122,8 @@
     getId = ->
         'wf' + (index++)

-    (node, pe, baseCallback, option) ->
-        root = node.root
+    (cd, pe, baseCallback, option) ->
+        root = cd.root

         # watchMode: simple, deep, array
         if option.isArray
@@ -145,9 +145,9 @@
                 filterName = filterExp
                 filterArg = null

-            filterBuilder = alight.getFilter filterName, node, filterArg
-
-            filter = filterBuilder filterArg, node,
+            filterBuilder = alight.getFilter filterName, cd, filterArg
+
+            filter = filterBuilder filterArg, cd,
                 setValue: prevCallback

             if f$.isFunction filter
@@ -172,7 +172,7 @@
             watchOptions.isArray = true
         else if watchMode is 'deep'
             watchOptions.deep = true
-        w = node.watch pe.expression, prevCallback, watchOptions
+        w = cd.watch pe.expression, prevCallback, watchOptions

         w.value = undefined
         w
@@ -181,17 +181,17 @@
 WA = (callback) ->
     @.cb = callback

-watchAny = (node, key, callback) ->
-    root = node.root
+watchAny = (cd, key, callback) ->
+    root = cd.root

     wa = new WA callback

-    node.rwatchers[key].push wa
+    cd.rwatchers[key].push wa
     root.watchers[key].push wa

     return {
         stop: ->
-            removeItem node.rwatchers[key], wa
+            removeItem cd.rwatchers[key], wa
             removeItem root.watchers[key], wa
     }

@@ -219,9 +219,9 @@
         option =
             isArray: true

-    node = @
-    root = node.root
-    scope = node.scope
+    cd = @
+    root = cd.root
+    scope = cd.scope

     if f$.isFunction name
         exp = name
@@ -242,13 +242,13 @@
             name = 'root.private.' + name
         key = name
         if key is '$any'
-            return watchAny node, 'any', callback
+            return watchAny cd, 'any', callback
         if key is '$finishScan'
-            return watchAny node, 'finishScan', callback
+            return watchAny cd, 'finishScan', callback
         if key is '$finishScanOnce'
             return root.watchers.finishScanOnce.push callback
         if key is '$destroy'
-            return node.destroy_callbacks.push callback
+            return cd.destroy_callbacks.push callback
         if key is '$finishBinding'
             return root.watchers.finishBinding.push callback
         if option.deep
@@ -261,7 +261,7 @@
     if alight.debug.watch
         console.log '$watch', name

-    d = node.watchers[key]
+    d = cd.watchers[key]
     if d
         if not option.readOnly
             d.extraLoop = true
@@ -276,7 +276,7 @@
             else
                 pe = alight.utils.parsExpression name
                 if pe.result.length > 1  # has filters
-                    return makeFilterChain node, pe, callback, option
+                    return makeFilterChain cd, pe, callback, option
                 ce = alight.utils.compile.expression(name)
                 isStatic = ce.isSimple and ce.simpleVariables.length is 0 and not option.isArray
                 exp = ce.fn
@@ -303,17 +303,17 @@
             returnValue = d.value

         if not isStatic
-            node.watchers[key] = d
-            node.watchList.push d
+            cd.watchers[key] = d
+            cd.watchList.push d
             # insert scope into root-chain
-            if not node.lineActive
-                node.lineActive = true
+            if not cd.lineActive
+                cd.lineActive = true
                 t = root.nodeTail
                 if t
-                    root.nodeTail = t.nextSibling = node
-                    node.prevSibling = t
+                    root.nodeTail = t.nextSibling = cd
+                    cd.prevSibling = t
                 else
-                    root.nodeHead = root.nodeTail = node
+                    root.nodeHead = root.nodeTail = cd

     r =
         $: d
@@ -342,8 +342,8 @@
             return
         # remove watch
         if not d.isStatic
-            delete node.watchers[key]
-            removeItem node.watchList, d
+            delete cd.watchers[key]
+            removeItem cd.watchList, d
         if option.onStop
             option.onStop()

@@ -382,13 +382,13 @@
     changes = 0
     total = 0

-    node = root.nodeHead
-    while node
-        scope = node.scope
+    cd = root.nodeHead
+    while cd
+        scope = cd.scope

         # default watchers
-        total += node.watchList.length
-        for w in node.watchList.slice()
+        total += cd.watchList.length
+        for w in cd.watchList.slice()
             result.src = w.src
             last = w.value
             value = w.exp scope
@@ -428,7 +428,7 @@
                 if alight.debug.scan > 1
                     console.log 'changed:', w.src

-        node = node.nextSibling
+        cd = cd.nextSibling

     result.total = total
     result.changes = changes
@@ -515,11 +515,11 @@
     @.root.scan option

 ChangeDetector::setValue = (name, value) ->
-    node = @
-    fn = node.compile name + ' = $value',
+    cd = @
+    fn = cd.compile name + ' = $value',
         input: ['$value']
         no_return: true
-    fn node.scope, value
+    fn cd.scope, value

 ChangeDetector::eval = (exp) ->
     fn = @.compile exp


Test all the things

Test all the things

--- test/tests.js.coffee
+++ test/tests.js.coffee
@@ -1,7 +1,411 @@
 assert = require 'assert'
-
-describe 'Array', ->
-  describe '#indexOf()', ->
-    it 'should return -1 when the value is not present', ->
-      assert.equal(-1, [1,2,3].indexOf(5))
-      assert.equal(-1, [1,2,3].indexOf(0))
+sinon = require 'sinon'
+ActionQueue = require '../index'
+
+describe 'ActionQueue', ->
+  testAction = (action, numArgs) ->
+    numArgs = numArgs || 0
+    assert(action, "action should be defined")
+    assert(typeof action.func == 'function', "action should have a function")
+    assert(action.args instanceof Array, "action's args should be an array")
+    assert(action.args.length == numArgs, "action should have "+numArgs+" arguments")
+
+  testActions = (actions, numActions) ->
+    assert(actions instanceof Array, "actions should be an array")
+    assert(actions.length == numActions, "there should be "+numActions+" actions")
+
+  testSteps = (steps, numSteps) ->
+    assert(steps instanceof Array, "steps should be an array")
+    assert(steps.length == numSteps, "should have "+numSteps+" steps")
+
+  constructQueue = ->
+    queue = new ActionQueue()
+    queue.addAction ->
+      console.log('step 1, action 1: arguments:\n', arguments)
+    queue.addAction ->
+      console.log('step 1, action 2: arguments:\n', arguments)
+    , ['arg1', 'arg2']
+    queue.endStep()
+    queue.addAction ->
+      console.log('step 2, action 1: arguments, this:\n', arguments, this);
+    queue.addAction ->
+      console.log('step 2, action 2: arguments:\n', arguments)
+    , ['arg1', 'arg2']
+    queue.endStep()
+
+
+  describe '#constructor()', ->
+
+    it "should create an action queue", ->
+      queue = new ActionQueue()
+      assert(queue)
+
+    it "should not be initially waiting", ->
+      queue = new ActionQueue()
+      state = queue.state()
+      assert(state.Waiting == false)
+
+    it "should have no inital steps", ->
+      queue = new ActionQueue()
+      state = queue.state()
+      assert.deepEqual(state.Steps, [])
+
+  describe '#addAction()', ->
+    it "should be able to add an action without arguments to the current step", ->
+      queue = new ActionQueue()
+      queue.addAction ->
+        console.log('action 1: arguments:\n', arguments)
+      state = queue.state()
+
+      assert(state['Current step'].length == 1, "queue should have 1 action in the current step")
+      testAction(state['Current step'][0], 0)
+
+    it "should be able to add an action with arguments to the current step", ->
+      queue = new ActionQueue()
+      queue.addAction ->
+        console.log('action 1: arguments:\n', arguments)
+      , ['arg1', 'arg2']
+      state = queue.state()
+
+      assert(state['Current step'].length == 1)
+      testAction(state['Current step'][0], 2)
+
+    it "must be provided a function as the basis for the action", ->
+      queue = new ActionQueue()
+      assert.throws ->
+        queue.addAction()
+      , "should throw an exception when the first argument passed in isn't a function"
+
+    it "should be able to add multiple actions to the current step", ->
+      queue = new ActionQueue()
+      queue.addAction ->
+        console.log('action 1: arguments:\n', arguments)
+      queue.addAction ->
+        console.log('action 2: arguments:\n', arguments)
+      , ['arg1', 'arg2']
+      state = queue.state()
+
+      assert.deepEqual(state.Steps, [], "steps should be an empty array")
+
+      # Testing current step
+      testActions(state['Current step'], 2)
+
+      # Testing first action
+      testAction(state['Current step'][0], 0)
+
+      # Testing second action
+      testAction(state['Current step'][1], 2)
+
+  describe '#endStep', ->
+    it 'should push all actions to the next step', ->
+      queue = new ActionQueue()
+      queue.addAction ->
+        console.log('action 1: arguments:\n', arguments)
+      queue.addAction ->
+        console.log('action 2: arguments:\n', arguments)
+      , ['arg1', 'arg2']
+      queue.endStep()
+
+      state = queue.state()
+
+      # Current step should be empty
+      actions = state['Current step']
+      assert.deepEqual(actions, [])
+
+      # Testing steps
+      steps = state.Steps
+      testSteps(steps, 1)
+
+      # Testing first step
+      actions = steps[0]
+      testActions(actions, 2)
+
+      # Testing first action
+      testAction(actions[0], 0)
+
+      # Testing second action
+      testAction(actions[1], 2)
+
+    it "should be able to queue up multiple actions after we've ended a step", ->
+      queue = new ActionQueue()
+      queue.addAction ->
+        console.log('step 1, action 1: arguments:\n', arguments)
+      queue.addAction ->
+        console.log('step 1, action 2: arguments:\n', arguments)
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction ->
+        console.log('step 2, action 1: arguments, this:\n', arguments, this);
+      queue.addAction ->
+        console.log('step 2, action 2: arguments:\n', arguments)
+      , ['arg1', 'arg2']
+
+      state = queue.state()
+
+      # Current step should have 2 actions
+      actions = state['Current step']
+      assert(actions.length == 2, "should have 2 actions")
+
+      # Testing steps
+      steps = state.Steps
+      testSteps(steps, 1)
+
+      # Testing first step
+      actions = steps[0]
+      testActions(actions, 2)
+
+      # Testing first action
+      testAction(actions[0], 0)
+
+      # Testing second action
+      testAction(actions[1], 2)
+
+    it "should be able to end multiple steps", ->
+      queue = constructQueue()
+      state = queue.state()
+
+      # Current step should have 2 actions
+      actions = state['Current step']
+      assert.deepEqual(actions, [], "there should be no actions in the current step")
+
+      # Testing steps
+      steps = state.Steps
+      testSteps(steps, 2)
+
+      # Testing first step
+      actions = steps[0]
+      testActions(actions, 2)
+      testAction(actions[0], 0)
+      testAction(actions[1], 2)
+
+      # Testing second step
+      actions = steps[1]
+      testActions(actions, 2)
+      testAction(actions[0], 0)
+      testAction(actions[1], 2)
+
+  describe "#wait", ->
+    it "should set an internal wait flag", ->
+      queue = constructQueue()
+      queue.wait()
+      state = queue.state()
+
+      assert(state.Waiting == true, "wait flag should be true")
+
+    it "should stop waiting", ->
+      queue = constructQueue()
+      queue.wait()
+      queue.complete()
+      state = queue.state()
+
+      assert(state.Waiting == false, "wait flag should be true")
+
+    it "should accept a callback", ->
+      queue = constructQueue()
+      onComplete = sinon.spy()
+      queue.wait onComplete
+      state = queue.state()
+
+      state.Callback()
+      assert(onComplete.called, "wait callback should have been called")
+
+    it "callback should be called when waiting is over", ->
+      queue = constructQueue()
+      onComplete = sinon.spy()
+      queue.wait onComplete
+      state = queue.state()
+
+      queue.complete()
+      assert(onComplete.called, "wait callback should have been called")
+
+  describe "#runNextStep", ->
+    it "should not call queued up actions if waiting", ->
+      queue = new ActionQueue()
+      step1action1 = sinon.spy()
+      step1action2 = sinon.spy()
+      step2action1 = sinon.spy()
+      step2action2 = sinon.spy()
+      queue.addAction step1action1
+      queue.addAction step1action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction step2action1
+      queue.addAction step2action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      onComplete = sinon.spy()
+      queue.wait onComplete
+      queue.runNextStep()
+
+      assert(!step1action1.called, "step1action1 was called, but shouldn't have been")
+      assert(!step1action2.called, "step1action2 was called, but shouldn't have been")
+      assert(!step2action1.called, "step2action1 was called, but shouldn't have been")
+      assert(!step2action2.called, "step2action2 was called, but shouldn't have been")
+
+    it "should call queued up actions in first step if not waiting", ->
+      queue = new ActionQueue()
+      step1action1 = sinon.spy()
+      step1action2 = sinon.spy()
+      step2action1 = sinon.spy()
+      step2action2 = sinon.spy()
+      queue.addAction step1action1
+      queue.addAction step1action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction step2action1
+      queue.addAction step2action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(!step2action1.called, "step2action1 was called, but shouldn't have been")
+      assert(!step2action2.called, "step2action2 was called, but shouldn't have been")
+
+    it "should call queued up actions in multiple steps if called repeatedly", ->
+      queue = new ActionQueue()
+      step1action1 = sinon.spy()
+      step1action2 = sinon.spy()
+      step2action1 = sinon.spy()
+      step2action2 = sinon.spy()
+      queue.addAction step1action1
+      queue.addAction step1action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction step2action1
+      queue.addAction step2action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(!step2action1.called, "step2action1 was called, but shouldn't have been")
+      assert(!step2action2.called, "step2action2 was called, but shouldn't have been")
+
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(step2action1.called, "step2action1 wasn't called, but should've been")
+      assert(step2action2.called, "step2action2 wasn't called, but should've been")
+
+    it "should call queued up actions in multiple steps if called repeatedly", ->
+      queue = new ActionQueue()
+      step1action1 = sinon.spy()
+      step1action2 = sinon.spy()
+      step2action1 = sinon.spy()
+      step2action2 = sinon.spy()
+      currentStepAction1 = sinon.spy()
+      queue.addAction step1action1
+      queue.addAction step1action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction step2action1
+      queue.addAction step2action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction currentStepAction1
+
+      state = queue.state()
+      steps = state.Steps
+      testSteps(steps, 2)
+      testActions(state['Current step'], 1)
+
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(!step2action1.called, "step2action1 was called, but shouldn't have been")
+      assert(!step2action2.called, "step2action2 was called, but shouldn't have been")
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+
+      state = queue.state()
+      steps = state.Steps
+      testSteps(steps, 1)
+
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(step2action1.called, "step2action1 wasn't called, but should've been")
+      assert(step2action2.called, "step2action2 wasn't called, but should've been")
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+
+      state = queue.state()
+      steps = state.Steps
+      testSteps(steps, 0)
+
+      queue.runNextStep()
+
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+
+    it "should call queued up actions in multiple steps, but not if waiting", ->
+      queue = new ActionQueue()
+      step1action1 = sinon.spy()
+      step1action2 = sinon.spy()
+      step2action1 = sinon.spy()
+      step2action2 = sinon.spy()
+      currentStepAction1 = sinon.spy()
+      queue.addAction step1action1
+      queue.addAction step1action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction step2action1
+      queue.addAction step2action2
+      , ['arg1', 'arg2']
+      queue.endStep()
+      queue.addAction currentStepAction1
+      onComplete = sinon.spy()
+      queue.wait onComplete
+
+      state = queue.state()
+      steps = state.Steps
+      testSteps(steps, 2)
+      testActions(state['Current step'], 1)
+
+      queue.runNextStep()
+
+      assert(!step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(!step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(!step2action1.called, "step2action1 was called, but shouldn't have been")
+      assert(!step2action2.called, "step2action2 was called, but shouldn't have been")
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+      assert(!onComplete.called, "onComplete was called, but shouldn't have been")
+
+      queue.complete()
+
+      state = queue.state()
+      steps = state.Steps
+      testSteps(steps, 2)
+
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(!step2action1.called, "step2action1 wasn't called, but should've been")
+      assert(!step2action2.called, "step2action2 wasn't called, but should've been")
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+      assert(onComplete.called, "onComplete was called, but shouldn't have been")
+
+      state = queue.state()
+      steps = state.Steps
+      testSteps(steps, 1)
+
+      queue.runNextStep()
+
+      assert(step1action1.called, "step1action1 wasn't called, but should've been")
+      assert(step1action2.called, "step1action2 wasn't called, but should've been")
+      assert(step2action1.called, "step2action1 wasn't called, but should've been")
+      assert(step2action2.called, "step2action2 wasn't called, but should've been")
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+      assert(onComplete.called, "onComplete was called, but shouldn't have been")
+
+      queue.runNextStep()
+
+      assert(!currentStepAction1.called, "currentStepAction1 was called, but shouldn't have been")
+      assert(onComplete.called, "onComplete was called, but shouldn't have been")
+
+


fixing indents

fixing indents

--- src/poll.coffee
+++ src/poll.coffee
@@ -14,47 +14,47 @@
 #   logikz

 module.exports = (robot) ->
-    generateId = () ->
-        text = ""
-        possibleValues = "0123456789abcdef"
-        text += possibleValues.charAt(Math.floor(Math.random() * possibleValues.length)) for [1..5]
+	generateId = ->
+		text = ""
+		possibleValues = "0123456789abcdef"
+		text += possibleValues.charAt(Math.floor(Math.random() * possibleValues.length)) for [1..5]

-    parseOptions = (optionsString) ->
-        (optionsString.split "option: ")[1..]
+	parseOptions = (optionsString) ->
+		(optionsString.split "option: ")[1..]

-    createPoll = (topic, options) ->
-        @robot.logger.info "Creating poll with #{topic}"
-        id = generateId()
-        pollOptions = parseOptions options
-        @robot.logger.info "We have #{pollOptions.length()} options"
-        @robot.brain.data.poll[id].topic = topic
-        optionsString = ""
+	createPoll = (topic, options) ->
+		@robot.logger.info "Creating poll with #{topic}"
+		id = generateId()
+		pollOptions = parseOptions options
+		@robot.logger.info "We have #{pollOptions.length()} options"
+		@robot.brain.data.poll[id].topic = topic
+		optionsString = ""

-        for pollOption in pollOptions
-            index = pollOptions.indexOf(pollOption)
-            robot.brain.data.poll[id].option[index].text
-            optionsString += "\t#{index}: #{pollOption}\n"
-        @robot.brain.save()
-        @robot.logger.info "Brain saved"
+		for pollOption in pollOptions
+			index = pollOptions.indexOf(pollOption)
+			robot.brain.data.poll[id].option[index].text
+			optionsString += "\t#{index}: #{pollOption}\n"
+		@robot.brain.save()
+		@robot.logger.info "Brain saved"

-        text =  """
-                POLL ID: #{id}
-                Topic: #{topic}
-                Options:
-                #{optionsString}
-                """
+		text =  """
+				POLL ID: #{id}
+				Topic: #{topic}
+				Options:
+				#{optionsString}
+				"""

-    robot.respond /start poll (.*?)\s(option:\s?.*)+/i, (msg) ->
-        @robot.logger.info "Create poll called: #{msg}"
-        topic = msg.match[1]
-        @robot.logger.info "Topic: #{topic}"
-        options = msg.match[2]
-        @robot.logger.info "options: #{options}"
-        response = createPoll topic, options
-        msg.send "@channel: #{response}"
+	robot.respond /start poll (.*?)\s(option:\s?.*)+/i, (msg) ->
+		@robot.logger.info "Create poll called: #{msg}"
+		topic = msg.match[1]
+		@robot.logger.info "Topic: #{topic}"
+		options = msg.match[2]
+		@robot.logger.info "options: #{options}"
+		response = createPoll topic, options
+		msg.send "@channel: #{response}"

-    robot.respond /poll ping/i, (msg) ->
-        @robot.logger.info "ping called"
-        msg.reply "poll pong"
+	robot.respond /poll ping/i, (msg) ->
+		@robot.logger.info "ping called"
+		msg.reply "poll pong"


Test for adding an AdHoc card to MMT

Test for adding an AdHoc card to MMT

--- test/javascripts/integration/manuscript_manager_template_test.js.coffee
+++ test/javascripts/integration/manuscript_manager_template_test.js.coffee
@@ -19,8 +19,8 @@
   adminJournalPayload = ef.createPayload()
   adminJournalPayload.addRecords(records)
   adminJournalsResponse = adminJournalPayload.toJSON()
+  admin = ef.createRecord('User', admin: true)

-  admin = ef.createRecord('User', admin: true)
   server.respondWith 'GET', "/admin/journals", [
     200, {"Content-Type": "application/json"}, JSON.stringify(adminJournalsResponse)
   ]
@@ -39,3 +39,33 @@
     .then -> Em.$(columnTitleSelect).html('Shazam!')
   andThen ->
     ok exists find 'h2.column-title:contains("Shazam!")'
+
+test 'Adding an Ad-Hoc card', ->
+  ef = ETahi.Factory
+  records = createJournalWithTaskTemplate
+    kind: "Task"
+    title: "Ad Hoc"
+  adminJournalPayload = ef.createPayload()
+  adminJournalPayload.addRecords(records)
+  adminJournalsResponse = adminJournalPayload.toJSON()
+  admin = ef.createRecord('User', admin: true)
+
+  server.respondWith 'GET', "/admin/journals", [
+    200, {"Content-Type": "application/json"}, JSON.stringify(adminJournalsResponse)
+  ]
+
+  server.respondWith 'GET', "/users/#{admin.id}", [
+    200
+    'Content-Type': 'application/json'
+    JSON.stringify {user: admin}
+  ]
+
+  visit("/admin/journals/1/manuscript_manager_templates/1/edit")
+  click 'a.button--green:contains("Add New Card")'
+    .then -> ok exists find '.chosen-container.task-type-select'
+  pickFromChosenSingle '.task-type-select', 'Ad Hoc'
+  click '.button--green:contains("Add")'
+    .then -> ok exists find 'h1.inline-edit:contains("Ad Hoc")'
+  click '.button--green.overlay-close-button'
+  andThen ->
+    ok exists find '.column .card-content:contains("Ad Hoc")'


don't collect functions in loop

don't collect functions in loop

--- exifexodus.coffee
+++ exifexodus.coffee
@@ -188,6 +188,8 @@
         xhrOpen.call xhr, form.getAttribute('method') or 'GET', action
         xhrSend.call xhr, formData

+  null
+

 init = -> addEventListener 'submit', onSubmit, true


Use HTTP URL instead of local path

Use HTTP URL instead of local path

This cuts 14 seconds off the spec time for some reason
and exercises the same feature in a similar way.

--- spec/apm-cli-spec.coffee
+++ spec/apm-cli-spec.coffee
@@ -60,11 +60,10 @@
     afterEach ->
       server.close()

-    describe 'when a path is specified', ->
+    describe 'when a URL is specified', ->
       it 'installs the module at the path', ->
-        modulePath = path.join(__dirname, 'fixtures', 'test-module')
         callback = jasmine.createSpy('callback')
-        apm.run(['install', modulePath], callback)
+        apm.run(['install', "http://localhost:3000/test-module-1.0.0.tgz"], callback)

         waitsFor 'waiting for install to complete', 600000, ->
           callback.callCount > 0


Add coverage for scope#rule update tracking

--- spec/grammar-spec.coffee
+++ spec/grammar-spec.coffee
@@ -394,6 +394,12 @@
             tokens = registry.decodeTokens(line, tags)
             expect(tokens[0]).toEqual value: 'enumerate', scopes: ["test.include-external-repository-rule", "support.function.builtin.python"]

+            updateCallback = jasmine.createSpy('updateCallback')
+            grammar.onDidUpdate(updateCallback)
+            expect(grammar.grammarUpdated('source.python')).toBe true
+            expect(grammar.grammarUpdated('not.included')).toBe false
+            expect(updateCallback.callCount).toBe 1
+
         describe "when a grammar matching the desired scope is unavailable", ->
           it "updates the grammar if a matching grammar is added later", ->
             registry.removeGrammarForScopeName('text.html.basic')


add spec

add spec

--- spec/specs/update.coffee
+++ spec/specs/update.coffee
@@ -21,6 +21,10 @@
           0,
           [one, two],  (i, a, b) -> [i,a,b])
       [0, [0,1,2]], unstable)
+  describe "Works with naked streams (not wrapped in array)", ->
+    expectPropertyEvents(
+      -> Bacon.update(2, once(1), (a,b) -> a + b)
+      [2, 3], unstable)
   it "Rejects patterns with Properties only", -> expectError("At least one EventStream required", ->
     Bacon.update(0, [Bacon.constant()], ->))
   it "toString", ->


Update ArticlesGraph.coffee

Update ArticlesGraph.coffee

--- src/ArticlesGraph.coffee
+++ src/ArticlesGraph.coffee
@@ -6,10 +6,7 @@

 class ArticlesGraph
     constructor: ->
-                    #console.log('in ArticlesGraph ctor')
                     @dbPath     = './db'
-                    #@level      = levelup   (@dbPath)
-                    #@db         = levelgraph(@level)
                     @level      = null
                     @db         = null
                     @dataFile   = './src/article-data.json'


fixed bug in task type creation

fixed bug in task type creation

--- app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
+++ app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
@@ -157,7 +157,6 @@

     event.preventDefault()

-    debugger
     if not @ui.form[0].checkValidity()
       Toast.error("Please supply all needed values.")
       return
@@ -168,7 +167,7 @@

     # Add 'required' attribute to select once it's supported
     # https://github.com/davidstutz/bootstrap-multiselect/issues/620
-    if _.isEmpty(formValues.allowedModes)
+    if _.isEmpty(formValues.settings.allowedModes)
       Toast.error("Please provide at least one allowed mode.")
       return


fixed text area value for task types

fixed text area value for task types

--- app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
+++ app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
@@ -35,7 +35,7 @@

               Description

-              <textarea id="description" name="description" class="form-control" value="<%- description %>"></textarea>
+              <textarea id="description" name="description" class="form-control"><%- description %></textarea>


Fixed issue#419 - Unable to set owner + customer in postmaster/email filter.

--- app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee
+++ app/assets/javascripts/app/controllers/_ui_element/postmaster_set.coffee
@@ -16,8 +16,11 @@
             relation: 'TicketState'
           },
           {
-            value:    'x-zammad-ticket-customer'
+            value:    'x-zammad-ticket-customer_id'
             name:     'Customer'
+            relation: 'User'
+            tag:      'user_autocompletion'
+            disableCreateUser: true,
           },
           {

@@ -26,10 +29,11 @@
             relation: 'Group'
           },
           {
-            value:    'x-zammad-ticket-owner'
+            value:    'x-zammad-ticket-owner_id'
             name:     'Owner'
-            relation: 'User',
-            tag:      'user_autocompletion',
+            relation: 'User'
+            tag:      'user_autocompletion'
+            disableCreateUser: true,
           },
           {
             value:    'x-zammad-ignore'


Change blacklist picture

Change blacklist picture

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -20,9 +20,12 @@
       # NSFW Filter
       b = safebooru unless d.data.allowNSFW or msg.channel.name.indexOf('nsfw') >= 0
       # Blacklisted tags (discord community guidelines)
-      blacklist = ['loli', 'shota', 'gore', 'guro', 'lolicon']
       for tag in tags.split(' ')
-        if tag in blacklist then return msg.reply 'nope', false, {
+        if tag in ['loli', 'shota', 'lolicon'] then return msg.reply '', false, {
+          image:
+            url: 'https://cdn.discordapp.com/attachments/244581077610397699/315655455143886850/Screenshot_from_2017-05-20_21-01-03.png'
+        }
+        if tag in ['gore', 'guro'] then return msg.reply 'nope', false, {
           image:
             url: 'http://25.media.tumblr.com/tumblr_lqhsh2zVkZ1qjlcvoo1_500.jpg'
         }


Change to start with many blobs, not one

--- src/EngineBuilder.coffee
+++ src/EngineBuilder.coffee
@@ -19,18 +19,16 @@
         y:0,
         radius:240
       .c 'boundary',{}
-      ###
-      for(var i = 0; i < 200; ++i) {
-        engine.e().c('pos', {
-          x:Math.random()*400-200,
-          y:Math.random()*400-200,
-          radius:8
-        }).c('blob', {
-          velX:Math.random()*60-30,
-          velY:Math.random()*60-30,
+      for i in [0..200]
+        engine.e().c 'pos',
+          x: Math.random()*400-200
+          y: Math.random()*400-200
+          radius: 8
+        .c 'blob',
+          velX: Math.random()*60-30
+          velY: Math.random()*60-30
           weight: 64
-        }).c('render', {});
-      }
+        .c 'render', {}
       ###
       engine.e().c 'pos',
         x:Math.random()*400-200
@@ -40,6 +38,7 @@
         velY:Math.random()*60-30
         weight: 10000
       .c 'render', {}
+      ###

   engine.a 'blobSplit', require('./Blob').splitAction


Allow disable even if plugin disabled

Allow disable even if plugin disabled

--- index.cjsx
+++ index.cjsx
@@ -238,6 +238,11 @@

 SecretarySettingArea = React.createClass
   handleDisable: ->
+    # Write config
+    for key, id of CONFIG
+      config.set(key, null)
+    config.set('plugin.secretary.ship', -1)
+    # Dispatch event
     event = new Event 'secretary.config.disable'
     window.dispatchEvent event


fix "horizonal"

fix "horizonal"

--- index.cjsx
+++ index.cjsx
@@ -16,17 +16,17 @@
   extension: '.json'
 i18n.setLocale(window.language)

-row = if layout == 'horizonal' then 6 else 3
-shipRow = if layout == 'horizonal' then 12 else 5
-mapRow = if layout == 'horizonal' then 9 else 5
-rankRow = if layout == 'horizonal' then 3 else 2
+row = if layout == 'horizontal' then 6 else 3
+shipRow = if layout == 'horizontal' then 12 else 5
+mapRow = if layout == 'horizontal' then 9 else 5
+rankRow = if layout == 'horizontal' then 3 else 2

 window.addEventListener 'layout.change', (e) ->
   {layout} = e.detail
-  row = if layout == 'horizonal' then 6 else 3
-  shipRow = if layout == 'horizonal' then 12 else 5
-  mapRow = if layout == 'horizonal' then 9 else 5
-  rankRow = if layout == 'horizonal' then 3 else 2
+  row = if layout == 'horizontal' then 6 else 3
+  shipRow = if layout == 'horizontal' then 12 else 5
+  mapRow = if layout == 'horizontal' then 9 else 5
+  rankRow = if layout == 'horizontal' then 3 else 2

 exp = [
   0, 0, 100, 300, 600, 1000, 1500, 2100, 2800, 3600,


support globing in modules option

support globing in modules option

--- src/tree.coffee
+++ src/tree.coffee
@@ -2,6 +2,7 @@
 crypto = require 'crypto'
 util = require 'util'
 {EventEmitter} = require 'events'
+multimatch = require 'multimatch'
 load = require './load'
 {flatten} = require './misc'

@@ -70,11 +71,11 @@
     # Filter with the modules options
     if options.modules.length
       modules = tree.map (leaf) -> leaf.module
-      modules = options.modules.filter (module) -> modules.indexOf(module) isnt -1
+      modules = multimatch modules, options.modules
       tree = @load_tree modules
       # Filter with the fast options
-      tree = tree.filter( (leaf) =>
-        return true if options.modules.indexOf(leaf.module) isnt -1
+      tree = tree.filter( (leaf) =>
+        return true if multimatch(leaf.module, options.modules).length
         leaf.actions = leaf.actions.filter (action) =>
           action.required
         leaf.actions.length
@@ -101,7 +102,17 @@
     callback null, mods

 ###
-Return a array of object with the module name and its associated actions
+Return a array of objects with the module name and its associated actions.
+
+```json
+[ { module: 'masson/bootstrap/cache_memory',
+    actions: [ [Object], [Object] ] },
+  { module: 'masson/bootstrap/mecano',
+    actions: [ [Object], [Object] ] },
+  { module: 'masson/bootstrap/', actions: [] },
+  { module: 'masson/commons/java',
+    actions: [ [Object], [Object], [Object], [Object], [Object], [Object] ] } ]
+```
 ###
 Tree::load_tree = (modules) ->
   called = {}


fix serialize failure (Collection, Dictionary)

--- src/util.coffee
+++ src/util.coffee
@@ -67,12 +67,12 @@
             return val if not val? or typeof val isnt 'object'

             if Array.isArray val
-                return (attachClassName(item) for item in val)
+                return (attachClassName(item, inModel) for item in val)

             ret = {}
             isModel = val.constructor.className?
             Object.keys(val).forEach (key) ->
-                ret[key] = attachClassName(val[key], isModel)
+                ret[key] = attachClassName(val[key], isModel || inModel)

             if val instanceof Error
                 ret.stack = val.stack


Minor refactoring to make application.coffee code order more similar to Ozaria

--- app/core/application.coffee
+++ app/core/application.coffee
@@ -60,16 +60,6 @@
     store.commit('me/updateUser', me.attributes)
     store.commit('updateFeatures', features)

-    if me.showChinaRemindToast()
-      setInterval ( -> noty {
-        text: '你已经练习了一个小时了，建议休息一会儿哦'
-        layout: 'topRight'
-        type:'warning'
-        killer: false
-        timeout: 5000
-        }), 3600000  # one hour
-
-
     @store = store
     @api = api

@@ -94,6 +84,7 @@
     CocoModel.pollAchievements()
     unless me.get('anonymous')
       @checkForNewAchievement()
+    @remindPlayerToTakeBreaks()
     window.i18n = i18nextInstance = i18next.default.createInstance {
       lng: me.get('preferredLanguage', true)
       fallbackLng: locale.mapFallbackLanguages()
@@ -147,6 +138,15 @@
   setHocCampaign: (campaignSlug) -> storage.save('hoc-campaign', campaignSlug)
   getHocCampaign: -> storage.load('hoc-campaign')

+  remindPlayerToTakeBreaks: ->
+    return unless me.showChinaRemindToast()
+    setInterval ( -> noty {
+      text: '你已经练习了一个小时了，建议休息一会儿哦'
+      layout: 'topRight'
+      type:'warning'
+      killer: false
+      timeout: 5000
+      }), 3600000  # one hour
 }

 module.exports = Application


adding pattern matching for comprehensions.

--- nodes.coffee
+++ nodes.coffee
@@ -571,8 +571,10 @@
       else
         objects.push "$code, "
     objects: objects.join('')
-    ending: if objects.indexOf('\n') >= 0 then "\n$@tab]" else ']'
-    "[$objects$ending"
+    if objects.indexOf('\n') >= 0
+      "[\n${@idt(1)}$objects\n$@tab]"
+    else
+      "[$objects]"

 #### ClassNode

@@ -1124,9 +1126,7 @@
       index_var:    null
       source_part:  "$svar = ${ @source.compile(o) };\n$@tab"
       if @pattern
-        o.indent:   @idt 1
-        o.top:      true
-        var_part:   new AssignNode(@name, literal("$svar[$ivar]")).compile(o) + "\n"
+        var_part:   new AssignNode(@name, literal("$svar[$ivar]")).compile(merge o, {indent: @idt(1), top: true}) + "\n"
       else
         var_part:   "$body_dent$name = $svar[$ivar];\n" if name
       if not @object


fixing unfoldSoak performance my memoizing. #1033

--- nodes.coffee
+++ nodes.coffee
@@ -404,19 +404,22 @@

   # Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
   unfoldSoak: (o) ->
-    if ifn = @base.unfoldSoak o
-      Array::push.apply ifn.body.properties, @properties
-      return ifn
-    for prop, i in @properties when prop.soak
-      prop.soak = off
-      fst = new Value @base, @properties.slice 0, i
-      snd = new Value @base, @properties.slice i
-      if fst.isComplex()
-        ref = new Literal o.scope.freeVariable 'ref'
-        fst = new Parens new Assign ref, fst
-        snd.base = ref
-      return new If new Existence(fst), snd, soak: on
-    null
+    return @unfoldedSoak if @unfoldedSoak?
+    result = do =>
+      if ifn = @base.unfoldSoak o
+        Array::push.apply ifn.body.properties, @properties
+        return ifn
+      for prop, i in @properties when prop.soak
+        prop.soak = off
+        fst = new Value @base, @properties.slice 0, i
+        snd = new Value @base, @properties.slice i
+        if fst.isComplex()
+          ref = new Literal o.scope.freeVariable 'ref'
+          fst = new Parens new Assign ref, fst
+          snd.base = ref
+        return new If new Existence(fst), snd, soak: on
+      null
+    @unfoldedSoak = result or no

 #### Comment


Issue #901 ... allow constructor functions to maintain their position in the class body.

--- nodes.coffee
+++ nodes.coffee
@@ -775,10 +775,9 @@
           if func.bound
             throw new Error 'cannot define a constructor as a bound function'
           if func instanceof Code
-            @ctor = func
+            assign = @ctor = func
           else
-            @ctor = new Assign(new Value(new Literal name), func)
-          assign = null
+            assign = @ctor = new Assign(new Value(new Literal name), func)
         else
           unless assign.variable.this
             assign.variable = new Value(new Literal(name), [new Access(base, 'proto')])
@@ -794,8 +793,8 @@
       if child instanceof Expressions
         for node, i in exps = child.expressions
           if node instanceof Value and node.isObject(true)
-            exps[i] = compact @addProperties node, name
-        child.expressions = exps = compact flatten exps
+            exps[i] = @addProperties node, name
+        child.expressions = exps = flatten exps

   # Make sure that a constructor is defined for the class, and properly
   # configured.
@@ -803,6 +802,7 @@
     if not @ctor
       @ctor = new Code
       @ctor.body.push new Call 'super', [new Splat new Literal 'arguments'] if @parent
+      @body.expressions.unshift @ctor
     @ctor.ctor     = @ctor.name = name
     @ctor.klass    = null
     @ctor.noReturn = yes
@@ -817,9 +817,8 @@

     @setContext name
     @walkBody name
+    @body.expressions.unshift new Extends lname, @parent if @parent
     @ensureConstructor name
-    @body.expressions.unshift new Extends lname, @parent if @parent
-    @body.expressions.unshift @ctor
     @body.expressions.push lname
     @addBoundFunctions o


Simplify Value::unfoldSoak momoization code (only a bit...)

--- nodes.coffee
+++ nodes.coffee
@@ -457,10 +457,9 @@

   # Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
   unfoldSoak: (o) ->
-    return @unfoldedSoak if @unfoldedSoak?
-    result = do =>
+    @unfoldedSoak ?= do =>
       if ifn = @base.unfoldSoak o
-        Array::push.apply ifn.body.properties, @properties
+        ifn.body.properties.push @properties...
         return ifn
       for prop, i in @properties when prop.soak
         prop.soak = off
@@ -471,8 +470,7 @@
           fst = new Parens new Assign ref, fst
           snd.base = ref
         return new If new Existence(fst), snd, soak: on
-      null
-    @unfoldedSoak = result or no
+      no

 #### Comment