Added a bunch of Swedish translations

Added a bunch of Swedish translations

--- app/locale/sv.coffee
+++ app/locale/sv.coffee
@@ -61,7 +61,7 @@
     available: "Tillgänglig"
 #    skills_granted: "Skills Granted" # Property documentation details
     heroes: "Hjältar" # Tooltip on hero shop button from /play
-#    achievements: "Achievements" # Tooltip on achievement list button from /play
+    achievements: "Prestationer" # Tooltip on achievement list button from /play
     account: "Konto" # Tooltip on account button from /play
     settings: "Inställningar" # Tooltip on settings button from /play
 #    poll: "Poll" # Tooltip on poll button from /play
@@ -259,9 +259,9 @@
     victory_review: "Berätta mer!" # Only in old-style levels.
     victory_hour_of_code_done: "Är du klar?"
     victory_hour_of_code_done_yes: "Ja, jag är klar med min Hour of Code!"
-#    victory_experience_gained: "XP Gained"
-#    victory_gems_gained: "Gems Gained"
-#    victory_new_item: "New Item"
+    victory_experience_gained: "XP mottaget"
+    victory_gems_gained: "Vunna ädelstenar"
+    victory_new_item: "Nytt föremål"
     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"
     guide_title: "Guide"
@@ -272,104 +272,104 @@
     tome_cast_button_running: "Kör..."
     tome_cast_button_ran: "Körde"
     tome_submit_button: "Lämna in"
-#    tome_reload_method: "Reload original code for this method" # Title text for individual method reload button.
-#    tome_select_method: "Select a Method"
-#    tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos).
+    tome_reload_method: "Ladda om den ursprungliga koden för den här metoden" # Title text for individual method reload button.
+    tome_select_method: "Välj en metod"
+    tome_see_all_methods: "Se alla metoder som du kan editera" # Title text for method list selector (shown when there are multiple programmable methdos).
     tome_select_a_thang: "Välj någon för "
     tome_available_spells: "Tillgängliga förmågor"
-#    tome_your_skills: "Your Skills"
-#    tome_help: "Help"
-#    tome_current_method: "Current Method"
+    tome_your_skills: "Dina färdigheter"
+    tome_help: "Hjälp"
+    tome_current_method: "Nuvarande metod"
     hud_continue_short: "Fortsätt"
     code_saved: "Kod sparad"
     skip_tutorial: "Hoppa över (esc)"
-#    keyboard_shortcuts: "Key Shortcuts"
+    keyboard_shortcuts: "Kortkommandon"
     loading_ready: "Redo!"
     loading_start: "Starta Nivå"
     problem_alert_title: "Fixa din kod"
     problem_alert_help: "Hjälp"
     time_current: "Nu:"
-#    time_total: "Max:"
-#    time_goto: "Go to:"
+    time_total: "Max:"
+    time_goto: "Gå till:"
     infinite_loop_try_again: "Försök igen"
     infinite_loop_reset_level: "Återställ Nivå"
 #    infinite_loop_comment_out: "Comment Out My Code"
-#    tip_toggle_play: "Toggle play/paused with Ctrl+P."
-#    tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward."
+    tip_toggle_play: "Spela/pausa med Ctrl+P."
+    tip_scrub_shortcut: "Ctrl+] och Ctrl+[ spolar framåt och bakåt."
 #    tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info."
-#    tip_open_source: "CodeCombat is 100% open source!"
-#    tip_beta_launch: "CodeCombat launched its beta in October, 2013."
-#    tip_think_solution: "Think of the solution, not the problem."
+    tip_open_source: "CodeCombat är 100% öppen källkod!"
+    tip_beta_launch: "CodeCombat startade sin beta i oktober 2013."
+    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 fel-fria program; endast det tredje fungerar. - Alan Perlis"
+    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_forums: "Head over to the forums and tell us what you think!"
-#    tip_baby_coders: "In the future, even babies will be Archmages."
+    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_harry: "Yer a Wizard, "
+    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_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_no_try: "Do. Or do not. There is no try. - Yoda"
-#    tip_patience: "Patience you must have, young Padawan. - Yoda"
-#    tip_documented_bug: "A documented bug is not a bug; it is a feature."
-#    tip_impossible: "It always seems impossible until it's done. - Nelson Mandela"
-#    tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds"
+    tip_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_hardware_problem: "Q: How many programmers does it take to change a light bulb?  A: None, it's a hardware problem."
+    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_no_code: "No code is faster than no code."
-#    tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries"
+    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_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr."
-#    tip_google: "Have a problem you can't solve? Google it!"
-#    tip_adding_evil: "Adding a pinch of evil."
-#    tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven"
-#    tip_open_source_contribute: "You can help CodeCombat improve!"
-#    tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch"
+    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"

   game_menu:
     inventory_tab: "Utrustning"
     save_load_tab: "Spara/Ladda"
     options_tab: "Inställningar"
     guide_tab: "Guide"
-#    guide_video_tutorial: "Video Tutorial"
-#    guide_tips: "Tips"
-    multiplayer_tab: "Flerspelareläge"
+    guide_video_tutorial: "Videogenomgång"
+    guide_tips: "Tips"
+    multiplayer_tab: "Flerspelarläge"
     auth_tab: "Registrera dig"
     inventory_caption: "Utrusta din hjälte"
-#    choose_hero_caption: "Choose hero, language"
-#    save_load_caption: "... and view history"
+    choose_hero_caption: "Välj hjälte, språk"
+    save_load_caption: "... och visa historik"
 #    options_caption: "Configure settings"
 #    guide_caption: "Docs and tips"
-#    multiplayer_caption: "Play with friends!"
-#    auth_caption: "Save your progress."
-
-#  leaderboard:
-#    leaderboard: "Leaderboard"
-#    view_other_solutions: "View Leaderboards"
-#    scores: "Scores"
-#    top_players: "Top Players by"
-#    day: "Today"
-#    week: "This Week"
-#    all: "All-Time"
-#    time: "Time"
-#    damage_taken: "Damage Taken"
-#    damage_dealt: "Damage Dealt"
-#    difficulty: "Difficulty"
-#    gold_collected: "Gold Collected"
+    multiplayer_caption: "Spela med vänner!"
+    auth_caption: "Spara dina framsteg."
+
+  leaderboard:
+    leaderboard: "Topplista"
+    view_other_solutions: "Visa topplistor"
+    scores: "Poäng"
+    top_players: "Toppspelare efter"
+    day: "Idag"
+    week: "Den här veckan"
+    all: "Alla"
+    time: "Tid"
+    damage_taken: "Skada mottagen"
+    damage_dealt: "Skada utdelad"
+    difficulty: "Svårighetsgrad"
+    gold_collected: "Samlat guld"

   inventory:
     choose_inventory: "Använd föremål"
@@ -503,23 +503,23 @@
     level_to_unlock: "Nivå som låser upp:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
     restricted_to_certain_heroes: "Det är bara vissa hjältar som kan spela den här nivån."

-#  skill_docs:
-#    writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
-#    read_only: "read-only"
-#    action_name: "name"
-#    action_cooldown: "Takes"
-#    action_specific_cooldown: "Cooldown"
-#    action_damage: "Damage"
-#    action_range: "Range"
-#    action_radius: "Radius"
-#    action_duration: "Duration"
-#    example: "Example"
-#    ex: "ex" # Abbreviation of "example"
-#    current_value: "Current Value"
-#    default_value: "Default value"
-#    parameters: "Parameters"
-#    returns: "Returns"
-#    granted_by: "Granted by"
+  skill_docs:
+    writable: "skrivbar" # Hover over "attack" in Your Skills while playing a level to see most of this
+    read_only: "endast läsning"
+    action_name: "namn"
+    action_cooldown: "Tar"
+    action_specific_cooldown: "Återhämtningstid"
+    action_damage: "Skada"
+    action_range: "Räckvidd"
+    action_radius: "Radie"
+    action_duration: "Löptid"
+    example: "Exampel"
+    ex: "ex" # Abbreviation of "example"
+    current_value: "Nuvarande värde"
+    default_value: "Standardvärde"
+    parameters: "Parametrar"
+    returns: "Returnerar"
+    granted_by: "Ges av"

 #  save_load:
 #    granularity_saved_games: "Saved"
@@ -532,7 +532,7 @@
     music_description: "Stäng av/sätt på bakgrundsmusik."
     editor_config: "Ställ in redigerare"
     editor_config_title: "Redigerarinställningar"
-#    editor_config_level_language_label: "Language for This Level"
+    editor_config_level_language_label: "Språk på den här nivån"
 #    editor_config_level_language_description: "Define the programming language for this particular level."
 #    editor_config_default_language_label: "Default Programming Language"
 #    editor_config_default_language_description: "Define the programming language you want to code in when starting new levels."
@@ -631,8 +631,8 @@
     forum_prefix: "För någonting offentligt, var vänlig testa "
     forum_page: "vårt forum"
     forum_suffix: " istället."
-#    faq_prefix: "There's also a"
-#    faq: "FAQ"
+    faq_prefix: "Det finns också en"
+    faq: "FAQ"
 #    subscribe_prefix: "If you need help figuring out a level, please"
 #    subscribe: "buy a CodeCombat subscription"
 #    subscribe_suffix: "and we'll be happy to help you with your code."
@@ -679,24 +679,24 @@
     error_saving: "Ett fel uppstod när ändringarna skulle sparas"
     saved: "Ändringar sparade"
     password_mismatch: "De angivna lösenorden stämmer inte överens."
-#    password_repeat: "Please repeat your password."
+    password_repeat: "Var god skriv ditt lösenord en gång till."
 #    job_profile: "Job Profile" # Rest of this section (the job profile stuff and wizard stuff) is deprecated
 #    job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks."
 #    job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job."
 #    sample_profile: "See a sample profile"
 #    view_profile: "View Your Profile"

-#  keyboard_shortcuts:
-#    keyboard_shortcuts: "Keyboard Shortcuts"
-#    space: "Space"
-#    enter: "Enter"
-#    escape: "Escape"
-#    shift: "Shift"
-#    run_code: "Run current code."
-#    run_real_time: "Run in real time."
-#    continue_script: "Continue past current script."
-#    skip_scripts: "Skip past all skippable scripts."
-#    toggle_playback: "Toggle play/pause."
+  keyboard_shortcuts:
+    keyboard_shortcuts: "Kortkommandon"
+    space: "Mellanslag"
+    enter: "Enter"
+    escape: "Escape"
+    shift: "Shift"
+    run_code: "Kör nuvarande kod."
+    run_real_time: "Kör i realtid."
+    continue_script: "Fortsätt förbi nuvarande skript."
+    skip_scripts: "Hoppa över alla skript som kan hoppas över."
+    toggle_playback: "Spela/Pausa."
 #    scrub_playback: "Scrub back and forward through time."
 #    single_scrub_playback: "Scrub back and forward through time by a single frame."
 #    scrub_execution: "Scrub through current spell execution."
@@ -704,7 +704,7 @@
 #    toggle_grid: "Toggle grid overlay."
 #    toggle_pathfinding: "Toggle pathfinding overlay."
 #    beautify: "Beautify your code by standardizing its formatting."
-#    maximize_editor: "Maximize/minimize code editor."
+    maximize_editor: "Maximera/minimera kodredigeraren."

 #  community:
 #    main_title: "CodeCombat Community"
@@ -725,7 +725,7 @@
 #    contribute_to_the_project: "Contribute to the project"

   classes:
-    archmage_title: "Huvudmagiker"
+    archmage_title: "Ärkemagiker"
     archmage_title_description: "(Kodare)"
 #    archmage_summary: "If you are a developer interested in coding educational games, become an archmage to help us build CodeCombat!"
     artisan_title: "Hantverkare"
@@ -764,7 +764,7 @@
 #    fork_title: "Fork New Version"
 #    fork_creating: "Creating Fork..."
 #    generate_terrain: "Generate Terrain"
-#    more: "More"
+    more: "Mer"
 #    wiki: "Wiki"
 #    live_chat: "Live Chat"
 #    thang_main: "Main"
@@ -831,8 +831,8 @@
     edit_btn_preview: "Förhandsgranska"
     edit_article_title: "Redigera artikel"

-#  polls:
-#    priority: "Priority"
+  polls:
+    priority: "Prioritet"

   contribute:
     page_title: "Bidragande"
@@ -846,7 +846,7 @@
     archmage_attribute_2: "Viss erfarenhet av programmering och personligt initiativ. Vi hjälper dig att bli orienterad, men kan inte lägga mycket tid på att träna dig."
     how_to_join: "Hur man går med"
     join_desc_1: "Alla kan hjälpa till! Kolla bara in vår "
-    join_desc_2: "för att komma igång, och kryssa i rutan nedanför för att markera att du är en modig huvudmagiker och få de senaste nyheterna via email. Vill du chatta om vad som ska göras eller hur du bli mer involverad?"
+    join_desc_2: "för att komma igång, och kryssa i rutan nedanför för att markera att du är en modig ärkemagiker och få de senaste nyheterna via email. Vill du chatta om vad som ska göras eller hur du bli mer involverad?"
     join_desc_3: ", eller hitta oss i vår "
     join_desc_4: "så tar vi det därifrån!"
     join_url_email: "Maila oss"
@@ -881,9 +881,9 @@
     diplomat_launch_url: "lanseringen i oktober"
     diplomat_introduction_suf: "är det att det finns ett stort intresse för CodeCombat i andra länder! Vi bygger en kår av översättare ivriga att förvandla en samling ord till en annan samling ord för att få CodeCombat så tillgänglig i världen som möjligt. Om du gillar att få tjuvkikar på kommande innehåll och att få dessa nivåer till de andra i ditt land så snart som möjligt är det här kanske klassen för dig."
     diplomat_attribute_1: "Flytande engelska och språket du vill översätta till. När man förmedlar komplicerade idéer är det viktigt att ha ett starkt grepp om båda!"
-#    diplomat_i18n_page_prefix: "You can start translating our levels by going to our"
-#    diplomat_i18n_page: "translations page"
-#    diplomat_i18n_page_suffix: ", or our interface and website on GitHub."
+    diplomat_i18n_page_prefix: "Du kan börja översätta nivåer genom att gå till vår"
+    diplomat_i18n_page: "översättningssida"
+    diplomat_i18n_page_suffix: ", eller använda vårt gränssnitt och hemsida på GitHub."
     diplomat_join_pref_github: "Hitta ditt språks locale-fil "
     diplomat_github_url: "på GitHub"
     diplomat_join_suf_github: ", redigera den online, och skicka en ryckförfrågan. Kryssa också i rutan här nedanför för att hålla dig uppdaterad om nya internationaliseringsutvecklingar."
@@ -896,7 +896,7 @@
     ambassador_subscribe_desc: "Få mail om supportuppdateringar och flerspelarutvecklingar"
     changes_auto_save: "Förändringar sparas automatiskt när du ändrar kryssrutor."
     diligent_scribes: "Våra flitiga skriftlärda:"
-    powerful_archmages: "Våra kraftfulla huvudmagiker:"
+    powerful_archmages: "Våra kraftfulla ärkemagiker:"
     creative_artisans: "Våra kreativa hantverkare:"
     brave_adventurers: "Våra modiga äventyrare:"
     translating_diplomats: "Våra översättande diplomater:"
@@ -909,11 +909,11 @@
     simulation_explanation: "Genom att simulera matcher kan du få dina matcher rankade fortare."
     simulate_games: "Simulera matcher!"
     simulate_all: "ÅTERSTÄLL OCH SIMULERA MATCHER"
-#    games_simulated_by: "Games simulated by you:"
-#    games_simulated_for: "Games simulated for you:"
-#    games_simulated: "Games simulated"
-#    games_played: "Games played"
-#    ratio: "Ratio"
+    games_simulated_by: "Spel simulerade av dig:"
+    games_simulated_for: "Spel simulerade åt dig:"
+    games_simulated: "Simulerade spel"
+    games_played: "Spelade spel"
+    ratio: "Förhållande"
     leaderboard: "Resultattavla"
     battle_as: "Kämpa som "
     summary_your: "Dina "
@@ -927,12 +927,12 @@
     rank_failed: "Kunde inte ranka"
     rank_being_ranked: "Matchen blir rankad"
 #    rank_last_submitted: "submitted "
-#    help_simulate: "Help simulate games?"
+    help_simulate: "Hjälp till att simulera spel?"
     code_being_simulated: "Din nya kod håller på att bli simulerad av andra spelare för rankning. Detta kommer att uppdateras allt eftersom nya matcher kommer in."
     no_ranked_matches_pre: "Inga rankade matcher för "
     no_ranked_matches_post: " laget! Spela mot några motståndare och kom sedan tillbaka it för att få din match rankad."
     choose_opponent: "Välj en motståndare"
-#    select_your_language: "Select your language!"
+    select_your_language: "Välj språk!"
     tutorial_play: "Spela tutorial"
     tutorial_recommended: "Rekommenderas om du aldrig har spelat tidigare"
     tutorial_skip: "Hoppa över tutorial"
@@ -955,34 +955,34 @@
 #    tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details"
 #    tournament_blurb_zero_sum: "Unleash your coding creativity in both gold gathering and battle tactics in this alpine mirror match between red sorcerer and blue sorcerer. The tournament began on Friday, March 27 and will run until Monday, April 6 at 5PM PDT. Compete for fun and glory! Check out the details"
 #    tournament_blurb_blog: "on our blog"
-#    rules: "Rules"
-#    winners: "Winners"
-
-#  user:
-#    stats: "Stats"
-#    singleplayer_title: "Singleplayer Levels"
-#    multiplayer_title: "Multiplayer Levels"
-#    achievements_title: "Achievements"
-#    last_played: "Last Played"
-#    status: "Status"
-#    status_completed: "Completed"
-#    status_unfinished: "Unfinished"
-#    no_singleplayer: "No Singleplayer games played yet."
-#    no_multiplayer: "No Multiplayer games played yet."
-#    no_achievements: "No Achievements earned yet."
-#    favorite_prefix: "Favorite language is "
+    rules: "Regler"
+    winners: "Vinnare"
+
+  user:
+    stats: "Stats"
+    singleplayer_title: "Enspelarnivåer"
+    multiplayer_title: "Flerspelarnivåer"
+    achievements_title: "Prestationer"
+    last_played: "Senast spelad"
+    status: "Status"
+    status_completed: "Avklarad"
+    status_unfinished: "Ej avklarad"
+    no_singleplayer: "Inga spel i enkelspelarläge än."
+    no_multiplayer: "Inga spel i flerspelarläge än."
+    no_achievements: "Inga prestationer än."
+    favorite_prefix: "Favoritspråk: "
 #    favorite_postfix: "."

-#  achievements:
-#    last_earned: "Last Earned"
-#    amount_achieved: "Amount"
-#    achievement: "Achievement"
+  achievements:
+    last_earned: "Senast förvärvad den"
+    amount_achieved: "Antal"
+    achievement: "Prestation"
 #    category_contributor: "Contributor"
 #    category_ladder: "Ladder"
-#    category_level: "Level"
-#    category_miscellaneous: "Miscellaneous"
-#    category_levels: "Levels"
-#    category_undefined: "Uncategorized"
+    category_level: "Nivå"
+    category_miscellaneous: "Övrigt"
+    category_levels: "Nivåer"
+    category_undefined: "Okategoriserad"
 #    current_xp_prefix: ""
 #    current_xp_postfix: " in total"
 #    new_xp_prefix: ""
@@ -991,26 +991,26 @@
 #    left_xp_infix: " until level "
 #    left_xp_postfix: ""

-#  account:
-#    recently_played: "Recently Played"
-#    no_recent_games: "No games played during the past two weeks."
-#    payments: "Payments"
+  account:
+    recently_played: "Spelade nyligen"
+    no_recent_games: "Inga spel spelade de senaste två veckorna."
+    payments: "Betalningar"
 #    purchased: "Purchased"
-#    subscription: "Subscription"
-#    invoices: "Invoices"
+    subscription: "Prenumeration"
+    invoices: "Fakturor"
 #    service_apple: "Apple"
-#    service_web: "Web"
-#    paid_on: "Paid On"
+    service_web: "Webb"
+    paid_on: "Betalat den"
 #    service: "Service"
-#    price: "Price"
-#    gems: "Gems"
-#    active: "Active"
+    price: "Pris"
+    gems: "Ädelstenar"
+    active: "Aktiv"
 #    subscribed: "Subscribed"
 #    unsubscribed: "Unsubscribed"
 #    active_until: "Active Until"
-#    cost: "Cost"
-#    next_payment: "Next Payment"
-#    card: "Card"
+    cost: "Kostnad"
+    next_payment: "Nästa betalning"
+    card: "Kort"
 #    status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now."
 #    status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!"

@@ -1115,7 +1115,7 @@
     opensource_description_prefix: "Spana in "
     github_url: "vår GitHub"
     opensource_description_center: " och hjälp till om du vill! CodeCombat är byggt på dussintals projekt med öppen källkod, och vi älskar dem. Se "
-    archmage_wiki_url: "vår Huvudmagiker-wiki"
+    archmage_wiki_url: "vår Ärkemagiker-wiki"
     opensource_description_suffix: "för en lista över mjukvaran som gör detta spel möjligt."
     practices_title: "Respektfulla \"best practices\""
     practices_description: "Dessa är våra löften till dig, spelaren, på lite mindre juristspråk."


refactored contest branch merge spec v8

refactored contest branch merge spec v8

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -1540,7 +1540,7 @@
         test splitter, [11,12,21,22], done

-      it.skip 'v8', (done) ->
+      it 'v8', (done) ->
         splitter = (choice,data) ->
           choice.tree()
           .deliver('-')
@@ -1552,18 +1552,21 @@
             #   branch()
             #   .then
             choice.branch "1", (b,data) ->
-              data
-            .then (choice,data) ->
-              choice.branch "a", (b,data) ->
-                data
+              b.tree()
+              .deliver(data)
               .then (choice,data) ->
-                data += '1a-'
-                data
-              choice.branch "b", (b,data) ->
-                data
-              .then (choice,data) ->
-                data += '1b-'
-                data
+                choice.branch "a", (b,data) ->
+                  b.tree()
+                  .deliver(data)
+                  .then (choice,data) ->
+                    data += '1a-'
+                    data
+                choice.branch "b", (b,data) ->
+                  b.tree()
+                  .deliver(data)
+                  .then (choice,data) ->
+                    data += '1b-'
+                    data
             # branch()
             #   tree()
             #   .then()


increase archive extract timeout

increase archive extract timeout

--- salvus/page/archive.coffee
+++ salvus/page/archive.coffee
@@ -211,6 +211,7 @@
                     command    : command
                     args       : args
                     err_on_exit: false
+                    timeout    : 120
                     cb         : (err, out) =>
                         #console.log("done extract: ", err, out)
                         @element.find("a[href=#extract]").icon_spin(false)


remove old contour files

remove old contour files

--- src/data.coffee
+++ src/data.coffee
@@ -197,10 +197,12 @@
   demDone = Q.defer()
   bbox = REGION[region].bbox

-  exec("gdalwarp
-        data/srtm-1arcsec-ro.tiff
-        data/contours/#{region}.tiff
-        -te #{bbox.join(' ')}")
+  exec("rm -f data/contours/#{region}.*")
+  .then ->
+    exec("gdalwarp
+          data/srtm-1arcsec-ro.tiff
+          data/contours/#{region}.tiff
+          -te #{bbox.join(' ')}")
   .then ->
     exec("gdal_contour
           data/contours/#{region}.tiff


add todo

add todo

--- assets/javascripts/lib/travis/auth.coffee
+++ assets/javascripts/lib/travis/auth.coffee
@@ -33,3 +33,4 @@

   signIn: ->
     @trySignIn()
+    # TODO: timeout for redirect goes here


don't do a project rename if the name is the same

this prevents double renaming which happens when you press
enter as well as no name change

--- services/web/public/coffee/ide/settings/controllers/ProjectNameController.coffee
+++ services/web/public/coffee/ide/settings/controllers/ProjectNameController.coffee
@@ -17,6 +17,8 @@
 			newName = $scope.inputs.name
 			if !newName? or newName.length == 0 or newName.length > MAX_PROJECT_NAME_LENGTH
 				return
+			if $scope.project.name == newName
+				return
 			$scope.project.name = newName
 			settings.saveProjectSettings({name: $scope.project.name})


more specific

more specific

--- angular/test/protractor/group_spec.coffee
+++ angular/test/protractor/group_spec.coffee
@@ -3,7 +3,7 @@
   page = require './helpers/page_helper.coffee'
   staticPage = require './helpers/static_page_helper.coffee'

-  describe 'visiting a parent group as a subgroup member', ->
+  fdescribe 'visiting a parent group as a subgroup member', ->
     it 'displays parent group in sidebar if member of a subgroup', ->
       page.loadPath 'visit_group_as_subgroup_member'
       page.expectText '.group-theme__name', 'Point Break'


Fail gracefully when git cmd not found

Fail gracefully when git cmd not found

--- lib/git.coffee
+++ lib/git.coffee
@@ -26,13 +26,16 @@
       c_stdout @save ?= ''
       @save = null

-  new BufferedProcess
-    command: command
-    args: args
-    options: options
-    stdout: stdout
-    stderr: stderr
-    exit: exit
+  try
+    new BufferedProcess
+      command: command
+      args: args
+      options: options
+      stdout: stdout
+      stderr: stderr
+      exit: exit
+  catch error
+    new StatusView(type: 'alert', message: 'Git Plus is unable to locate git command. Please ensure process.env.PATH can access git.')

 gitStatus = (stdout) ->
   gitCmd


Trigger evento skuSelectorReady

Trigger evento skuSelectorReady

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -390,7 +390,7 @@
 #	if options.selectOnOpening or selector.findSelectedSku()
 #		renderer.selectDimension(selector.dimensions[0])

-
+	$(window).trigger('skuSelectorReady')
 	this.removeClass('sku-selector-loading')

 	# Chaining


adds methods to make a transaction and to check if a transaction is possible

--- models/account.coffee
+++ models/account.coffee
@@ -1,9 +1,49 @@
 Backbone = require 'backbone'
 { KERNAL_API_URL } = require '../config.coffee'
+transactionMap = require '../maps/transactions'
 _ = require 'underscore'

 module.exports = class Account extends Backbone.Model

   url: ->
-    "#{KERNAL_API_URL}/accounts/#{@id}"
+    "#{KERNAL_API_URL}/accounts/#{@idOr_id()}"

+  idOr_id: ->
+    @id or @get('_id')
+
+  holdsPosition: (contract) ->
+    _.any @get('open_positions'), (position) ->
+      position.contract is contract.get('id')
+
+  makeTransaction: ({ transaction, contract, block_id }, options) ->
+    order = new Backbone.Model
+    order.url = "#{@url()}/#{transaction}/#{contract.id}/block_id/1"
+    order.save null, options
+
+  canMakeTransaction: (transaction, contract) ->
+    return { success: false, reason: 'Contract not found.' } unless contract
+
+    switch transaction
+      # check that user has enough account balance
+      when 'buy'
+        amount = contract.get transactionMap[transaction]
+        if amount > @get('balance')
+          return {
+            success: false
+            reason: "You don't have enough cåin to purchase #{contract.id}"
+          }
+        else
+          return {
+            success: true
+          }
+      # check that user holds this contract
+      when 'sell'
+        if @holdsPosition contract
+          return {
+            success: true
+          }
+        else
+          return {
+            success: false
+            reason: "You don't hold any contracts for this future."
+          }


fixed a bug

fixed a bug

--- transitionRegion.coffee
+++ transitionRegion.coffee
@@ -1,13 +1,16 @@
-((root, factory) ->
+((factory) ->
   if typeof define is 'function' and define.amd
     define ['backbone.marionette'], (marionette) ->
-      factory(root, marionette)
+      factory marionette
   else if typeof exports is 'object'
     Marionette = require 'backbone.marionette'
-    module.exports = factory root, Marionette
+    module.exports = factory Marionette
   else
-    factory(root, Backbone.Marionette)
-) this, (Marionette) ->
+    factory Backbone.Marionette
+) (Marionette) ->
+
+  delay = (duration, callback) ->
+    setTimeout callback, duration

   getFadeDuration = (region) ->
     @region.transitionDuration or 100
@@ -18,13 +21,12 @@
       if @skipTransition?(view)
         super view
       else
-        setTimeout =>
+        delay duration, =>
           duration = @transitionDuration or 100
           @$el.contents().detach()
           view.$el.hide()
           @el.appendChild(view.el)
           view.$el.fadeIn fadeDuration
-        , duration

     _destroyView: ->
       if @skipTransition?(@currentView)


Add envelope class

Add envelope class

--- scripts/poo-tracker.coffee
+++ scripts/poo-tracker.coffee
@@ -4,6 +4,9 @@
 info = Url.parse process.env.POO_REDIS_URL or "redis://localhost:6379/0"
 redis_client = Redis.createClient(info.port, info.hostname)
 redis_client.auth info.auth.split(":")[1] if info.auth
+
+class Envelope
+  constructor: (@room, @user) ->

 module.exports = (robot) ->
   POO_TRACKER_KEY = "poops"
@@ -15,7 +18,8 @@
       return if reply == null

       room = process.env.HUBOT_IRC_ROOMS or "#arrakis"
-      e = room: room
+      e = new Envelope room, null
+      e.room
       robot.send e, reply
       redis_client.set POO_LATEST_KEY, reply


Set key on components to silence React warnings

--- src/components/task-plan/reading.cjsx
+++ src/components/task-plan/reading.cjsx
@@ -111,10 +111,13 @@
     {id, courseId} = @props
     plan = TaskPlanStore.get(id)

-    headerText = if TaskPlanStore.isNew(id) then 'Add Reading Assignment' else 'Edit Reading Assignment'
+    headerText = <span key='header-text'>
+      {if TaskPlanStore.isNew(id) then 'Add Reading Assignment' else 'Edit Reading Assignment'}
+    </span>
     topics = TaskPlanStore.getTopics(id)
     formClasses = ['edit-reading', 'dialog']
     closeBtn = <Close
+      key='close-button'
       className='pull-right'
       onClick={@cancel}/>


Digest stuff to the metadata section

Digest stuff to the metadata section

--- src/file.coffee
+++ src/file.coffee
@@ -138,6 +138,15 @@
   getParent: ->
     Directory ?= require './directory'
     new Directory(path.dirname @path)
+
+  # Public: Get the SHA-1 digest of this file
+  #
+  # Returns a {String}.
+  getDigest: ->
+    @digest ? @setDigest(@readSync())
+
+  setDigest: (contents) ->
+    @digest = crypto.createHash('sha1').update(contents ? '').digest('hex')

   ###
   Section: Reading and Writing
@@ -197,15 +206,6 @@
     @cachedContents = text
     @subscribeToNativeChangeEvents() if not previouslyExisted and @hasSubscriptions()
     undefined
-
-  setDigest: (contents) ->
-    @digest = crypto.createHash('sha1').update(contents ? '').digest('hex')
-
-  # Public: Get the SHA-1 digest of this file
-  #
-  # Returns a {String}.
-  getDigest: ->
-    @digest ? @setDigest(@readSync())

   # Writes the text to specified path.
   #


:racehorse: Only use iconv-lite for non-UTF8 files

iconv-lite adds some require time and seems to be slower than
node's built-in encoders so only use it when the file is set to
be non-UTF8.

--- src/file.coffee
+++ src/file.coffee
@@ -6,9 +6,10 @@
 {Emitter, Disposable} = require 'event-kit'
 fs = require 'fs-plus'
 Grim = require 'grim'
-Q = null # Defer until used
+
+Q = null     # Defer until used
 runas = null # Defer until used
-iconv = require 'iconv-lite'
+iconv = null # Defer until used

 Directory = null
 PathWatcher = require './main'
@@ -173,11 +174,23 @@
     if not @exists()
       @cachedContents = null
     else if not @cachedContents? or flushCache
-      contents = fs.readFileSync(@getPath())
-      @cachedContents = iconv.decode(contents, @getEncoding())
+      encoding = @getEncoding()
+      if encoding is 'utf8'
+        @cachedContents = fs.readFileSync(@getPath(), encoding)
+      else
+        iconv ?= require 'iconv-lite'
+        @cachedContents = iconv.decode(fs.readFileSync(@getPath()), encoding)

     @setDigest(@cachedContents)
     @cachedContents
+
+  writeFileSync: (filePath, contents) ->
+    encoding = @getEncoding()
+    if encoding is 'utf8'
+      fs.writeFileSync(filePath, text, {encoding})
+    else
+      iconv ?= require 'iconv-lite'
+      fs.writeFileSync(filePath, iconv.encode(contents, encoding))

   # Public: Reads the contents of the file.
   #
@@ -195,8 +208,14 @@
       promise = deferred.promise
       content = []
       bytesRead = 0
-      readStream = fs.createReadStream(@getPath())
-      readStream = readStream.pipe(iconv.decodeStream(@getEncoding()))
+
+      encoding = @getEncoding()
+      if encoding is 'utf8'
+        readStream = fs.createReadStream(@getPath(), {encoding})
+      else
+        iconv ?= require 'iconv-lite'
+        readStream = fs.createReadStream(@getPath()).pipe(iconv.decodeStream(encoding))
+
       readStream.on 'data', (chunk) ->
         content.push(chunk)
         bytesRead += chunk.length
@@ -232,7 +251,7 @@
   # permission to the path.
   writeFileWithPrivilegeEscalationSync: (filePath, text) ->
     try
-      fs.writeFileSync(filePath, iconv.encode(text, @getEncoding()))
+      @writeFileSync(filePath, text)
     catch error
       if error.code is 'EACCES' and process.platform is 'darwin'
         runas ?= require 'runas'


Fix exists hardcoded /tmp and remove uneeded ()

--- src/file.coffee
+++ src/file.coffee
@@ -130,9 +130,8 @@

   # Public: Returns a {Boolean}, true if the file exists, false otherwise.
   exists: ->
-    deferred = Q.defer()
-    fs.exists('/tmp', deferred.resolve);
-    deferred.promise
+    Q.Promise (resolve, reject) =>
+      fs.exists @getPath(), resolve

   # Public: Returns a {Boolean}, true if the file exists, false otherwise.
   existsSync: ->
@@ -352,7 +351,7 @@

   detectResurrection: ->
     @exists().then (exists) =>
-      if (exists)
+      if exists
         @subscribeToNativeChangeEvents()
         @handleNativeChangeEvent('resurrect', @getPath())
       else


Use shift() not pop(). Don't wanna reverse the paths

--- lib/single-process-search.coffee
+++ lib/single-process-search.coffee
@@ -11,7 +11,7 @@
   finishedScanning = false
   pathCount = 0
   pathsSearching = 0
-  pathsToSearch = []
+  pathQueue = []

   searchPath = (filePath) ->
     pathsSearching++
@@ -21,15 +21,15 @@
       checkIfFinished()

   searchNextPath = ->
-    if pathsSearching < MAX_CONCURRENT_SEARCH and pathsToSearch.length
-      searchPath(pathsToSearch.pop())
+    if pathsSearching < MAX_CONCURRENT_SEARCH and pathQueue.length
+      searchPath(pathQueue.shift())

   maybeSearchPath = (filePath) =>
     pathCount++
     if pathsSearching < MAX_CONCURRENT_SEARCH
       searchPath(filePath)
     else
-      pathsToSearch.push(filePath)
+      pathQueue.push(filePath)

   onFinishedScanning = ->
     finishedScanning = true


Improve string scoring

Improve string scoring

--- app/assets/javascripts/exo/matcher.coffee
+++ app/assets/javascripts/exo/matcher.coffee
@@ -1,7 +1,7 @@
 namespace 'Exo', (exports) ->
   class exports.Matcher
     constructor: (options = {}) ->
-      options = _.defaults(options, threshold: 0.1)
+      options = _.defaults(options, threshold: 0.2)
       @results = new Exo.ArrayController()
       @results.comparator = null
       @sources = []
@@ -48,93 +48,118 @@
     # Private
     #

-    _scoreForQuery: (string, abbreviation, fuzziness = null) ->
-      # If the string is equal to the abbreviation, perfect match.
-      return 1 if string == abbreviation
+    _scoreForQuery: (string, abbreviation) ->
+      #
+      # Stolen from https://github.com/joshaven/string_score/blob/master/coffee/string_score.coffee
+      #

-      #if it's not a perfect match and is empty return 0
-      return 0 if abbreviation is ""
+      # **Size optimization notes**:
+      # Declaring `string` before checking for an exact match
+      # does not affect the speed and reduces size because `this`
+      # occurs only once in the code as a result.
+      #string = this
+
+      # Perfect match if the string equals the abbreviation.
+      return 1.0 if string == abbreviation

+      # Initializing variables.
+      string_length = string.length
       total_character_score = 0
+
+      # Awarded only if the string and the abbreviation have a common prefix.
+      should_award_common_prefix_bonus = 0.5
+
+      #### Sum character scores
+
+      # Add up scores for each character in the abbreviation.
+      for c, i in abbreviation
+          # Find the index of current character (case-insensitive) in remaining part of string.
+          index_c_lowercase = string.indexOf c.toLowerCase()
+          index_c_uppercase = string.indexOf c.toUpperCase()
+          min_index = Math.min index_c_lowercase, index_c_uppercase
+          index_in_string = if min_index > -1 then min_index else Math.max index_c_lowercase, index_c_uppercase
+
+          #### Identical strings
+          # Bail out if current character is not found (case-insensitive) in remaining part of string.
+          #
+          # **Possible size optimization**:
+          # Replace `index_in_string == -1` with `index_in_string < 0`
+          # which has fewer characters and should have identical performance.
+          return 0 if index_in_string == -1
+
+          # Set base score for current character.
+          character_score = 0.1
+
+          #### Case-match bonus
+          # If the current abbreviation character has the same case
+          # as that of the character in the string, we add a bonus.
+          #
+          # **Optimization notes**:
+          # `charAt` was replaced with an index lookup here because
+          # the latter results in smaller and faster code without
+          # breaking any tests.
+          if string[index_in_string] == c
+              character_score += 0.2
+
+          #### Consecutive character match and common prefix bonuses
+          # Increase the score when each consecutive character of
+          # the abbreviation matches the first character of the
+          # remaining string.
+          #
+          # **Size optimization disabled (truthiness shortened)**:
+          # It produces smaller code but is slower.
+          #
+          #     if !index_in_string
+          if index_in_string == 0
+              character_score += 0.8
+              # String and abbreviation have common prefix, so award bonus.
+              #
+              # **Size optimization disabled (truthiness shortened)**:
+              # It produces smaller code but is slower.
+              #
+              #     if !i
+              if i == 0
+                  should_award_common_prefix_bonus = 1 #yes
+
+          #### Acronym bonus
+          # Typing the first character of an acronym is as
+          # though you preceded it with two perfect character
+          # matches.
+          #
+          # **Size optimization disabled**:
+          # `string.charAt(index)` wasn't replaced with `string[index]`
+          # in this case even though the latter results in smaller
+          # code (when minified) because the former is faster, and
+          # the gain out of replacing it is negligible.
+          if string.charAt(index_in_string - 1) == ' '
+              character_score += 0.8 # * Math.min(index_in_string, 5) # Cap bonus at 0.4 * 5
+
+          # Left trim the matched part of the string
+          # (forces sequential matching).
+          string = string.substring(index_in_string + 1, string_length)
+
+          # Add to total character score.
+          total_character_score += character_score
+
+      # **Feature disabled**:
+      # Uncomment the following to weigh smaller words higher.
+      #
+      #     return total_character_score / string_length
+
       abbreviation_length = abbreviation.length
-      string_length = string.length
-      start_of_string_bonus = undefined
-      abbreviation_score = undefined
-      fuzzies = 1
-      final_score = undefined
-
-      # Walk through abbreviation and add up scores.
-      i = 0
-      character_score = undefined
-      index_in_string = undefined
-      c = undefined
-      index_c_lowercase = undefined
-      index_c_uppercase = undefined
-      min_index = undefined
-
-      # = 0
-      # = 0
-      # = ''
-      # = 0
-      # = 0
-      # = 0
-      while i < abbreviation_length
-
-        # Find the first case-insensitive match of a character.
-        c = abbreviation.charAt(i)
-        index_c_lowercase = string.indexOf(c.toLowerCase())
-        index_c_uppercase = string.indexOf(c.toUpperCase())
-        min_index = Math.min(index_c_lowercase, index_c_uppercase)
-        index_in_string = (if (min_index > -1) then min_index else Math.max(index_c_lowercase, index_c_uppercase))
-        if index_in_string is -1
-          if fuzziness
-            fuzzies += 1 - fuzziness
-            continue
-          else
-            return 0
-        else
-          character_score = 0.1
-
-        # Set base score for matching 'c'.
-
-        # Same case bonus.
-        character_score += 0.1  if string[index_in_string] is c
-
-        # Consecutive letter & start-of-string Bonus
-        if index_in_string is 0
-
-          # Increase the score when matching first character of the remainder of the string
-          character_score += 0.6
-
-          # If match is the first character of the string
-          # & the first character of abbreviation, add a
-          # start-of-string match bonus.
-          start_of_string_bonus = 1  if i is 0 #true;
-        else
-
-          # Acronym Bonus
-          # Weighing Logic: Typing the first character of an acronym is as if you
-          # preceded it with two perfect character matches.
-          # * Math.min(index_in_string, 5); // Cap bonus at 0.4 * 5
-          character_score += 0.8  if string.charAt(index_in_string - 1) is " "
-
-        # Left trim the already matched part of the string
-        # (forces sequential matching).
-        string = string.substring(index_in_string + 1, string_length)
-        total_character_score += character_score
-        ++i
-      # end of for loop
-
-      # Uncomment to weigh smaller words higher.
-      # return total_character_score / string_length;
       abbreviation_score = total_character_score / abbreviation_length
-
-      #percentage_of_matched_string = abbreviation_length / string_length;
-      #word_score = abbreviation_score * percentage_of_matched_string;
-
-      # Reduce penalty for longer strings.
-      #final_score = (word_score + abbreviation_score) / 2;
+
+      #### Reduce penalty for longer strings
+
+      # **Optimization notes (code inlined)**:
+      #
+      #     percentage_of_matched_string = abbreviation_length / string_length
+      #     word_score = abbreviation_score * percentage_of_matched_string
+      #     final_score = (word_score + abbreviation_score) / 2
       final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2
-      final_score = final_score / fuzzies
-      final_score += 0.15  if start_of_string_bonus and (final_score + 0.15 < 1)
-      final_score
+
+      #### Award common prefix bonus
+      if should_award_common_prefix_bonus and (final_score + 0.1 < 1)
+          final_score += 0.1
+
+      return final_score


svg fix

svg fix

--- app/assets/javascripts/scaffold.js.coffee
+++ app/assets/javascripts/scaffold.js.coffee
@@ -1,7 +1,7 @@
 ColorCampSubscriber = ->
   this.dispatcher = null
   this.subscribed_channel = null
-  this.svg = false
+  this.channel_svg = false
   this.reconnectIntv = null

   if window.location.hostname == 'color.camp'
@@ -39,7 +39,7 @@
         return unless data.id

         unless $('.color[data-uuid="' + data.id + '"]').size() > 0
-          if channel_svg
+          if _t.channel_svg
             el = $('<stop/>').attr('stop-color', '#' + data.color.hex)

           else


fix so that you could see main bg over navbar when you clicked about us link

--- app/assets/javascripts/static_page.coffee
+++ app/assets/javascripts/static_page.coffee
@@ -21,7 +21,7 @@
     $target = $($(this).attr('href'))

     $('html, body').stop().animate {
-      'scrollTop': $target.offset().top - $('#navigation').height()
+      'scrollTop': $target.offset().top - $('#navigation').height() + 1
     }, 500, 'swing'

 $(window).on 'load', ->


Insert tag when click tag link at comment modal.

--- app/assets/javascripts/undersky.js.coffee
+++ app/assets/javascripts/undersky.js.coffee
@@ -285,6 +285,15 @@
         textarea = self.parents('.modal.create-comment').find('textarea')
         textarea.focus()
         textarea.val(username + textarea.val().replace(username, ''))
+
+
+      @tag: (e) ->
+        e && e.preventDefault()
+        self = $(this)
+        tag = ' ' + $(this).text();
+        textarea = self.parents('.modal.create-comment').find('textarea')
+        textarea.focus()
+        textarea.val(textarea.val().replace(tag, '') + tag)

       @validate: (e) ->
         self = $(this)
@@ -342,6 +351,7 @@
       do ->
         $d.delegate '.comments-button.create-comment a', 'click', self.show
         $d.delegate '.modal.create-comment .username a', 'click', self.reply
+        $d.delegate '.modal.create-comment a.tag', 'click', self.tag
         $d.delegate '.modal.create-comment [name="text"]', 'keyup change', self.validate
         $d.delegate '.modal.create-comment [name="cancel"]', 'click', self.hide
         $('.modal.create-comment form').bindAjaxHandler self.handler


Update schema.coffee

Update schema.coffee

--- modules/schema.coffee
+++ modules/schema.coffee
@@ -35,8 +35,8 @@
 						title		: String
 						updated 	: Date

-				MapgitSchema = new Schema
-					id 			: ObjectId
+				MapSchema = new Schema
+					id 		: ObjectId
 					permaLink	: String
 					content 	: String


force an npm rebuild to work around issue #129, missing bcrypt bindings file

see https://github.com/npm/npm/issues/5400

--- server-ce/Gruntfile.coffee
+++ server-ce/Gruntfile.coffee
@@ -153,18 +153,22 @@
 				return callback(error) if error?
 				Helpers.installNpmModules service, (error) ->
 					return callback(error) if error?
-					Helpers.runGruntInstall service, (error) ->
+					Helpers.rebuildNpmModules service, (error) ->
 						return callback(error) if error?
-						callback()
+						Helpers.runGruntInstall service, (error) ->
+							return callback(error) if error?
+							callback()

 		updateService: (service, callback = (error) ->) ->
 			Helpers.updateGitRepo service, (error) ->
 				return callback(error) if error?
 				Helpers.installNpmModules service, (error) ->
 					return callback(error) if error?
-					Helpers.runGruntInstall service, (error) ->
+					Helpers.rebuildNpmModules service, (error) ->
 						return callback(error) if error?
-						callback()
+						Helpers.runGruntInstall service, (error) ->
+							return callback(error) if error?
+							callback()

 		cloneGitRepo: (service, callback = (error) ->) ->
 			repo_src = service.repo
@@ -207,13 +211,21 @@
 							proc = spawn "git", ["push", "--tags"], cwd: dir, stdio: "inherit"
 							proc.on "close", () ->
 								callback()
-
+
 		installNpmModules: (service, callback = (error) ->) ->
 			dir = service.name
 			proc = spawn "npm", ["install"], stdio: "inherit", cwd: dir
 			proc.on "close", () ->
 				callback()
-
+
+		# work around for https://github.com/npm/npm/issues/5400
+		# where binary modules are not built due to bug in npm
+		rebuildNpmModules: (service, callback = (error) ->) ->
+			dir = service.name
+			proc = spawn "npm", ["rebuild"], stdio: "inherit", cwd: dir
+			proc.on "close", () ->
+				callback()
+
 		createDataDirs: (callback = (error) ->) ->
 			DIRS = [
 				"tmp/dumpFolder"


Fix html entities in draft solutions

Fix html entities in draft solutions

--- server/api/setupApi.coffee
+++ server/api/setupApi.coffee
@@ -1,6 +1,7 @@
 connectEnsureLogin = require('connect-ensure-login')
 passport = require('passport')
 iconv = require('iconv-lite')
+Entities = require('html-entities').XmlEntities

 import User from '../models/user'
 import Submit from '../models/submit'
@@ -27,6 +28,7 @@
 import InformaticsUser from '../informatics/InformaticsUser'

 ensureLoggedIn = connectEnsureLogin.ensureLoggedIn("/api/forbidden")
+entities = new Entities()

 wrap = (fn) ->
     (args...) ->
@@ -47,6 +49,7 @@

 createDraftSubmit = (problemId, userId, language, code) ->
     code = iconv.decode(new Buffer(code), "latin1")
+    code = entities.encode(code)
     time = new Date
     timeStr = +time
     submit = new Submit


solved concatenation problem and changed operator enums to negative values

--- server/src/DiceFace.coffee
+++ server/src/DiceFace.coffee
@@ -1,4 +1,6 @@
 class DICEFACES
+  @plus        : -2
+  @minus       : -1
   @zero        : 0
   @one         : 1
   @two         : 2
@@ -9,8 +11,7 @@
   @seven       : 7
   @eight       : 8
   @nine        : 9
-  @plus        : 10
-  @minus       : 11
+
 module.exports.DICEFACES = DICEFACES;

 numOps = 2
@@ -37,17 +38,17 @@
     @allocate()

   allocate: ->
+    @state.unallocated = []
     ops = 0
     for x in [1..24]  #24 dice rolls
       rand = Math.random()  #get a random number
       if rand < 2/3  #first we decide if the roll yields an operator or a digit
         rand = Math.floor(Math.random() * 10)  #2/3 of the time we get a digit, decided by a new random number
       else  #1/3 of the time we get an operator, again we generate a new random number to decide which operator to use
-        rand = Math.floor(Math.random() * numOps) + 10
+        rand = Math.floor(Math.random() * - numOps)
         ops++ #we keep track of the number of operators generated so that later we can check if there are enough
       @state.unallocated.push(rand)  #here we add the die to the unallocated resources array
     if (ops < 2) || (ops > 21)  #if there are too few or too many operators, we must roll again
-      @state.unallocated = []  #clear the unallocated resources array
       @allocate()  #do the allocation again

   setGoal: (dice) ->
@@ -55,18 +56,27 @@

   scan: (dice) ->
     scanned = []
-    for index in [0..dice.length]
-      if 0 <= dice[index] <= 9
-        if (index < dice.length - 1) && (0 <= dice[index + 1] <= 9)
-          if (index < dice.length - 2) && (0 <= dice[index + 2] <= 9)
+    index = 0
+    while index < dice.length
+      if 0 <= dice[index] <= 9  #if current element is a number
+        if (index < dice.length - 1) && (0 <= dice[index + 1] <= 9) #if next element is a number
+          if (index < dice.length - 2) && (0 <= dice[index + 2] <= 9) #if third element is a number
             throw "Numbers limited to 2 digits in a row"
-          scanned.push(dice[index] * 10 + dice[index + 1])
-          index++
+          else
+            scanned.push(dice[index] * 10 + dice[index + 1]) #concatenate
+            console.log "index before = #{index}"
+            index++
+            console.log "index after = #{index}"
         else
           scanned.push(dice[index])
+      else
+        scanned.push(dice[index])
+      index++
     scanned

   addClient: (clientid) ->
+    @allocate()
+    console.log @state.unallocated
     if @players.length == @playerLimit
       throw new Error("Game full")
     else
@@ -119,4 +129,6 @@
   constructor: (id) ->
     @id = id

-module.exports.Player = Player;+module.exports.Player = Player;
+
+


Updated timer values

Updated timer values

--- server/src/Settings.coffee
+++ server/src/Settings.coffee
@@ -4,10 +4,10 @@
   @DEBUG = off
   # The global dice used in debug mode
   @DEBUGDICE = [1, DICEFACESYMBOLS.plus, 2, DICEFACESYMBOLS.minus, 3, 4, 5, 6, 7, 8, 9, 0, DICEFACESYMBOLS.divide, 9, 9, 1, 2, 9, 4, 5, DICEFACESYMBOLS.minus, DICEFACESYMBOLS.plus, 2, 4]
-  @turnSeconds = 30
+  @turnSeconds = 99
   @challengeDecisionTurnSeconds = 40
-  @submitTurnSeconds = 40
-  @goalSeconds = 30
+  @submitTurnSeconds = 99
+  @goalSeconds = 99


Fixed formatting

Fixed formatting

--- grammars/papyrus-fallout4.cson
+++ grammars/papyrus-fallout4.cson
@@ -230,8 +230,8 @@
 				match: "\\b([1-9]+[0-9]*|0)"
 			}
             {
-                'match': '(?i)\\b(0x[0-9a-f]+)\\b'
-                'name': 'constant.numeric.integer.hexadecimal.papyrus.fallout4'
+                match: '(?i)\\b(0x[0-9a-f]+)\\b'
+                name: 'constant.numeric.integer.hexadecimal.papyrus.fallout4'
             }
 			{
 				name: "constant.language.bool.papyrus.fallout4"


add summaries

add summaries

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -13,6 +13,7 @@

 counters = {}
 gauges = {}
+summaries = {}

 destructors = []

@@ -44,7 +45,7 @@
 	inc : (key, sampleRate = 1)->
 		statsd.increment buildKey(key), sampleRate
 		key = this.sanitizeKey(key)
-		if !counters[key]
+		if !counters[key]?
 			counters[key] = new prom.Counter({
 		  		name: "#{name}_#{key}",
 				help: key,
@@ -66,18 +67,26 @@
 		done:->
 			timeSpan = new Date - this.start
 			statsd.timing(buildKey(this.key), timeSpan, this.sampleRate)
+			if !summaries[key]?
+				summary = new client.Summary({
+					name: "#{name}_#{key}",
+					help: key,
+					maxAgeSeconds: 600,
+					ageBuckets: 10
+				})
+			summaries[key].observe(timeSpan)
 			return timeSpan

 	gauge : (key, value, sampleRate = 1)->
 		statsd.gauge buildKey(key), value, sampleRate
 		key = this.sanitizeKey(key)
-		if !gauges[key]
+		if !gauges[key]?
 			gauges[key] = new prom.Gauge({
-		  		name: "#{name}_#{key}",
+				name: "#{name}_#{key}",
 				help: key,
 				labelNames: ['name','host']
 			})
-		gauges[key].set({name: name, host: hostname},this.sanitizeValue(value))
+		gauges[key].set({name: name, host: hostname}, this.sanitizeValue(value))

 	globalGauge: (key, value, sampleRate = 1)->
 		statsd.gauge buildGlobalKey(key), value, sampleRate


add support for statsd count method

add support for statsd count method

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -20,6 +20,9 @@

 	inc : (key, sampleRate = 1)->
 		statsd.increment buildKey(key), sampleRate
+
+	count : (key, count, sampleRate = 1)->
+		statsd.count buildKey(key), count, sampleRate

 	timing: (key, timeSpan, sampleRate)->
 		statsd.timing(buildKey(key), timeSpan, sampleRate)


minor fix

minor fix

--- library/membrane/securing.coffee
+++ library/membrane/securing.coffee
@@ -118,7 +118,7 @@
     # is asynchronously wired in, so consult with `async` package.
     # Please be sure invoke the `next` arg to proceed, if relevant.
     ignition: (request, response, next) ->
-        str = (address) -> address.address
+        str = (address) -> address?.address
         inbound = request.connection.address()
         assert server = @kernel.server.address()
         assert secure = @kernel.secure.address()


removed popup of login form after successful registration (user got already logged in anyway)

--- client/app/MainApp/login/loginview.coffee
+++ client/app/MainApp/login/loginview.coffee
@@ -181,7 +181,7 @@
           duration  : 2000
         KD.getSingleton('router').clear()
         setTimeout =>
-          @animateToForm "login"
+          @hide()
           @registerForm.reset()
           @registerForm.button.hideLoader()
           # setTimeout =>


Fixed: render data was not passed to screen title

--- client/app/lib/popover_screen_view.coffee
+++ client/app/lib/popover_screen_view.coffee
@@ -51,7 +51,8 @@

     # Subrender routine. Render the title element.
     _renderTitle: ->
-        @titleElement.html @templateTitle(title: @screenTitle)
+        renderData = @getRenderData()
+        @titleElement.html @templateTitle(renderData)

     # Subrender routine. Render the content element.
@@ -63,7 +64,7 @@
     # Data to be passed to the template content. Can be overriden.
     # Returns a JS key/value object.
     getRenderData: ->
-        return _.extend {}, @model.toJSON()
+        return _.extend {}, @model.toJSON(), title: @screenTitle

     # Perform stuff after the element is in the DOM here. Defined in subclasses.


Fixes duplicate lines

Fixes duplicate lines

--- server/methods/addUserToRoom.coffee
+++ server/methods/addUserToRoom.coffee
@@ -43,8 +43,6 @@
 			t: 'au'
 			msg: newUser.name
 			u:
-				_id: newUser._id
-				username: data.username
 				_id: fromId
 				username: Meteor.user().username


Also position discussion icons aligned with the top of the annotation

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -110,15 +110,13 @@
     console.error 'FactlinkJailRoot: No text container found for ', el

   _updatePosition: =>
-    top = @$nearEl.offset().top + @$nearEl.outerHeight()/2 - @frame.$el.outerHeight()/2
-
     textContainer = @_textContainer(@$nearEl[0])
     contentBox = FactlinkJailRoot.contentBox(textContainer)

     left = contentBox.left + contentBox.width
     left = Math.min left, $(window).width() - @frame.$el.outerWidth()

-    @_showAtCoordinates top, left
+    @_showAtCoordinates @$nearEl.offset().top, left

     if FactlinkJailRoot.can_haz.debug_bounding_boxes
       @$boundingBox?.remove()


Simplify code using newer jQuery features

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -1,12 +1,10 @@
 class Button
   constructor: (dom_events={}) ->
     @frame = new FactlinkJailRoot.ControlIframe()
-    @frame.doc.body.innerHTML = @content
-    el = @el=  @frame.doc.body.firstChild
+    @frame.setContent($.parseHTML(@content))
+    @$el =  $(@frame.doc.body.firstChild)
     for event, callback of dom_events
-      el.addEventListener event, callback
-    @frame.resizeFrame()
-    @$el = $(el)
+      @$el.on event, callback

   startLoading: => @$el.addClass "fl-loading"
   stopLoading:  => @$el.removeClass  "fl-loading"


remove broken click event

remove broken click event

--- Framework/Views/iOS/TabGroup_Framework_iOS.coffee
+++ Framework/Views/iOS/TabGroup_Framework_iOS.coffee
@@ -25,8 +25,8 @@
       tab.window = item.view.window if item.view?

       @tabs.addTab(tab)
-      if item.onClick?
-        tab.addEventListener("click", item.onClick)
+      #if item.onClick?
+        #tab.addEventListener("click", item.onClick)

     @tabs.open()


deploy: only disable sourcemaps in non-dev environs

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -225,9 +225,13 @@
       # no need to send static file serving requests to webserver
       # serve static content from nginx
       location /a/ {
-        location ~* \.(map)$ {
-          return 404;
-          access_log off;
+
+        #{if environment isnt "dev" then "
+          location ~* \.(map)$ {
+            return 404;
+            access_log off;
+          }
+        " else ""
         }

         root #{KONFIG.projectRoot}/website/;


Added color picker option template method.

--- src/Templates.coffee
+++ src/Templates.coffee
@@ -3,29 +3,27 @@

 "
-exports.generatorColorPicker = "
-

-
Active Cell

-

-    <input type='text'
-            class='wolfcage-colorpicker-hexinput'
-            id='wolfcage-colorpicker-active-hex' />
-

-

-
Cell Border

-

-    <input type='text'
-            class='wolfcage-colorpicker-hexinput'
-            id='wolfcage-colorpicker-border-hex' />
-

-

-
Inactive Cell

-

-    <input type='text'
-            class='wolfcage-colorpicker-hexinput'
-            id='wolfcage-colorpicker-inactive-hex' />
+exports.colorPickers = "
+

+

+
Active Cell

+        <select id='wolfcage-colorpicker-active-hex' ></select>
+

+

+
Cell Border

+        <select id='wolfcage-colorpicker-border-hex'  ></select>
+

+

+
Inactive Cell

+        <select id='wolfcage-colorpicker-inactive-hex'  ></select>
+

 "
+
+exports.colorPickerOption = (color)=>
+    return "<option value='#{color.hex}' style='background-color:#{color.hex}'>
+                   #{color.name}
+            </option>"

 exports.generatorPreviewCell = ({leftBitActive, middleBitActive, rightBitActive, previewIndex}) =>
     leftBitClass = if leftBitActive then "wolfcage-generator-preview-cell-active" else ""


Add brackets for older node support when chaining

--- src/jira.coffee
+++ src/jira.coffee
@@ -126,7 +126,7 @@
       if shouldTransitionRegex.test(message)
         [ __, toState] =  message.match shouldTransitionRegex

-    fetch "#{jiraUrl}/rest/api/2/user/search?username=#{msg.message.user.email_address}"
+    fetch("#{jiraUrl}/rest/api/2/user/search?username=#{msg.message.user.email_address}")
     .then (user) ->
       reporter = user[0] if user and user.length is 1
       quoteRegex = /`{1,3}([^]*?)`{1,3}/
@@ -211,7 +211,7 @@
       message = ""
       id = ""
       ticket = issue.trim().toUpperCase()
-      fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}"
+      fetch("#{jiraUrl}/rest/api/2/issue/#{ticket}")
       .then (json) ->
         id = json.id
         message = """
@@ -267,7 +267,7 @@
     [ __, ticket, toState ] = msg.match
     ticket = ticket.toUpperCase()

-    fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}?expand=transitions.fields"
+    fetch("#{jiraUrl}/rest/api/2/issue/#{ticket}?expand=transitions.fields")
     .then (json) ->
       type = _(transitions).find (type) -> type.name is toState
       transition = json.transitions.find (state) -> state.to.name.toLowerCase() is type.jira.toLowerCase()
@@ -294,15 +294,13 @@
       when "down", "bottom" then direction = "Bottom"
       else throw "invalid direction"

-    fetch "#{jiraUrl}/rest/api/2/issue/#{ticket}"
+    fetch("#{jiraUrl}/rest/api/2/issue/#{ticket}")
     .then (json) ->
       if json.id
-        fetch("#{jiraUrl}/secure/Rank#{direction}.jspa?issueId=#{json.id}", headers: headers)
+        fetch "#{jiraUrl}/secure/Rank#{direction}.jspa?issueId=#{json.id}"
         #Note: this fetch returns HTML and cannot be validated/parsed for success
       else
         throw "Cannot find ticket `#{ticket}`"
-    .then (res) ->
-      checkStatus res
     .then () ->
       msg.send "<@#{msg.message.user.id}> Ranked `#{ticket}` to `#{direction}`"
     .catch (error) ->
@@ -316,7 +314,7 @@
     slackUser = lookupSlackUser person

     if slackUser
-      fetch "#{jiraUrl}/rest/api/2/user/search?username=#{slackUser.email_address}"
+      fetch("#{jiraUrl}/rest/api/2/user/search?username=#{slackUser.email_address}")
       .then (user) ->
         jiraUser = user[0] if user and user.length is 1
         if jiraUser


allow for external ranges json file. fixes #93

--- anon.coffee
+++ anon.coffee
@@ -36,9 +36,18 @@
   blocks.filter((block) -> isIpInRange(ip, block)).length > 0

 getConfig = (path) ->
+  config = loadJson path
+  # see if ranges are externally referenced as a separate .json files
+  if config.accounts
+    for account in config.accounts
+      if typeof account.ranges == 'string'
+        account.ranges = loadJson account.ranges
+  config
+
+loadJson = (path) ->
   if path[0] != '/' and path[0..1] != './'
     path = './' + path
-  require(path)
+  require path

 getStatusLength = (edit, name, template) ->
   # returns length of the tweet based on shortened url


[Defaults] #18 - Avoid _results created by

--- tests/src/app/features/defaults/module.spec.coffee
+++ tests/src/app/features/defaults/module.spec.coffee
@@ -111,6 +111,7 @@
         ]

         for plugin in gulpPlugins then expect(defaultOptions.plugins).to.have.property(plugin)
+        return

       it 'Contains all third-party plugins loaded in basebuild', ->
@@ -132,4 +133,5 @@
           "chalk"
         ]

-        for plugin in thirdPartyPlugins then expect(defaultOptions.plugins).to.have.property(plugin)+        for plugin in thirdPartyPlugins then expect(defaultOptions.plugins).to.have.property(plugin)
+        return


mixins

mixins

--- ccss.coffee
+++ ccss.coffee
@@ -7,6 +7,11 @@
     # a pair is either a css declaration, or a nested rule
     declarations = ''
     nested = {}
+
+    if mixin = pairs.mixin
+      delete pairs.mixin
+      for key, value of mixin
+        pairs[key] = value

     for key, value of pairs
       if typeof value is 'object'


More specs to cover new functionality (grid, removal)

--- components/related_posts/test/view.coffee
+++ components/related_posts/test/view.coffee
@@ -22,86 +22,115 @@
   after ->
     benv.teardown()

-  beforeEach (done) ->
-    sinon.stub Backbone, 'sync'
-    artist  = new Artist fabricate 'artist'
-    @view   = new RelatedPostsView { el: $('<fixture></fixture>'), model: artist, numToShow: 2, modelName: 'Artist' }
-    done()
+  describe 'default behavior', ->
+    beforeEach (done) ->
+      sinon.stub Backbone, 'sync'
+      artist  = new Artist fabricate 'artist'
+      @view   = new RelatedPostsView { el: $('<fixture></fixture>'), model: artist, numToShow: 2, modelName: 'Artist' }
+      done()

-  afterEach ->
-    Backbone.sync.restore()
+    afterEach ->
+      Backbone.sync.restore()

-  describe '#initialize', ->
-    it 'makes the right API call', ->
-      Backbone.sync.args[0][1].url.should.include 'api/v1/related/posts'
-      Backbone.sync.args[0][2].data['artist[]'].should.equal @view.model.id
+    describe '#initialize', ->
+      it 'makes the right API call', ->
+        Backbone.sync.args[0][1].url.should.include 'api/v1/related/posts'
+        Backbone.sync.args[0][2].data['artist[]'].should.equal @view.model.id

-  describe '#render', ->
-    it 'doesnt render anything if there are no results', ->
-      Backbone.sync.args[0][2].success []
-      @view.$el.html().should.include 'Do you have an interesting insight'
-      @view.$el.html().should.include 'Add Post'
+    describe '#render', ->
+      it 'doesnt render anything if there are no results', ->
+        Backbone.sync.args[0][2].success []
+        @view.$el.html().should.include 'Do you have an interesting insight'
+        @view.$el.html().should.include 'Add Post'

-    it 'renders the right content', ->
+      it 'renders the right content', ->
+        Backbone.sync.args[0][2].success [
+          fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
+          fabricate 'post', id: 'bitty-the-queen', title: 'Bitty is the best cat'
+        ]
+        @view.$el.html().should.include 'is-list'
+        @view.$el.html().should.include ''
+        @view.$el.html().should.include ''
+        @view.$el.find('li').length.should.equal 2
+        @view.$el.find('a.related-posts-show-all').length.should.equal 0
+
+      it 'correctly allows you to click see more', ->
+        Backbone.sync.args[0][2].success [
+          fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
+          fabricate 'post', id: 'bitty-the-queen', title: 'Bitty is the best cat'
+          fabricate 'post', id: 'dogs-are-just-ok', title: 'Dogs are eh'
+        ]
+        @view.$el.html().should.include ''
+        @view.$el.html().should.include ''
+        @view.$el.find('li').length.should.equal 2
+        @view.$el.find('a.related-posts-show-all').length.should.equal 1
+        @view.$el.find('a.related-posts-show-all').click()
+        @view.$el.html().should.include ''
+        @view.$el.html().should.include ''
+        @view.$el.html().should.include ''
+        @view.$el.find('li').length.should.equal 3
+
+      describe 'has 1 result', ->
+        it 'properly pluralizes the headline', ->
+          Backbone.sync.args[0][2].success [
+            fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
+          ]
+          @view.$el.html().should.include '1 Post About Pablo Picasso'
+
+      describe 'has 2 results', ->
+        it 'properly pluralizes the headline', ->
+          Backbone.sync.args[0][2].success [
+            fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
+            fabricate 'post', id: 'bitty-the-queen', title: 'Bitty is the best cat'
+          ]
+          @view.$el.html().should.include '2 Posts About Pablo Picasso'
+
+      describe '#addPost', ->
+        beforeEach ->
+          @e = preventDefault: sinon.stub()
+          sinon.stub location, 'reload'
+
+        afterEach ->
+          location.reload.restore()
+
+        it 'should pass through if user exists', ->
+          @view.currentUser = new CurrentUser fabricate('user', id: 'current-user-id')
+          @view.addPost @e
+          @e.preventDefault.called.should.be.ok
+          _.last(Backbone.sync.args)[2].success { results: [
+            fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
+          ]}
+          _.last(Backbone.sync.args)[2].success([])
+          location.href.should.containEql '/post'
+
+        it 'should intercept if user is *not* logged in', ->
+          RelatedPostsView.currentUser = false
+          @view.addPost @e
+          @e.preventDefault.called.should.be.ok
+
+  describe 'non-default behavior', ->
+    beforeEach (done) ->
+      sinon.stub Backbone, 'sync'
+      @artist = new Artist fabricate 'artist'
+      done()
+
+    afterEach ->
+      Backbone.sync.restore()
+
+    it 'can render a grid', ->
+      @view = new RelatedPostsView
+        el: $('<fixture></fixture>'), model: @artist, numToShow: 2, modelName: 'Artist', mode: 'grid'
       Backbone.sync.args[0][2].success [
         fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
         fabricate 'post', id: 'bitty-the-queen', title: 'Bitty is the best cat'
       ]
-      @view.$el.html().should.include ''
-      @view.$el.html().should.include ''
-      @view.$el.find('li').length.should.equal 2
-      @view.$el.find('a.related-posts-show-all').length.should.equal 0
+      @view.$el.html().should.include 'is-grid'
+      @view.$el.html().should.include 'figure'
+      @view.$el.html().should.include '
By Craig Spaeth
'

-    it 'correctly allows you to click see more', ->
-      Backbone.sync.args[0][2].success [
-        fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
-        fabricate 'post', id: 'bitty-the-queen', title: 'Bitty is the best cat'
-        fabricate 'post', id: 'dogs-are-just-ok', title: 'Dogs are eh'
-      ]
-      @view.$el.html().should.include ''
-      @view.$el.html().should.include ''
-      @view.$el.find('li').length.should.equal 2
-      @view.$el.find('a.related-posts-show-all').length.should.equal 1
-      @view.$el.find('a.related-posts-show-all').click()
-      @view.$el.html().should.include ''
-      @view.$el.html().should.include ''
-      @view.$el.html().should.include ''
-      @view.$el.find('li').length.should.equal 3
-
-    describe 'has 1 result', ->
-      it 'properly pluralizes the headline', ->
-        Backbone.sync.args[0][2].success [
-          fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
-        ]
-        @view.$el.html().should.include '1 Post About Pablo Picasso'
-
-    describe 'has 2 results', ->
-      it 'properly pluralizes the headline', ->
-        Backbone.sync.args[0][2].success [
-          fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
-          fabricate 'post', id: 'bitty-the-queen', title: 'Bitty is the best cat'
-        ]
-        @view.$el.html().should.include '2 Posts About Pablo Picasso'
-
-    describe '#addPost', ->
-      beforeEach ->
-        @e = preventDefault: sinon.stub()
-        sinon.stub location, 'reload'
-
-      afterEach ->
-        location.reload.restore()
-
-      it 'should pass through if user exists', ->
-        @view.currentUser = new CurrentUser fabricate('user', id: 'current-user-id')
-        @view.addPost @e
-        @e.preventDefault.called.should.be.ok
-        _.last(Backbone.sync.args)[2].success { results: [
-          fabricate 'post', id: 'cats-rule-dogs-drool-literally', title: 'Cats rule, and dogs drool'
-        ]}
-        _.last(Backbone.sync.args)[2].success([])
-        location.href.should.containEql '/post'
-
-      it 'should intercept if user is *not* logged in', ->
-        RelatedPostsView.currentUser = false
-        @view.addPost @e
-        @e.preventDefault.called.should.be.ok
+    it 'removes itself if empty', ->
+      @view = new RelatedPostsView
+        el: $('<fixture></fixture>'), model: @artist, numToShow: 2, modelName: 'Artist', canBeEmpty: false
+      @view.remove = sinon.stub()
+      Backbone.sync.args[0][2].success []
+      @view.remove.called.should.be.ok


fix C2CChoose snippet

fix C2CChoose snippet

--- snippets/language-coursescript.cson
+++ snippets/language-coursescript.cson
@@ -1852,8 +1852,8 @@
       Text: $6
       Text: $7
       Opts:
-      Text(Checked=true): $8
-      Text(Checked=true): $9
+      Text(checked=true): $8
+      Text(checked=true): $9
       Text: $10
       Text: $11
       Text: $12


fix indent

fix indent

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -31,4 +31,4 @@
       $.notifyRequest()
       btn.hide()
   else
-  	btn.hide()
+    btn.hide()


Load addthis with https

Load addthis with https

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -14,4 +14,4 @@
     window.addthis_share = null

     # Finally, load addthis
-    $.getScript "http://s7.addthis.com/js/250/addthis_widget.js#pubid=ra-526425ac0ea0780b"
+    $.getScript "https://s7.addthis.com/js/250/addthis_widget.js#pubid=ra-526425ac0ea0780b"


JS to add pattern attribute

JS to add pattern attribute

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -240,6 +240,10 @@
   $('.question-required').find('input,select,textarea').each ->
     $(this).prop('required', true)
     $(this).attr('aria-required', 'true')
+
+  $('.qae-form').find('input[type="number"]').each ->
+    $(this).attr('pattern', '[0-9]*')
+    $(this).attr('inputmode', 'decimal')

   # Calculates the UK Sales for Sus Dev form
   # UK sales = turnover - exports


FIX: subparse with partial matches required sourceMaps - guess what? We've had those for a while; just needed to link them up properly. Now you can have partial sub-parses even when the subparse-text isn't 1:1 with the parent text (as in the case of IndentBlock parsing)

--- source/Caffeine.Eight/Parser.coffee
+++ source/Caffeine.Eight/Parser.coffee
@@ -156,17 +156,17 @@
     options.parentParser = @
     if match = subparser.parse subsource, merge(options, isSubparse: true, logParsingFailures: @_logParsingFailures)
       {offset, matchLength, source, parser} = match
-      # log subsource: SUCCESS: {offset, matchLength, source, parser}
       match.subparseInfo = {offset, matchLength, source, parser}

-
-      # if options.allowPartialMatch was requested - and the match was partial...
       if match.matchLength < subsource.length
-
-        if match.text != parentNode.getNextText match.matchLength
-          throw new Error "INTERNAL TODO: SubParse was a partial match, but a source-map is required to determine the matchLength in the original source."
-
-        originalMatchLength = match.matchLength
+        # options.allowPartialMatch was requested and the match was partial...
+
+        originalMatchLength = if sourceMap
+          sourceMap(match.matchLength) - parentNode.nextOffset
+        else if match.text == parentNode.getNextText match.matchLength
+          match.matchLength
+        else
+          throw new Error "Subparse requires a sourceMap to determine the match-length in the parent text for partial-matches."

       match.offset      = parentNode.nextOffset
       match.matchLength = originalMatchLength


Adds rome to app/index.coffee

Adds rome to app/index.coffee

--- app/assets/javascripts/pano/app/index.coffee
+++ app/assets/javascripts/pano/app/index.coffee
@@ -2,4 +2,5 @@
 #= require dropzone
 #= require minicolors
 #= require redux
+#= require rome
 #= require_tree .


Cache currentBuilder

Cache currentBuilder

--- vendor/space-pen.coffee
+++ vendor/space-pen.coffee
@@ -27,22 +27,22 @@
   @builderStack: []

   elements.forEach (tagName) ->
-    View[tagName] = (args...) -> @currentBuilder().tag(tagName, args...)
+    View[tagName] = (args...) -> @currentBuilder.tag(tagName, args...)

   @subview: (name, view) ->
-    @currentBuilder().subview(name, view)
+    @currentBuilder.subview(name, view)

-  @text: (string) -> @currentBuilder().text(string)
+  @text: (string) -> @currentBuilder.text(string)

-  @raw: (string) -> @currentBuilder().raw(string)
-
-  @currentBuilder: ->
-    @builderStack[@builderStack.length - 1]
+  @raw: (string) -> @currentBuilder.raw(string)

   @pushBuilder: ->
-    @builderStack.push(new Builder)
+    builder = new Builder
+    @builderStack.push(builder)
+    @currentBuilder = builder

   @popBuilder: ->
+    @currentBuilder = @builderStack[@builderStack.length - 2]
     @builderStack.pop()

   @buildHtml: (fn) ->


Don't run an animation if there is nothing to animate

--- src/animation.coffee
+++ src/animation.coffee
@@ -29,7 +29,7 @@

 	AnimationProperties: [
 		"view", "properties", "curve", "time",
-		"origin", "tolerance", "precision", "modifiers"
+		"origin", "tolerance", "precision", "modifiers", "limits"
 		"debug", "profile", "callback"
 	]
 	AnimatableCSSProperties: {
@@ -161,8 +161,8 @@

 		# Throw a warning if we have nothing to animate
 		if animatedProperties.length is 0
-			console.error "Animation[#{@animationId}] Warning: nothing to animate"
-
+			console.log "Animation[#{@animationId}] Warning: nothing to animate"
+			return

 		########################################################
 		# Generate the keyframe css and insert


forgot injection

forgot injection

--- addon/components/autox-show-for.coffee
+++ addon/components/autox-show-for.coffee
@@ -10,6 +10,7 @@
   layout: layout
   tagName: "ul"
   workflow: inject.service "workflow"
+  lookup: inject.service "lookup"
   classNames: ["autox-show-for"]
   classNameBindings: ["userHasDefinedComponent::list-group"]
   ctx: apply "workflow", "model", (wf, model) -> wf.fetchCtx(model)


Fix for new theme

Fix for new theme

--- lib/grammar-selector.coffee
+++ lib/grammar-selector.coffee
@@ -15,6 +15,7 @@
   initialize: ->
     @editor = rootView.getActiveView()
     return unless @editor instanceof Editor
+    @list.addClass('mark-active') # TODO: there may be a better way to specify this.
     @currentGrammar = @editor.getGrammar()
     @autoDetect = name: 'Auto Detect'
     @currentGrammar = @autoDetect if @currentGrammar is syntax.nullGrammar
@@ -28,10 +29,8 @@
     @attach()

   itemForElement: (grammar) ->
-    if grammar is @currentGrammar
-      grammarClass = 'active-item'
-    else
-      grammarClass = 'inactive-item'
+    grammarClass = ''
+    grammarClass = 'active' if grammar is @currentGrammar

     $$ ->
       @li grammar.name, class: grammarClass


Don't fetch field guide images unless the guide exists

--- app/pages/project/potential-field-guide.cjsx
+++ app/pages/project/potential-field-guide.cjsx
@@ -27,7 +27,7 @@
     apiClient.type('field_guides').get project_id: project.id
       .then ([guide]) =>
         @setState {guide}
-        guide.get('attached_images').then (images) =>
+        guide?.get('attached_images')?.then (images) =>
           icons = {}
           for image in images
             icons[image.id] = image


use the preferred outlet

use the preferred outlet

--- lib/grammar-status-view.coffee
+++ lib/grammar-status-view.coffee
@@ -5,7 +5,7 @@
 class GrammarStatusView extends View
   @content: ->
     @div class: 'grammar-status', =>
-      @a href: '#', class: 'inline-block'
+      @a href: '#', class: 'inline-block', outlet: 'grammarLink'

   initialize: (@statusBar) ->
     @subscribe @statusBar, 'active-buffer-changed', =>
@@ -38,9 +38,8 @@
         grammarName = 'Plain Text'
       else
         grammarName = grammar.name ? grammar.scopeName
-      @children()
+      @grammarLink
         .text(grammarName)
         .attr('title', grammarName)
-        .show()
     else
       @hide()


:bug: Remove subscription to contents-modified signal

--- lib/minimap-editor-view.coffee
+++ lib/minimap-editor-view.coffee
@@ -60,9 +60,6 @@

     @subscribe @editor, 'screen-lines-changed.minimap', (changes) =>
       @pendingChanges.push changes
-      @requestUpdate()
-
-    @subscribe @editor, 'contents-modified.minimap', =>
       @requestUpdate()

     @subscribe @displayBuffer, 'tokenized.minimap', =>


:bug: Fix retrieval of the dom colors

:bug: Fix retrieval of the dom colors

--- lib/minimap-render-view.coffee
+++ lib/minimap-render-view.coffee
@@ -331,7 +331,8 @@
   #
   # Returns a {String}.
   getDefaultColor: ->
-    @transparentize(@minimapView.editorView.css('color'), @getTextOpacity())
+    color = @retrieveStyleFromDom(['.dummy'], 'color')
+    @transparentize(color, @getTextOpacity())

   # Returns the text color for the passed-in `token` object.
   #
@@ -416,7 +417,7 @@
     unless @dummyNode?
       @dummyNode = document.createElement('span')
       @dummyNode.style.visibility = 'hidden'
-      @editorView.append(@dummyNode)
+      @editorView.shadowRoot.appendChild(@dummyNode)

   # Internal: Converts a `rgb(...)` color into a `rgba(...)` color
   # with the specified opacity.


Handle special case where network is unavailable and package is not in cache

--- lib/package-detail-view.coffee
+++ lib/package-detail-view.coffee
@@ -87,7 +87,7 @@
   fetchPackage: ->
     @showLoadingMessage()
     @packageManager.getClient().package @pack.name, (err, packageData) =>
-      if err
+      if err || !(packageData?.name?)
         @hideLoadingMessage()
         @showErrorMessage()
       else


clearer reconnection flow

clearer reconnection flow

--- src/main.coffee
+++ src/main.coffee
@@ -7,6 +7,7 @@
 tmp       = require 'tmp'
 clipboard = require('electron').clipboard
 Menu      = require('electron').menu
+session = require('electron').session

 tmp.setGracefulCleanup()

@@ -91,8 +92,9 @@
            {url:'https://plus.google.com', env:'HTTPS_PROXY'}
         ]
         Q.all todo.map (t) -> Q.Promise (rs) ->
-            session = require('electron').session
+            console.log "resolving proxy #{t.url}"
             session.defaultSession.resolveProxy t.url, (proxyURL) ->
+                console.log "resolved proxy #{proxyURL}"
                 # Format of proxyURL is either "DIRECT" or "PROXY 127.0.0.1:8888"
                 [_, purl] = proxyURL.split ' '
                 process.env[t.env] ?= if purl then "http://#{purl}" else ""
@@ -149,8 +151,18 @@
     # keeps trying to connec the hangupsjs and communicates those
     # attempts to the client.
     reconnect = ->
+      console.log 'reconnecting', reconnectCount
       proxycheck().then ->
-        client.connect(creds)
+          client.connect(creds)
+          .then ->
+              console.log 'connected', reconnectCount
+              # on first connect, send init, after that only resync
+              if reconnectCount == 0
+                  sendInit()
+              else
+                  syncrecent()
+              reconnectCount++
+          .catch (e) -> console.log 'error connecting', e

     # counter for reconnects
     reconnectCount = 0
@@ -158,18 +170,7 @@
     # whether to connect is dictated by the client.
     ipc.on 'hangupsConnect', ->
         console.log 'hconnect'
-        # first connect
         reconnect()
-        .then ->
-            console.log 'connected', reconnectCount
-            # on first connect, send init, after that only resync
-            if reconnectCount == 0
-                sendInit()
-            else
-                syncrecent()
-            reconnectCount++
-        .catch (e) -> console.log 'error connecting', e
-

     ipc.on 'hangupsDisconnect', ->
         console.log 'hdisconnect'
@@ -181,8 +182,8 @@
     mainWindow.on 'move',  (ev) -> ipcsend 'move', mainWindow.getPosition()

     # whenever it fails, we try again
-    client.on 'connect_failed', ->
-        console.log 'connect_failed'
+    client.on 'connect_failed', (e) ->
+        console.log 'connect_failed', e
         wait(3000).then -> reconnect()

     # when client requests (re-)init since the first init


should not ref function types

should not ref function types

--- src/main.coffee
+++ src/main.coffee
@@ -250,7 +250,8 @@
         id = obj.__r_id
         # to see if it is a cyclic reference
         cached = if map[host][port][id]? then map[host][port][id] else null
-        if cached
+        # there is no point to create reference for functions
+        if cached and typeof obj isnt 'function'
             # new ref type
             return @__createRemoteDesc(id, host, port, 'ref')
         else
@@ -260,11 +261,11 @@
                 return @serializeForRemoteTypes(obj, map)
             # serialize function
             if typeof obj is 'function'
-                funcDesc = @__newRemoteFunctionDesc(id, @host, @port)
+                funcDesc = @__newRemoteFunctionDesc(id, host, port)
                 return funcDesc
             # serialize object
             if lodash.isArray(obj)
-                objDesc = @__newRemoteArrayDesc(id, @host, @port)
+                objDesc = @__newRemoteArrayDesc(id, host, port)
                 #we only care elements in array
                 #if the array has member function
                 for element in obj


get self info and identities from client.init

--- src/main.coffee
+++ src/main.coffee
@@ -83,28 +83,21 @@
   loadAppWindow: -> @mainWindow.loadUrl 'file://' + __dirname + '/ui/index.html'
   refresh: -> @mainWindow.webContents.send 'model:update', @model
   clientConnectionSuccess: =>
+    self = @client.init.self_entity
+    @model.self =
+      id: self.id.chat_id
+      username: self.properties.display_name
+      photo_url: self.properties.photo_url
+    @model.identityAdd @model.self.id, @model.self.username, @model.self.photo_url
+    entities = @client.init.entities
+    entities.forEach (ntt) =>
+      @model.identityAdd ntt.id.chat_id, ntt.properties.display_name, ntt.properties.photo_url
     @model.connection = 'online'
     @refresh()
-    promise = @getselfinfo()
-    promise.then =>
-      @syncrecentconversations().fail (err) -> console.log 'error', err, err.stack
-    pormise.fail (err) -> console.log 'error', err, err.stack
+    @syncrecentconversations().fail (err) -> console.log 'error', err, err.stack
   clientConnectionError: =>
     @model.connection = 'error'
     @refresh()
-  getselfinfo: ->
-    ret = client.getselfinfo()
-    success = (res) =>
-      fs = require('fs')
-      @model.self.username = name = res.self_entity.properties.display_name
-      @model.self.photo_url = res.self_entity.properties.photo_url
-      @model.self.id = id = res.self_entity.id.chat_id
-      @model.identityAdd id, name
-      @refresh()
-    failure = (error) =>
-      console.log 'error', error
-    ret = ret.then success, failure
-    return ret
   syncrecentconversations: =>
     client.syncrecentconversations().then (data) =>
       @model.loadRecentConversations data


correct for Windows paths

correct for Windows paths

--- lib/util/epicenter/sync.coffee
+++ lib/util/epicenter/sync.coffee
@@ -43,6 +43,7 @@
             if tryCount > MAX_RETRIES
                 die()

+        simPath = simPath.replace(/\\/g,"/")
         uploader.uploadFileAPI conf.domain, localPath, simPath, token, (err, stdout, stderr) ->
             diff = process.hrtime time
             formattedDiff = ((diff[0] * 1e9 + diff[1]) / 1000000).toFixed(0)
@@ -51,6 +52,7 @@
                 #Server sometimes doesn't return json for no reason, ignore and try again
                 response = JSON.parse stdout
             catch
+                console.log simPath + "|" + localPath
                 return upload localPath, stats, tryCount + 1

             if not response.status? or +response.status in [200, 201]


add some debug output

add some debug output

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -300,10 +300,15 @@
     # of the resource. Use for unobtrusive retrieval of resources.
     # The method is an HTTP verb, coherent with the REST interface.
     GET: (request, response, resource, domain, session) ->
+        assert identify = try @constructor.identify()
         assert symbol = "$root".toString().toLowerCase()
         assert args = [symbol, request, yes, undefined]
+        message = "Compile visual context of %s service"
+        logger.debug message.grey, identify.underline
         @assembleContext args..., (context, compiled) ->
+            sz = "Compied %s bytes of a visual context"
             length = do -> compiled.length or undefined
+            logger.debug sz.grey, "#{length}".underline
             assert _.isString response.charset = "utf-8"
             response.setHeader "Content-Length", length
             response.setHeader "Content-Type", "text/html"


Add an UUID v4 identifier in the service

--- library/nucleus/service.coffee
+++ library/nucleus/service.coffee
@@ -25,6 +25,7 @@

 assert = require "assert"
 logger = require "winston"
+uuid = require "node-uuid"
 events = require "eventemitter2"
 colors = require "colors"
 util = require "util"
@@ -58,7 +59,7 @@
     # the kernel instance as a parameter. You can override it as
     # you see fit, but be sure to invoke the super constructor and
     # it is highly advised to store the kernel instance in object.
-    constructor: (@kernel) -> super
+    constructor: (@kernel) -> @uuid = uuid.v4()

     # Either obtain or set the HTTP location of the current service.
     # This method is a proxy that forwards the invocation to the


Fix test

Fix test

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -169,7 +169,7 @@

         it "highlights the found text in the new edit session when find next is triggered", ->
           findResultsView = editor.find('.search-results')
-          rootView.open('sample.coffee')
+          rootView.openSync('sample.coffee')
           editor = rootView.getActiveView()

           findView.trigger 'find-and-replace:find-next'


add test for the bug

add test for the bug

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -390,7 +390,7 @@

     describe "with multiple cursors", ->
       it "deletes each selection", ->
-        editor.setText("abcd\n1234\nABCD")
+        editor.setText("abcd\n1234\nABCD\n")
         editor.setCursorBufferPosition([0, 1])
         editor.addCursorAtBufferPosition([1, 2])
         editor.addCursorAtBufferPosition([2, 3])
@@ -403,6 +403,23 @@
           [0, 0],
           [1, 1],
           [2, 2],
+        ]
+
+      it "doesn't delete empty selections", ->
+        editor.setText("abcd\nabc\nabd")
+        editor.setCursorBufferPosition([0, 0])
+        editor.addCursorAtBufferPosition([1, 0])
+        editor.addCursorAtBufferPosition([2, 0])
+
+        keydown('d')
+        keydown('t')
+        commandModeInputKeydown('d')
+
+        expect(editor.getText()).toBe "d\nabc\nd"
+        expect(editor.getCursorBufferPositions()).toEqual [
+          [0, 0],
+          [1, 0],
+          [2, 0],
         ]

   describe "the D keybinding", ->


Fix clipboard

Fix clipboard

--- views/exportModule.cjsx
+++ views/exportModule.cjsx
@@ -1,5 +1,5 @@
 {React, ReactBootstrap, JSON, toggleModal} = window
-{Button, Input} = ReactBootstrap
+{Button, FormControl} = ReactBootstrap
 {shell} = require 'electron'
 {openExternal} = shell
 clipboard = require('electron').clipboard
@@ -18,7 +18,7 @@
       @setState
         code: code
   handleCopyClick: ->
-    clipboard.writeText @state.code, 'selection'
+    clipboard.writeText @state.code
     toggleModal __('Copy'), __('The code has been copied to the clipboard.')
   render: ->
     <div className='tab-container'>
@@ -36,11 +36,11 @@
         <Button bsSize='small' onClick={@handleCopyClick}>
           {__('Copy')}
         </Button>
-        <Input style={height: '250px'}
-               type='textarea'
-               label={__ 'Code'}
-               placeholder={__ 'Code'}
-               value={@state.code} />
+        <FormControl style={height: '250px'}
+                     componentClass='textarea'
+                     label={__ 'Code'}
+                     placeholder={__ 'Code'}
+                     value={@state.code} />
       </div>
     </div>


:white_check_mark:  #376 spec for clearMultipleCursorsOnEscapeInsertMode

--- spec/vim-state-spec.coffee
+++ spec/vim-state-spec.coffee
@@ -213,6 +213,24 @@
     it "puts the editor into normal mode when <ctrl-c> is pressed", ->
       withMockPlatform editorElement, 'platform-darwin' , ->
         ensure 'ctrl-c', mode: 'normal'
+
+    describe "clearMultipleCursorsOnEscapeInsertMode setting", ->
+      beforeEach ->
+        set
+          text: 'abc'
+          cursor: [[0, 0], [0, 1]]
+
+      describe "when enabled", ->
+        beforeEach ->
+          settings.set('clearMultipleCursorsOnEscapeInsertMode', true)
+        it "clear multiple cursor on escape", ->
+          ensure 'escape', mode: 'normal', numCursors: 1
+
+      describe "when disabled", ->
+        beforeEach ->
+          settings.set('clearMultipleCursorsOnEscapeInsertMode', false)
+        it "clear multiple cursor on escape", ->
+          ensure 'escape', mode: 'normal', numCursors: 2

   describe "replace-mode", ->
     describe "with content", ->