Update de-DE.coffee

Update de-DE.coffee

New strings translated. Nearly done, till Eastern I will finish this and start comparing with the english strings again.

--- app/locale/de-DE.coffee
+++ app/locale/de-DE.coffee
@@ -701,36 +701,36 @@
     more_info_3: "ist eine gute Anlaufstelle, um mit anderen Lehrer in Kontakt zu treten, die CodeCombat benutzen."

   teachers_quote:
-#    name: "Quote Form"
-#    title: "Request a Quote"
-#    subtitle: "Get CodeCombat in your classroom, club, school or district!"
-#    email_exists: "User exists with this email."
+    name: "Demo Fragebogen"
+    title: "Eine Demo beantragen"
+    subtitle: "Holen Sie sich CodeCombat in ihren Klassenraum, Club, ihre Schule oder Ortsteil!"
+    email_exists: "Benutzer mit dieser Email existiert bereits."
     phone_number: "Telephonnummer"
-#    phone_number_help: "Where can we reach you during the workday?"
+    phone_number_help: "Wo können wir Sie wochentags erreichen?"
     role_label: "Ihre Rolle"
     role_help: "Wählen Sie ihre primäre Rolle."
-#    tech_coordinator: "Technology coordinator"
-#    advisor: "Advisor"
-#    principal: "Principal"
-#    superintendent: "Superintendent"
+    tech_coordinator: "Technologie-Koordinator"
+    advisor: "Berater"
+    principal: "Direktor"
+    superintendent: "Leiter"
     parent: "Elternteil"
     organization_label: "Name der Schule/Bezirk"
     city: "Stadt"
     state: "Bundesland"
     country: "Land"
-#    num_students_help: "How many do you anticipate enrolling in CodeCombat?"
-#    education_level_label: "Education Level of Students"
-#    education_level_help: "Choose as many as apply."
+    num_students_help: "Wie viele beabsichtichen Sie in CodeCombat einzuschreiben?"
+    education_level_label: "Bildungslevel der Schüler"
+    education_level_help: "Wählen Sie so viele wie zutreffen."
     elementary_school: "Grundschule"
     high_school: "Gymnasium"
     please_explain: "(bitte erklären)"
     middle_school: "Mittelschule"
     college_plus: "Hochschule oder höher"
     anything_else: "Weitere Dinge die wir wissen sollten?"
-#    thanks_header: "Thanks for requesting a quote!"
-    thanks_p: "Wir melden uns bald. Fragen? Schreiben Sie uns eine Email :"
-#    thanks_anon: "Login or sign up with your account below to access your two free enrollments (we’ll notify you by email when they have been approved, which usually takes less than 48 hours). As always, the first hour of content is free for an unlimited number of students."
-#    thanks_logged_in: "Your two free enrollments are pending approval. We’ll notify you by email when they have been approved (usually within 48 hours). As always, the first hour of content is free for an unlimited number of students."
+    thanks_header: "Danke dass Sie eine Demo beantragen!"
+    thanks_p: "Wir melden uns bald. Fragen? Schreiben Sie uns eine Email:"
+    thanks_anon: "Loggen Sie sich unten ein oder Erstellen Sie einen Account um Zugang zu ihren zwei kostenlosen Einschreibungen zu erhalten (Wir werden Sie per Email benachrichtigen nach der Überprüfung, die normalerweise weniger als 48 Stunden dauert. So wie immer ist die erste Stunde des Inhaltes kostenlos für eine unbegrenzte Anzahl von Schülern."
+    thanks_logged_in: "Ihre zwei kostenlosen Einschreiben werden gerade überprüft. Wir werden Sie per Email nach der Überprüfung benachrichtigen, die normalerweise weniger als 48 Stunden dauert. So wie immer ist die erste Stunde des Inhaltes kostenlos für eine unbegrenzte Anzahl von Schülern."

   versions:
     save_version_title: "Neue Version speichern"
@@ -916,28 +916,28 @@
     edit_settings: "Klasseneinstellungen bearbeiten"
     edit_settings1: "Klasseneinstellungen bearbeiten"
     progress: "Klassenfortschritt"
-    add_students: "Studenten hinzufügen"
+    add_students: "Schüler hinzufügen"
     stats: "Statistiken"
-    total_students: "Anzahl Studenten:"
+    total_students: "Anzahl Schüler:"
     average_time: "Durchschnittliche Levelspielzeit:"
     total_time: "Gesamte Spielzeit:"
     average_levels: "Durchschnittliche abgeschlossene Level:"
     total_levels: "Anzahl abgeschlossener Level:"
     furthest_level: "Höchster abgeschlossener Level:"
     concepts_covered: "Abgedeckte Konzepte"
-    students: "Studenten"
-    students1: "Studenten"
+    students: "Schüler"
+    students1: "Schüler"
     concepts: "Konzepten"
     levels: "level"
     played: "Gespielt"
     play_time: "Spielzeit:"
     completed: "Abgeschlossen:"
-    invite_students: "Studenten einladen dieser Klasse beizutreten."
+    invite_students: "Schüler einladen dieser Klasse beizutreten."
     invite_link_header: "Link um dem Kurs beizutreten"
     invite_link_p_1: "Gib diesen Link an Schüler weiter, die dem Kurs beitreten sollen."
     invite_link_p_2: "Oder lass sie durch uns direkt anschreiben:"
     capacity_used: "Benutzte Kurs-Slots:"
-    enter_emails: "Studenten-Emails eingeben zum hinzufügen, eine per Zeile"
+    enter_emails: "Schüler-Emails eingeben zum hinzufügen, eine Adresse pro Zeile"
     send_invites: "Einladung senden"
     title: "Titel"
     description: "Beschreibung"
@@ -948,117 +948,117 @@
     create_class: "Klasse erstellen"
     select_all_courses: "Wählen Sie 'Alle Kurse' für einen 50% Rabatt!"
     all_courses: "Alle Kurse"
-    number_students: "Anzahl der Studenten"
-    enter_number_students: "Geben Sie die Anzahl der Studenten ein, die Sie für diese Klasse brauchen."
+    number_students: "Anzahl der Schüler"
+    enter_number_students: "Geben Sie die Anzahl der Schüler ein, die Sie für diese Klasse brauchen."
     name_class: "Benennen Sie ihre Klasse"
     displayed_course_page: "Diese Seite wird für Sie und ihre Schüler auf der Kursseite angezeigt. Sie kann später verändert werden."
     buy: "Kaufen"
     purchasing_for: "Sie sind dabei eine Lizenz für"
     creating_for: "Sie sind dabei eine Klasse zu erstellen für"
     for: "für" # Like in 'for 30 students'
-#    receive_code: "Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class."
-#    free_trial: "Free trial for teachers!"
-#    get_access: "to get individual access to all courses for evalutaion purposes."
+    receive_code: "Danach werden Sie einen Einschreibecode zum Verteilen an ihre Schüler bekommen, den diese benutzen können um sich in die Klasse einzuschreiben."
+    free_trial: "Kostenloser Testversion für Lehrer!"
+    get_access: "um Zugang zu allen Kursen zur Evaluation zu bekommen."
     questions: "Fragen?"
     teachers_click: "Lehrkräfte klicken hier"
     students_click: "Schüler klicken hier"
     courses_on_coco: "Kurse auf CodeCombat"
-#    designed_to: "Courses are designed to introduce computer science concepts using CodeCombat's fun and engaging environment. CodeCombat levels are organized around key topics to encourage progressive learning, over the course of 5 hours."
-#    more_in_less: "Learn more in less time"
-#    no_experience: "No coding experience necesssary"
-#    easy_monitor: "Easily monitor student progress"
-#    purchase_for_class: "Purchase a course for your entire class. It's easy to sign up your students!"
-#    see_the: "See the"
+    designed_to: "Die Kurse sind designt um in Informatikkonzepte einzuführen mit Hilfe von CodeCombat's spaßiger und fesselnder Umgebung. CodeCombat-Level sind um Schlüsselthemen organisiert um ein schrittweises Lernen zu fördern, in einem Kurs von fünf Stunden."
+    more_in_less: "Lernen Sie mehr in weniger Zeit"
+    no_experience: "Keine Programmiererfahrung nötig"
+    easy_monitor: "Verfolgen Sie einfach den Fortschritt ihrer Schüler"
+    purchase_for_class: "Bestellen Sie einen Kurs für die ganze Klasse. Es ist einfach für ihre Studenten sich anzumelden!"
+    see_the: "Sehen Sie"
     more_info: "für weitere Informationen."
-#    choose_course: "Choose Your Course:"
-#    enter_code: "Enter an unlock code to join an existing class"
-#    enter_code1: "Enter unlock code"
-#    enroll: "Enroll"
-#    pick_from_classes: "Pick from your current classes"
-#    enter: "Enter"
+    choose_course: "Wählen Sie ihren Kurs:"
+    enter_code: "Geben Sie einen Einschreibecode ein um einer existierenden Klasse beizutreten"
+    enter_code1: "Einschreibecode hier eingeben"
+    enroll: "Einschreiben"
+    pick_from_classes: "Wählen Sie von ihrer derzeitigen Klasse"
+    enter: "Eingabe"
     or: "oder"
     topics: "Inhalte"
-#    hours_content: "Hours of content:"
-#    get_free: "Get FREE course"
-#    enroll_paid: "Enroll Students in Paid Courses"
+    hours_content: "Stundenanzahl:"
+    get_free: "Bekommen Sie einen kostenlosen Kurs"
+    enroll_paid: "Schreiben Sie Schüler in Bezahlkurse ein"
     you_have1: "Sie haben"
     you_have2: "ungenutze bezahlte Einschreibungen"
-#    use_one: "Use 1 paid enrollment for"
-#    use_multiple: "Use paid enrollments for the following students:"
-#    already_enrolled: "already enrolled"
-#    licenses_remaining: "licenses remaining:"
-#    insufficient_enrollments: "insufficient paid enrollments"
-#    enroll_students: "Enroll Students"
-#    get_enrollments: "Get More Enrollments"
-#    change_language: "Change Course Language"
-#    keep_using: "Keep Using"
-#    switch_to: "Switch To"
+    use_one: "Benutzen Sie eine bezahlte Einschreibung für"
+    use_multiple: "Benutzen Sie bezahlte Einschreibungen für folgende Schüler:"
+    already_enrolled: "bereits eingeschrieben"
+    licenses_remaining: "verbleibende Lizenzen:"
+    insufficient_enrollments: "unzureichende bezahlte Einschreibungen"
+    enroll_students: "Schüler einschreiben"
+    get_enrollments: "Mehr Einschreibungen erhalten"
+    change_language: "Kurssprache wechseln"
+    keep_using: "Beibehalten"
+    switch_to: "Wechseln zu"
     greetings: "Grüße!"
     learn_p: "Erlerne Python"
     learn_j: "Erlerne JavaScript"
     language_cannot_change: "Die Sprache kann nicht geändert werden sobald ein Schüler der Klasse beigetreten ist."
     back_classrooms: "Zurück zu meinem Klassenraum"
     back_courses: "Zurück zu meinen Kursen"
-#    edit_details: "Edit class details"
-#    enrolled_courses: "enrolled in paid courses:"
-#    purchase_enrollments: "Purchase Enrollments"
+    edit_details: "Klassendetails bearbeiten"
+    enrolled_courses: "eingeschrieben in bezahlte Kurse:"
+    purchase_enrollments: "Einschreibungen kaufen"
     remove_student: "Schüler entfernen"
-#    assign: "Assign"
-#    to_assign: "to assign paid courses."
+    assign: "Zuordnen"
+    to_assign: "um bezahlte Kurse zuzuordnen."
     teacher: "Lehrer"
-#    complete: "Complete"
-#    none: "None"
+    complete: "Abgeschlossen"
+    none: "Keine"
     save: "Speichern"
     play_campaign_title: "Spiele die Kampagnen Version"
-#    play_campaign_description: "You’re ready to take the next step! Explore hundreds of challenging levels, learn advanced programming skills, and compete in multiplayer arenas!"
-#    create_account_title: "Create an Account"
-#    create_account_description: "Sign up for a FREE CodeCombat account and gain access to more levels, more programming skills, and more fun!"
-#    preview_campaign_title: "Preview Campaign"
-#    preview_campaign_description: "Take a sneak peek at all that CodeCombat has to offer before signing up for your FREE account."
+    play_campaign_description: "Du bist bereit für den nächsten Schritt! Entdecke hunderte von herausfordernden Leveln, lerne weiterführende Programmierfähigkeiten, und trete in Multiplayer Arenen an!"
+    create_account_title: "Einen Account erstellen"
+    create_account_description: "Registriere dich für einen kostenlosen CodeCombat-Account und bekomme Zugriff zu mehr Leveln, mehr Programmierfähigkeiten, und mehr Spaß!"
+    preview_campaign_title: "Kampagnenvorschau"
+    preview_campaign_description: "Nimm eine kurze Vorschau auf all das was CodeCombat zu bietne hat bevor du dich für einen kostenlosen Account registrierst."
     arena: "Arena"
     arena_soon_title: "Die Arena kommt bald"
-#    arena_soon_description: "We are working on a multiplayer arena for classrooms at the end of"
-#    not_enrolled1: "Not enrolled"
-#    not_enrolled2: "Ask your teacher to enroll you in the next course."
+    arena_soon_description: "Wir arbeiten an einer Multiplayerarena für Klassenräume bis zum Ende von"
+    not_enrolled1: "Nicht eingschrieben"
+    not_enrolled2: "Frage deine Lehrkraft ob sie dich in den nächsten Kurs einschreibt."
     next_course: "Nächster Kurs"
     coming_soon1: "Coming soon"
-#    coming_soon2: "We are hard at work making more courses for you!"
-#    available_levels: "Available Levels"
-#    welcome_to_courses: "Adventurers, welcome to Courses!"
+    coming_soon2: "Wir sind hart am Arbeiten um mehr Kurse für Sie zu erstellen!"
+    available_levels: "Verfügbare Level"
+    welcome_to_courses: "Abenteurer, willkomen zu den Kursen!"
     ready_to_play: "Bereit zum Spielen?"
     start_new_game: "Starten Sie ein neues Spiel"
     play_now_learn_header: "Jetzt spielen um folgendes zu lernen"
-#    play_now_learn_1: "basic syntax to control your character"
-#    play_now_learn_2: "while loops to solve pesky puzzles"
-#    play_now_learn_3: "strings & variables to customize actions"
-#    play_now_learn_4: "how to defeat an ogre (important life skills!)"
-#    welcome_to_page: "Welcome to your Courses page!"
+    play_now_learn_1: "grundlegende Syntax um deinen Character zu kontrollieren"
+    play_now_learn_2: "while-Schleifen um nervtötende Rätsel zu lösen"
+    play_now_learn_3: "Strings & Variablen um Aktionen anzupassen"
+    play_now_learn_4: "Wie man einen Oger besiegt (wichtige Lebensfähigkeit!)"
+    welcome_to_page: "Willkomen auf deine Kurseseite!"
 #    completed_hoc: "Amazing! You've completed the Hour of Code course!"
-#    ready_for_more_header: "Ready for more? Play the campaign mode!"
-#    ready_for_more_1: "Use gems to unlock new items!"
-#    ready_for_more_2: "Play through brand new worlds and challenges"
-#    ready_for_more_3: "Learn even more programming!"
-#    saved_games: "Saved Games"
+    ready_for_more_header: "Bereit für mehr? Spiele den Kampagnenmodus!"
+    ready_for_more_1: "Benutze Edelsteine um neue Items freizuschalten!"
+    ready_for_more_2: "Spiele brandneue Welten und Herausforderungen"
+    ready_for_more_3: "Lerne noch mehr zur Programmierung!"
+    saved_games: "Gespeicherte Spiele"
 #    hoc: "Hour of Code"
     my_classes: "Meine Klassen"
-#    class_added: "Class successfully added!"
+    class_added: "Klasse erfolgreich hinzugefügt!"
 #    view_class: "view class"
 #    view_levels: "view levels"
-#    join_class: "Join A Class"
+    join_class: "Einer Klasse beitreten"
 #    ask_teacher_for_code: "Ask your teacher if you have a CodeCombat class code! If so, enter it below:"
-#    enter_c_code: "<Enter Class Code>"
+    enter_c_code: "<Klassencode hier eingeben>"
     join: "Beitreten"
     joining: "Der Klasse beitreten"
-#    course_complete: "Course Complete"
-#    play_arena: "Play Arena"
+    course_complete: "Kurs Abgeschlossen"
+    play_arena: "Arena spielen"
     start: "Start"
     last_level: "Letzter Level"
 #    welcome_to_hoc: "Adventurers, welcome to our Hour of Code!"
-#    logged_in_as: "Logged in as:"
-#    not_you: "Not you?"
-#    welcome_back: "Hi adventurer, welcome back!"
-#    continue_playing: "Continue Playing"
-#    more_options: "More options:"
+    logged_in_as: "Angemeldet als:"
+    not_you: "Nicht du?"
+    welcome_back: "Hallo Abenteurer, willkommen zurück!"
+    continue_playing: "Weiterspielen"
+    more_options: "Mehr Optionen:"
 #    option1_header: "Option 1: Invite students via email"
 #    option1_body: "Students will automatically be sent an invitation to join this class, and will need to create an account with a username and password."
 #    option2_header: "Option 2: Send URL to your students"
@@ -1084,7 +1084,7 @@
 #    to_join_ask: "To join a class, ask your teacher for an unlock code."
     join_this_class: "Der Klasse beitreten"
 #    enter_here: "<enter unlock code here>"
-#    successfully_joined: "Successfully joined"
+    successfully_joined: "Erfolgreich beigetreten"
 #    click_to_start: "Click here to start taking"
     my_courses: "Meine Kurse"
     classroom: "Klassenraum"
@@ -1104,9 +1104,9 @@
 #    getting_started_3: "You'll see student's progress below as they sign up and join your class."
 #    additional_resources: "Additional Resources"
 #    additional_resources_1_pref: "Download/print our"
-#    additional_resources_1_mid: "Course 1"
-#    additional_resources_1_mid2: "and"
-#    additional_resources_1_mid3: "Course 2"
+    additional_resources_1_mid: "Kurs 1"
+    additional_resources_1_mid2: "und"
+    additional_resources_1_mid3: "Kurs 2"
 #    additional_resources_1_suff: "teacher's guides with solutions for each level."
 #    additional_resources_2_pref: "Complete our"
 #    additional_resources_2_suff: "to get two free enrollments for the rest of our paid courses."
@@ -1119,18 +1119,18 @@
 #    educator_wiki_pref: "Or check out our new"
 #    educator_wiki_mid: "educator wiki"
 #    educator_wiki_suff: "to browse the guide online."
-#    your_classes: "Your Classes"
-#    no_classes: "No classes yet!"
-#    create_new_class1: "create new class"
-#    available_courses: "Available Courses"
+    your_classes: "Deine Klassen"
+    no_classes: "Bisher keine Klassen!"
+    create_new_class1: "neue Klassen erstellen"
+    available_courses: "Verfügbare Kurse"
 #    unused_enrollments: "Unused enrollments available:"
 #    students_access: "All students get access to Introduction to Computer Science for free. One enrollment per student is required to assign them to paid CodeCombat courses. A single student does not need multiple enrollments to access all paid courses."
 #    active_courses: "active courses"
 #    no_students: "No students yet!"
 #    add_students1: "add students"
 #    view_edit: "view/edit"
-#    students_enrolled: "students enrolled"
-#    length: "Length:"
+    students_enrolled: "Schüler eingeschrieben"
+    length: "Länge:"

   classes:
     archmage_title: "Erzmagier"


Update pt-PT.coffee

Update pt-PT.coffee

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -1,6 +1,6 @@
 module.exports = nativeDescription: "Português (Portugal)", englishDescription: "Portuguese (Portugal)", translation:
   new_home:
-    slogan: "O jogo mais cativante para aprender programação."
+    slogan: "A forma mais cativante de aprender código real."
     classroom_edition: "Edição de Turma:"
     learn_to_code: "Aprender a programar:"
     play_now: "Jogar Agora"
@@ -22,7 +22,7 @@
     accessible_to: "Acessível a"
     everyone: "todos"
     democratizing: "Democratizar o processo de aprender a programar está na base da nossa filosofia. Todos devem poder aprender a programar."
-    forgot_learning: "Acho que eles se esqueceram que estavam a aprender alguma coisa." # {change}
+    forgot_learning: "Acho que eles se esqueceram que estavam a aprender alguma coisa."
     wanted_to_do: "Programar é algo que sempre quis fazer e nunca pensei que poderia aprender isso na escola."
 #    builds_concepts_up: "I like how CodeCombat builds the concepts up. It's really easy to understand and fun to figure it out."
     why_games: "Porque é que aprender através de jogos é importante?"
@@ -100,7 +100,7 @@
     help_suff: "e nós entraremos em contacto!"
     resource_hub: "Centro de Recursos"
 #    apcsp: "AP CS Principles"
-#    parent: "Parents"
+    parent: "Educadores"

   modal:
     close: "Fechar"
@@ -138,7 +138,7 @@
     poll: "Votações" # Tooltip on poll button from /play
     next: "Próximo" # Go from choose hero to choose inventory before playing a level
     change_hero: "Alterar Herói" # Go back from choose inventory to choose hero
-#    change_hero_or_language: "Change Hero or Language"
+    change_hero_or_language: "Alterar Herói ou Linguagem"
     buy_gems: "Comprar Gemas"
     subscribers_only: "Apenas para Subscritores!"
     subscribe_unlock: "Subscreve-te para Desbloqueares!"
@@ -367,7 +367,7 @@
     rejected: "Rejeitadas"
     withdrawn: "Canceladas"
     accept: "Aceitar"
-#    accept_and_save: "Accept&Save"
+    accept_and_save: "Aceitar&Guardar"
     reject: "Rejeitar"
     withdraw: "Cancelar"
     submitter: "Submissor"
@@ -538,7 +538,7 @@
     tip_toggle_play: "Alterna entre Jogar e Pausar com Ctrl+P."
     tip_scrub_shortcut: "Usa Ctrl+[ para rebobinar e Ctrl+] para avançar."
     tip_guide_exists: "Clica no guia, dentro do menu do jogo (no topo da página), para informações úteis."
-    tip_open_source: "O CodeCombat é 100% open source!" # {change}
+    tip_open_source: "O CodeCombat faz parte da comunidade open source!"
     tip_tell_friends: "Estás a gostar do CodeCombat? Fala de nós aos teus amigos!"
     tip_beta_launch: "O CodeCombat lançou o seu beta em outubro de 2013."
     tip_think_solution: "Pensa na solução, não no problema."
@@ -719,8 +719,8 @@
     purchase: "Adquirir"
     purchased: "Adquirido"

-#  subscribe_for_gems:
-#    prompt_title: "Not Enough Gems!"
+  subscribe_for_gems:
+    prompt_title: "Gemas Insuficientes!"
 #    prompt_body: "Subscribe to Premium to get gems and access to even more levels!"

   earn_gems:
@@ -940,7 +940,7 @@
     cat_title: "Designer de Jogos"
     scott_title: "Co-fundador, Engenheiro de Software"
     maka_title: "Defensor dos Clientes"
-    robin_title: "Gestora de Produto" # {change}
+    robin_title: "Gestora de Produto Sénior"
     nolan_title: "Gestor de Vendas"
     lisa_title: "Gestora de Desenvolvimento de Negócio"
     sean_title: "Gestor de Território"
@@ -949,10 +949,10 @@
     david_title: "Líder de Marketing"
     retrostyle_title: "Ilustração"
     retrostyle_blurb: "'RetroStyle Games'"
-    bryukh_title: "Designer de Jogos" # {change}
+#    bryukh_title: "Gameplay Developer"
     bryukh_blurb: "Constrói puzzles"
-#    daniela_title: "Content Crafter"
-#    daniela_blurb: "Creates stories"
+    daniela_title: "Construtora de Conteúdo"
+    daniela_blurb: "Cria histórias"
     community_title: "...e a nossa comunidade open source"
     community_subtitle: "Mais de 500 contribuidores ajudaram a construir o CodeCombat, com mais a se juntarem todas as semanas!"
     community_description_3: "O CodeCombat é um"
@@ -1570,8 +1570,8 @@
 #    empty_credits: "Exhausted Licenses"
     license_remaining: "licença restante"
     licenses_remaining: "licenças restantes"
-    one_license_used: "1 licença foi usada" # {change}
-    num_licenses_used: "__numLicensesUsed__ licenças foram usadas" # {change}
+    one_license_used: "1 de __totalLicenses__ licenças foi usada"
+    num_licenses_used: "__numLicensesUsed__ de __totalLicenses__ licenças foram usadas"
 #    starter_licenses: "starter licenses"
 #    start_date: "start date:"
 #    end_date: "end date:"
@@ -1938,7 +1938,7 @@

   contribute:
     page_title: "Contribuir"
-    intro_blurb: "O CodeCombat é 100% open source! Centenas de jogadores dedicados ajudaram-nos a transformar o jogo naquilo que ele é hoje. Junta-te a nós e escreve o próximo capítulo da aventura do CodeCombat para ensinar o mundo a programar!" # {change}
+    intro_blurb: "O CodeCombat faz parte da comunidade open source! Centenas de jogadores dedicados ajudaram-nos a transformar o jogo naquilo que ele é hoje. Junta-te a nós e escreve o próximo capítulo da aventura do CodeCombat para ensinar o mundo a programar!"
     alert_account_message_intro: "Hey, tu!"
     alert_account_message: "Para te subscreveres para receber e-mails de classes, necessitarás de iniciar sessão."
     archmage_introduction: "Uma das melhores partes da construção de jogos é que eles sintetizam muitas coisas diferentes. Gráficos, som, rede em tempo real, redes sociais, e, claro, muitos dos aspectos mais comuns da programação, desde a gestão de bases de dados de baixo nível, e administração do servidor até à construção do design e da interface do utilizador. Há muito a fazer, e se és um programador experiente com um verdadeiro desejo de mergulhar nas entranhas do CodeCombat, esta classe pode ser para ti. Gostaríamos muito de ter a tua ajuda para construir o melhor jogo de programação de sempre."
@@ -2352,7 +2352,7 @@

   legal:
     page_title: "Legal"
-#    opensource_introduction: "CodeCombat is part of the open source community."
+    opensource_introduction: "O CodeCombat faz parte da comunidade open source."
     opensource_description_prefix: "Confere "
     github_url: "o nosso GitHub"
     opensource_description_center: "e ajuda se quiseres! O CodeCombat é construído tendo por base dezenas de projetos open source, os quais nós amamos. Vê "
@@ -2375,9 +2375,9 @@
     contributor_description_prefix: "Todas as contribuições, tanto no sítio como no nosso repositório GitHub, estão sujeitas ao nosso"
     cla_url: "CLA"
     contributor_description_suffix: "com o qual deves concordar antes de contribuir."
-    code_title: "Código - MIT" # {change}
+#    code_title: "Client-Side Code - MIT"
 #    client_code_description_prefix: "All client-side code for codecombat.com in the public GitHub repository and in the codecombat.com database, is licensed under the"
-    mit_license_url: "licença do MIT"
+    mit_license_url: "Licença do MIT"
     code_description_suffix: "Isto inclui todo o código dentro dos Sistemas e dos Componentes, o qual é disponibilizado pelo CodeCombat para a criação de níveis."
     art_title: "Arte/Música - Creative Commons "
     art_description_prefix: "Todos os conteúdos comuns estão disponíveis à luz da"


show all projects when admin user mode is active

--- app/pages/projects.cjsx
+++ app/pages/projects.cjsx
@@ -20,8 +20,9 @@

   listProjects: ->
     query =
-      launch_approved: true
       include:'owners,avatar'
+    if !apiClient.params.admin
+      query.launch_approved = true
     Object.assign query, @props.query

     apiClient.type('projects').get query


Only open window when login is needed

Only open window when login is needed

--- backend/ide-init.coffee
+++ backend/ide-init.coffee
@@ -40,6 +40,9 @@
   win = new BrowserWindow({show: false})
   webContents = win.webContents

+  webContents.on 'did-finish-load', ->
+    win.show()
+
   webContents.on 'did-get-redirect-request', (e, oldURL, newURL) ->
     if newURL.match(/ide_token/)
       token = url.parse(newURL, true).query.ide_token
@@ -52,10 +55,7 @@
           atom.commands.dispatch(workspaceView, 'integrated-learn-environment:toggleTerminal')
         else

-  if win.loadUrl('https://learn.co/ide/token')
-    win.show()
-  else
-    promptManualEntry()
+  promptManualEntry() unless win.loadUrl('http://staging.learn.co/ide/token?ide_config=true')

 promptManualEntry = ->
   oauthPrompt = document.createElement 'div'


Handle assignee update failures gracefully

--- app/assets/javascripts/buckets.js.coffee
+++ app/assets/javascripts/buckets.js.coffee
@@ -53,6 +53,9 @@
       data: { prioritized_issue: { assignee: field.val() } }
       success: (html) ->
         issue.replaceWith(html)
+    .fail (data) ->
+      issue.toggleClass("assigning")
+

 makeIssuesStateable = ->
   $(document).on "change", ".js-write-access .js-issue-state-toggle", (e) ->


hand the errorHandler your own error-page options

--- web/handler.coffee
+++ web/handler.coffee
@@ -4,35 +4,35 @@
 util = require 'util'
 errs = require 'errs'

+# An `error-page` handler, because `templar` wasn't working.
+errorTemplate = (req, res, data) ->
+  template = if 400 <= data.code < 500 then "4xx" else "5xx"
+  template = __dirname + "/public/codes/" + template + ".html"
+
+  # Note that eventually all error pages will be html. Prefer HTTP requests.
+  # If we get them with `request` then have them configured through URLs.
+  fs.readFile template, (err, plate) ->
+    if err
+      # Error page not found.
+      data.furtherError = err
+      res.writeHead 500, "Content-Type": "text/html"
+      res.end JSON.stringify data
+    else
+      # Using plates to serve the html.
+      # So that the pages can say more about what went wrong.
+      res.writeHead data.code, "Content-Type": "text/html"
+      res.end plates.bind plate.toString(), data
+
+# The `error-page` module options.
+errorOptions =
+  debug: true
+  "*": errorTemplate
+
 # Pass to `vfs-http-handler` or call directly.
-module.exports = (req, res, err, code) ->
+module.exports = (req, res, err, code, opts = errorOptions) ->
   # Can pass err as a `String`, make it a real `Error`.
   err = errs.create err unless util.isError err
   console.error err.stack || err
-
-  # An `error-page` handler, because `templar` wasn't working.
-  errorTemplate = (req, res, data) ->
-    template = if 400 <= data.code < 500 then "4xx" else "5xx"
-    template = __dirname + "/public/codes/" + template + ".html"
-
-    # Note that eventually all error pages will be html. Prefer HTTP requests.
-    # If we get them with `request` then have them configured through URLs.
-    fs.readFile template, (err, plate) ->
-      if err
-        # Error page not found.
-        data.furtherError = err
-        res.writeHead 500, "Content-Type": "text/html"
-        res.end JSON.stringify data
-      else
-        # Using plates to serve the html.
-        # So that the pages can say more about what went wrong.
-        res.writeHead status, "Content-Type": "text/html"
-        res.end plates.bind plate.toString(), data
-
-  # The `error-page` module options.
-  errorOptions =
-    debug: true
-    "*": errorTemplate

   # The status code and error message.
   message = err.stack.message || err.message || err
@@ -52,6 +52,6 @@
     else status = 500

   # Handle the error.
-  res.error = new ErrorPage req, res, errorOptions
+  res.error = new ErrorPage req, res, opts
   res.error status, message


Add 'L' for layout.

Add 'L' for layout.

--- _includes/coffeescript/nav-keyboard.coffee
+++ _includes/coffeescript/nav-keyboard.coffee
@@ -35,6 +35,9 @@
       when 72
         loadPage "Inspirational advice on Humble Pied.", '/index.html'
         window.history.pushState('all', '', '/')
+      # Shortcut key: L
+      when 76
+        $('.guest-preview').toggleClass 'with-quote-only'

     # Back-button behavior is strange.
     # I’m not sure how to store titles in pushState,


BF-46 Save user settings

BF-46 Save user settings

--- app/assets/javascripts/model/user.coffee
+++ app/assets/javascripts/model/user.coffee
@@ -5,11 +5,13 @@
 # This takes care of the userdate.
 User ?= {}

+# Debounce for POST User.Configuration
+DEBOUNCE_TIME = 3000

 User.Configuration =

 	# userdata
-	# default values are defined by server
+	# default values are defined in server
 	moveValue : null
 	rotateValue : null
 	scaleValue : null
@@ -24,7 +26,7 @@

 	initialize : ->
 		unless @configDeferred
-
+			@push =	_.debounce @pushImpl, DEBOUNCE_TIME
 			@configDeferred = $.Deferred()

 			@configDeferred.fail =>
@@ -57,7 +59,9 @@

 			@configDeferred.promise()

-	push : ->
+	push : null
+
+	pushImpl : ->
 		deferred = $.Deferred()

 		request(
@@ -82,5 +86,5 @@
 		).always(-> deferred.resolve())

 		deferred.promise()
-
+
 User


Remove unnecessary defensive check.

Remove unnecessary defensive check.

--- web/js/main.coffee
+++ web/js/main.coffee
@@ -72,7 +72,7 @@
     new BlankSlateView().render()
     new RoutesView().render()

-    if navigator.geolocation and Reitti.Settings.get('canWatchPosition')
+    if Reitti.Settings.get('canWatchPosition')
       Reitti.Position.startWatching()

     Backbone.history.start(pushState: true)


Change 'click' event to 'mousedown'

Change 'click' event to 'mousedown'

'Click' activates when the mouse button is release, which feels slower
than 'mousedown' which triggers when the mouse button is actually
pressed down.

--- app/assets/javascripts/schemes.js.coffee
+++ app/assets/javascripts/schemes.js.coffee
@@ -26,7 +26,7 @@

     for klass, section of SECTIONS
       do (klass, section) ->
-        $(klass).click (event) ->
+        $(klass).mousedown (event) ->
           event.stopPropagation()

           selectedClass = klass


ensure prior errors get cleared once form is valid

`this.data 'errors', undefined` is equivalent to `this.data 'errors'`,
so it wasn't actually doing anything

test plan: n/a

Change-Id: I572a15014eda20a97a8d6fa87279e8a3590bce74
Reviewed-on: https://gerrit.instructure.com/19521
Reviewed-by: Ryan Florence <2d8900489f91ac272fd9ce9d5789db6522668ca2@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Product-Review: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>
QA-Review: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>

--- app/coffeescripts/jquery/validate.coffee
+++ app/coffeescripts/jquery/validate.coffee
@@ -36,6 +36,6 @@
       this.data 'errors', errors
       false
     else
-      this.data 'errors', undefined
+      this.data 'errors', null
       true


Refactor map view

Refactor map view

--- frontend/coffeescript/app/viz/map_view.coffee
+++ frontend/coffeescript/app/viz/map_view.coffee
@@ -51,7 +51,22 @@
             for field in fields when field.type is 'geopoint'
                 @selected_header or= field

-        _geopoint: ( datum ) =>
+        # Add a geopoint to all of the map layers.
+        _add_to_layers: (datum, geopoint) ->
+
+          # Set up a pop-up so that when the marker is clicked, the fields for
+          # that data point are showed.
+          fields =
+            for key, value of datum.get( 'data' )
+              "
#{key}: #{value}
"
+          marker = L.marker( [geopoint.lat, geopoint.lng], {icon: mapIcon} )
+          marker.bindPopup( fields.join('') )
+
+          @markers.addLayer( marker )
+          @clusters.addLayer( marker )
+          @heatmap.addDataPoint( {'lon': geopoint.lng, 'lat': geopoint.lat, 'value': 1 } )
+
+        _geopoint: ( datum ) ->
             # Ensure there is a column with geopoint data
             return null if not @selected_header?.name?

@@ -76,26 +91,10 @@
             return { lat: lat, lng: lng }

         render: ->
-            last_marker = undefined
-
-            for datum in @collection.models
-                geopoint = @_geopoint( datum )
-
-                if not geopoint?
-                    continue
-
-                marker = L.marker( [geopoint.lat, geopoint.lng], {icon: mapIcon} )
-
-                html = ''
-                for key, value of datum.get( 'data' )#marker.data
-                    html += "
#{key}: #{value}
"
-                marker.bindPopup( html )
-
-                @markers.addLayer( marker )
-                @clusters.addLayer( marker )
-
-                if datum.get( 'data' ).value?
-                    heatmap_value = datum.get( 'data' ).value
+          for datum in @collection.models
+            geopoint = @_geopoint( datum )
+            continue if not geopoint?
+            @_add_to_layers(datum, geopoint)

         _resize_map: () =>
             $( '#map' ).css( { 'height': ( @$el.parent().height() - 20 ) + 'px' } )
@@ -143,7 +142,7 @@
             @map.on( 'moveend', @refresh_viewport )

             # Clear all our markers when we get another set of markers.
-            @collection.on( 'reset', ()=>
+            @collection.on( 'reset', =>
                 @markers.clearLayers()
                 @clusters.clearLayers() )
             @
@@ -161,13 +160,7 @@

             # Instead of appending these views into the DOM, place them in the
             # map instead!
-            marker = L.marker([point.lat,  point.lng], {icon: mapIcon})
-            marker.bindPopup( itemView.el )
-
-            # Add marker to our different layers
-            collectionView.markers.addLayer( marker )
-            collectionView.clusters.addLayer( marker )
-            collectionView.heatmap.addDataPoint( {'lon': point.lng, 'lat': point.lat, 'value': 1 } )
+            @_add_to_layers(itemView.model, point)

         buildItemView: ( item, ItemViewType, itemViewOptions ) ->
             options = _.extend( { model: item, fields: @fields }, itemViewOptions )
@@ -178,45 +171,36 @@
           # invalidateSize() doesn't work.
           @map._onResize()

-        get_next_page: () ->
-
-            @data_offset = @data_offset + 1
-            selfie = @
-            @collection.fetch(
-                reset: false
-                data:
-                    geofield: @selected_header.name
-                    bbox: @bounds.toBBoxString()
-                    offset: @data_offset
-                success: (collection, response, options) ->
-                    if response.meta.pages > selfie.data_offset
-                        selfie.get_next_page()
-            )
-
+        get_next_page: ->
+          @data_offset = @data_offset + 1
+
+          @collection.fetch
+            reset: false
+            data:
+              geofield: @selected_header.name
+              bbox: @bounds.toBBoxString()
+              offset: @data_offset
+            success: (collection, response, options) =>
+              @get_next_page() if response.meta.pages > @data_offset
+
+        # Everytime we move around, grab new data from the server and
+        # refresh the viewport!
         refresh_viewport: ( event ) =>
-            # If we could not find any geofields or the user has not
-            # specified an existing one, don't do anything with the
-            # data.
-            if not @selected_header?
-                return
-
-            @data_offset = 0
-
-            # Everytime we move around, grab new data from the server and
-            # refresh the viewport!
-            @bounds = event.target.getBounds()
-
-            selfie = @
-
-            @collection.fetch(
-                reset: true
-                data:
-                    geofield: @selected_header.name
-                    bbox: @bounds.toBBoxString()
-                success: (collection, response, options) ->
-                    if response.meta.pages > 0
-                        selfie.get_next_page()
-            )
+          # If we could not find any geofields or the user has not
+          # specified an existing one, don't do anything with the
+          # data.
+          return if not @selected_header?
+
+          @data_offset = 0
+          @bounds = event.target.getBounds()
+
+          @collection.fetch
+            reset: true
+            data:
+              geofield: @selected_header.name
+              bbox: @bounds.toBBoxString()
+            success: (collection, response, options) =>
+              @get_next_page() if response.meta.pages > 0

     return DataMapView
 )


Extract function to ensure selection doesn't unintentionally change

--- app/js/jail_iframe/util/highlight.coffee
+++ app/js/jail_iframe/util/highlight.coffee
@@ -7,6 +7,25 @@
   finally
     # Scroll back to previous location
     window.scroll scrollLeft, scrollTop
+
+ensureSelectionSaved = (func) ->
+  oldRanges = []
+  try
+    # If the user currently has selected some text, store the selection
+    selection = window.document.getSelection()
+    oldRanges =
+      for i in [0...selection.rangeCount]
+        selection.getRangeAt(i)
+    func()
+  finally
+    # Reset the selection
+    window.document.getSelection().removeAllRanges()
+
+    # Restore previous selection
+    for selectedRange in oldRanges
+      window.document.getSelection().addRange selectedRange
+
+
 # Chrome, Firefox, Safari
 searchWithWindowFind = (searchString) ->
   # Trim


I've converted all ajax requests into node file API requests.

--- browser/frontend.coffee
+++ browser/frontend.coffee
@@ -12,11 +12,13 @@
 class_cache = {}
 raw_cache = {}

-$.ajax "browser/mini-rt.tar", {
-  type: 'GET'
-  dataType: 'text'
-  beforeSend: (jqXHR) -> jqXHR.overrideMimeType('text/plain; charset=x-user-defined')
-  success: (data) ->
+preload = ->
+  try
+    data = node.fs.readFileSync("/home/doppio/browser/mini-rt.tar")
+  catch e
+    console.error e
+
+  if data?
     file_count = 0
     done = false
     start_untar = (new Date).getTime()
@@ -55,16 +57,15 @@
       ->
         done = true
         on_complete() if file_count == 0
-  error: (jqXHR, textStatus, errorThrown) ->
-    console.error errorThrown
-}
-
-$.ajax "vendor/classes/com/sun/tools/script/shell/Main.class", {
-  type: 'GET'
-  dataType: 'text'
-  beforeSend: (jqXHR) -> jqXHR.overrideMimeType('text/plain; charset=x-user-defined')
-  success: (data) -> class_cache['!rhino'] = process_bytecode data
-}
+
+fetch_rhino = ->
+  try
+    data = node.fs.readFileSync("/home/doppio/vendor/classes/com/sun/tools/script/shell/Main.class")
+  catch e
+    console.error e
+
+  if data?
+    class_cache['!rhino'] = process_bytecode data

 try_path = (path) ->
   try
@@ -207,6 +208,8 @@
     e.preventDefault()

   $('#close_btn').click (e) -> close_editor(); e.preventDefault()
+  preload()
+  fetch_rhino()

 commands =
   javac: (args, cb) ->


Further loader simplifications

Further loader simplifications

--- js-library/app/js/loader/loader_common.coffee
+++ js-library/app/js/loader/loader_common.coffee
@@ -1,10 +1,9 @@
-mkEl = (name) -> (id, domContent) ->
-  wrapper = document.createElement(name)
-  id && wrapper.id = id
-  wrapper.appendChild(domContent)
-  wrapper
+mkEl = (name, id, content) ->
+  el = document.createElement(name)
+  id && el.id = id
+  content && el.appendChild(content)
+  el

-mkDiv = mkEl 'div'

 window.FACTLINK_START_LOADER = ->
   if window.FACTLINK_LOADED
@@ -12,22 +11,21 @@
     return
   window.FACTLINK_LOADED = true

+  # Add styles
+  style_tag = (mkEl 'style') null, document.createTextNode(style_code)
+  document.getElementsByTagName('head')[0].appendChild style_tag
+
   #### Create iframe so jslib's namespace (window) doesn't collide with any content window.
-  jslib_jail_iframe = document.createElement('iframe')
+  jslib_jail_iframe = (mkEl 'iframe') 'factlink-iframe'
   jslib_jail_iframe.style.display = 'block'
   jslib_jail_iframe.style.border = '0px solid transparent'
-  jslib_jail_iframe.id = 'factlink-iframe'

   # Wrappers for increased CSS specificity
   outerWrapperEl =
-    mkDiv 'fl-wrapper-1',
-      mkDiv 'fl-wrapper-2',
-        mkDiv 'fl-wrapper-3',
-          mkDiv 'fl', jslib_jail_iframe
-
-  # Keep in sync with #fl-wrapper-1 in basic.less
-  outerWrapperEl.style.visibility = 'hidden'; # Prevent showing stuff when css is not loaded yet
-  outerWrapperEl.style.position = 'absolute'; # Prevent reflowing of the page
+    mkEl 'div', 'fl-wrapper-1',
+      mkEl 'div', 'fl-wrapper-2',
+        mkEl 'div', 'fl-wrapper-3',
+          mkEl 'div', 'fl', jslib_jail_iframe

   document.body.appendChild outerWrapperEl

@@ -40,10 +38,9 @@
     window.FACTLINK[name] = ->
       if jslib_jail_iframe.contentWindow.Factlink?
         jslib_jail_iframe.contentWindow.Factlink[name](arguments...)
-        return # don't return the value, as we also don't do that when storing calls
       else
         storedMethodCalls.push {name: name, arguments: arguments}
-        return
+      return # don't return the value, as we can't do that when storing calls

   proxy_method 'on'
   proxy_method 'triggerClick'
@@ -59,12 +56,13 @@

   window.FACTLINK_ON_CORE_LOAD = ->
     # This seems to be necessary, don't understand entirely why
-    window.FACTLINK.easyXDM = jslib_jail_iframe.contentWindow.Factlink.easyXDM
+#    window.FACTLINK.easyXDM = jslib_jail_iframe.contentWindow.Factlink.easyXDM

     for methodCall in storedMethodCalls
       jslib_jail_iframe.contentWindow.Factlink[methodCall.name](methodCall.arguments...)
     storedMethodCalls = []

+  jslib_jail_iframe.contentWindow.FactlinkConfig = window.FactlinkConfig
   #### Load iframe with script tag
   jslib_jail_doc = jslib_jail_iframe.contentWindow.document

@@ -74,16 +72,10 @@

   script_tag = jslib_jail_doc.createElement 'script'
   script_tag.appendChild(
-    jslib_jail_doc.createTextNode("""window.FactlinkConfig = #{JSON.stringify(window.FactlinkConfig)};
-
-#{jslib_jail_code};
-
-window.parent.FACTLINK_ON_CORE_LOAD();
-"""))
+    jslib_jail_doc.createTextNode(
+      "#{jslib_jail_code};window.parent.FACTLINK_ON_CORE_LOAD();"))
   jslib_jail_doc.documentElement.appendChild(script_tag)

-  style_tag = (mkEl 'style') null, document.createTextNode(style_code)
-  document.getElementsByTagName('head')[0].appendChild style_tag

 jslib_jail_code = __INLINE_JS_PLACEHOLDER__
 style_code = __INLINE_CSS_PLACEHOLDER__


js code for analytics

js code for analytics

--- app/assets/javascripts/analytics.js.coffee
+++ app/assets/javascripts/analytics.js.coffee
@@ -0,0 +1,5 @@
+root = global ? window
+
+class root.MongodbLoggerAnalytics
+  constructor: ->
+    # comming soon


set @stepsNeedUpdate when updating contents & let stepShownCallback actually call updateSteps()

This makes steps that use setContents to load server-rendered HTML work more consistently and get their button click event handlers properly setup.

--- app/assets/javascripts/hat-trick.js.coffee
+++ app/assets/javascripts/hat-trick.js.coffee
@@ -10,6 +10,8 @@
   linkClass: "_ht_link"

   buttons: {}
+
+  stepsNeedUpdate: false

   stepShownCallback: ->
     if not @formwizardEnabled
@@ -293,7 +295,7 @@
         $step = $("fieldset##{stepId}")
         $step.html fieldsetContents
         this.addDefaultButtons($step)
-        this.updateSteps()
+        @stepsNeedUpdate = true

   handleServerData: (data) ->
     if data.metadata?
@@ -331,7 +333,6 @@
       if currentStep.repeatOf?
         this.repeatStep(currentStep)
       else
-        # log "Showing step #{currentStep.fieldset}"
         this.setLinkField(currentStep.fieldset) unless this.LinkFieldSet()

   bindEvents: ->


bug in next

bug in next

--- app/assets/javascripts/workflows.js.coffee
+++ app/assets/javascripts/workflows.js.coffee
@@ -24,7 +24,7 @@
   # Hover the next-level image
   $('#next-level-thumb-canvas').live('click', ->
     button = $('#levels .is-selected')
-    next_button = button.next()
+    next_button = button.next('li')
     button.removeClass('is-selected')
     next_button.addClass('is-selected')


Ensure github auth is configured at boot

--- app/coffee/AuthenticationController.coffee
+++ app/coffee/AuthenticationController.coffee
@@ -3,12 +3,14 @@
 logger = require "logger-sharelatex"
 mountPoint = settings.internal.github_latex_ci.mountPoint

+auth = github.auth.config({
+	id:     settings.github.client_id,
+	secret: settings.github.client_secret
+})
+
 module.exports =
 	login: (req, res, next = (error) ->) ->
-		auth_url = github.auth.config({
-			id:     settings.github.client_id,
-			secret: settings.github.client_secret
-		}).login(['user:email', 'read:org', 'repo:status', 'admin:repo_hook'])
+		auth_url = auth.login(['user:email', 'read:org', 'repo:status', 'admin:repo_hook'])
 		req.session ||= {}
 		req.session.state = auth_url.match(/&state=([0-9a-z]{32})/i)[1];
 		logger.log state: req.session.state, url: auth_url, "redirecting to github login page"
@@ -21,7 +23,7 @@
 		else
 			code = req.query.code
 			logger.log code: code, "getting access_token from github"
-			github.auth.login code, (error, token) ->
+			auth.login code, (error, token) ->
 				return next(error) if error?
 				req.session.token = token
 				res.redirect "#{mountPoint}/repos"


Edit bushpig text

Edit bushpig text

Removed parentheses from scientific name, minor text edits throughout.

--- app/lib/field-guide-content/bushpig.coffee
+++ app/lib/field-guide-content/bushpig.coffee
@@ -1,9 +1,9 @@
 module.exports =
   description: '''
-    Bushpigs resemble the domestic pig, with blunt, muscular snouts, small eyes, and pointed, tufted ears. They are reddish-brown to dark brown in color and their fur becomes darker with age. They have a silver-colored mane, which bristles when they become agitated. Their sharp tusks are fairly short and inconspicuous. Males are normally larger than females.
+    Bushpigs resemble the domestic pig, with a blunt, muscular snout, small eyes, and pointed, tufted ears. Their fur is reddish-brown to dark brown and becomes darker with age. They have a silver-colored mane, which bristles when they become agitated. Their sharp tusks are fairly short and inconspicuous. Males are normally larger than females.
   '''

-  scientificName: '(Potamochoerus larvatus)'
+  scientificName: 'Potamochoerus larvatus'
   mainImage: 'assets/fieldguide-content/mammals/bushpig/bushpig-feature.jpg'

   conservationStatus: 'Least Concern' # Options are Least Concern, Near Threatened, Vulnerable, and Endangered; all have their own little icons as well.
@@ -30,22 +30,22 @@

   sections: [{
     title: 'Habitat'
-    content: '
Savanna, woodland, forests, and riverine vegetation.
'
+    content: '
Savanna, woodland, forests, and riverine vegetation
'
   }, {
     title: 'Diet'
-    content: 'They are omnivorous and their diet includes roots, rhizomes, bulbs, tubers, fruits, insect larvae, crops and carrion.'
+    content: 'Roots, rhizomes, bulbs, tubers, fruits, insect larvae, crops, and carrion'
   }, {
     title: 'Predators'
-    content: 'Lions, leopards, hyenas, pythons, and humans.'
+    content: 'Lions, leopards, hyenas, pythons, and humans'
   }, {
     title: 'Behavior'
     content: '''
-
Bushpigs are social animals that are found in groups of up to 12 members, consisting of a dominant male and female, with other females and juveniles. Bushpigs are highly territorial, and can be aggressive, especially when young are present. They are predominantly nocturnal. They seek shelter in dense vegetation and build nests during rainy and cold periods. In the heat, they wallow in the mud.

+
Bushpigs are social animals that are found in groups of up to 12 members, consisting of a dominant male and female, with other females and juveniles. Bushpigs are highly territorial and can be aggressive, especially when young are present. They are predominantly nocturnal. They seek shelter in dense vegetation and build nests during rainy and cold periods. In the heat, they wallow in the mud.

     '''
   }, {
     title: 'Breeding'
     content: '''
-      A bushpig reaches sexual maturity at 18 to 21 months. Bushpig males exclude other males for access to a group of females. The males compete by butting heads. Most births occur between September and November. The female bushpig goes to their sheltered nest before giving birth. After birth, they nurse their young for 2 to 4 months. Parents usually drive out young bushpigs at 6 months of age.
+      A bushpig reaches sexual maturity at 18 to 21 months. Bushpig males exclude other males for access to a group of females. The males compete by butting heads. Most births occur between September and November. The female bushpig goes to their sheltered nest before giving birth. After birth, they nurse their young for two to four months. Parents usually drive out young bushpigs at six months of age.
     '''
   }, {
     title: 'Fun Facts'


client/app: remove DomainItem

client/app: remove DomainItem

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/domains/domainitem.coffee
+++ client/app/lib/domains/domainitem.coffee
@@ -1,59 +0,0 @@
-kd               = require 'kd'
-nick             = require '../util/nick'
-globals          = require 'globals'
-KodingSwitch     = require '../commonviews/kodingswitch'
-KDLoaderView     = kd.LoaderView
-KDListItemView   = kd.ListItemView
-CustomLinkView   = require '../customlinkview'
-KDCustomHTMLView = kd.CustomHTMLView
-
-
-module.exports = class DomainItem extends KDListItemView
-
-  constructor: (options = {}, data) ->
-
-    options.type = 'domain'
-
-    super options, data
-
-
-  viewAppended: ->
-
-    { domain, machineId } = @getData()
-    currentMachineId      = @getOption 'machineId'
-
-    topDomain = "#{nick()}.#{globals.config.userSitesDomain}"
-
-    @addSubView new CustomLinkView
-      title  : domain
-      href   : "http://#{domain}"
-      target : '_blank'
-
-    unless domain is topDomain
-      @addSubView new KDCustomHTMLView
-        tagName  : 'span'
-        cssClass : 'remove'
-        click    : =>
-          @getDelegate().emit 'DeleteDomainRequested', this
-
-    @addSubView @stateToggle = new KodingSwitch
-      cssClass     : 'tiny'
-      defaultValue : machineId is currentMachineId
-      callback     : (state) =>
-        @getDelegate().emit 'DomainStateChanged', this, state
-
-    @addSubView @loader = new KDLoaderView
-      size     : { width : 16 }
-      cssClass : 'hidden'
-
-
-  setLoadingMode: (disable) ->
-
-    if disable
-      @setClass 'in-progress'
-      @loader.show()
-      @stateToggle.setOption 'disabled', yes
-    else
-      @unsetClass 'in-progress'
-      @loader.hide()
-      @stateToggle.setOption 'disabled', no


Ensure start recording button states are correct (disabled if user isn't valid).

--- client/room/room.coffee
+++ client/room/room.coffee
@@ -162,4 +162,4 @@
   error: ->
     Session.get 'recError'
   disabled: ->
-    'disabled' if Session.get('recWaiting') or not Session.get 'recTitle'
+    'disabled' if Session.get('recWaiting') or Session.get('recError') or not Session.get 'recTitle'


reset form after comment

reset form after comment

--- app/views/ershou/comments/create.js.coffee
+++ app/views/ershou/comments/create.js.coffee
@@ -1,2 +1,3 @@
 $(".comments").append("<%= escape_javascript(render(@comment)) %>")
-$("#new_comment").each -> this.reset()+$("#new_comment").each -> this.reset()
+$('#new_comment .nav-tabs a[href="#content"]').tab('show');


model: remove excessive new lines in comments

--- coffee/src/model.coffee
+++ coffee/src/model.coffee
@@ -16,7 +16,6 @@
   #    m.addProperty 'title', 'Untitled'
   #    m.addProperty 'description', undefined
   #    m.addProperty 'age', 0
-  #
   properties: => {}

   initialize: =>
@@ -31,11 +30,9 @@
   #    m.addProperty 'age', 0
   #
   # After the above:
-  #
   #    m.title()              is equivalent to         m.get 'title'
   #
   # And,
-  #
   #    m.title 'Title'        is equivalent to         m.set 'title', 'Title'
   #
   # And age has an initial value of 0


read all pods from wolfram

read all pods from wolfram

--- commands/wolfram.coffee
+++ commands/wolfram.coffee
@@ -47,8 +47,9 @@
                     subpods = pod.children.filter (node) -> node.name is 'subpod'
                     for subpod in subpods
                         [text] = subpod.children.filter (node) -> node.name is 'plaintext'
-                        message += "\n#{[text.firstchild().text]}"
+                        message += "\n#{[text.firstChild().text]}"
             catch e
+                console.log e
                 message += ''

         respond message


Drop support for legacy bracketed actions

--- components/Store.coffee
+++ components/Store.coffee
@@ -22,19 +22,13 @@
       output.done()
       return
     return unless input.hasStream 'action'
-    packets = []
-    brackets = []
-    input.getStream('action').forEach (ip) ->
-      if ip.type is 'openBracket'
-        brackets.push ip.data
-      if ip.type is 'closeBracket'
-        brackets.pop()
-      if ip.type is 'data'
-        packets.push
-          data: ip.data
-          brackets: brackets.slice 0
-    for packet in packets
-      data = packet.data
+    packets = input.getStream('action').filter((ip) ->
+      ip.type is 'data'
+    ).map (ip) -> ip.data
+    for data in packets
+      unless data.action
+        console.error 'Received action without expected payload', data
+        output.done()
       if data and typeof data is 'object' and data.payload and data.action
         # New-style action object
         if data.state
@@ -48,13 +42,5 @@
             state: c.state
             payload: data.payload
         continue
-      # Old-style action with only payload, and action defined by brackets
-      action = packet.brackets.join ':'
-      debug "#{action} was sent in legacy payload-only format"
-      output.send
-        pass:
-          action: action
-          state: c.state
-          payload: data
     output.done()
     return


Catch non-protype functions

Catch non-protype functions

--- build/tasks/output-for-loop-returns.coffee
+++ build/tasks/output-for-loop-returns.coffee
@@ -12,7 +12,7 @@
       js = grunt.file.read(jsPath)
       method = null
       for line, index in js.split('\n')
-        [match, className, methodName] = /^\s*([a-zA-Z]+)\.prototype\.([a-zA-Z]+)\s*=\s*function\(/.exec(line) ? []
+        [match, className, methodName] = /^\s*([a-zA-Z]+)\.(?:prototype\.)?([a-zA-Z]+)\s*=\s*function\(/.exec(line) ? []
         if className and methodName
           method = "#{className}::#{methodName}"
         else


require setup sharify file

require setup sharify file

--- desktop/components/json_page/seed.coffee
+++ desktop/components/json_page/seed.coffee
@@ -1,6 +1,7 @@
 _ = require 'underscore'
 inquirer = require 'inquirer'
 JSONPage = require './index'
+require('../../lib/setup_sharify')
 sd = require('sharify').data

 [name, environment, path] = process.argv.slice 2


Account for DQ in rating and solvedByWeek

--- server/calculations/calculateRatingEtc.coffee
+++ server/calculations/calculateRatingEtc.coffee
@@ -30,7 +30,6 @@
 findProblemLevel = (problemId) ->
     problem = await Problem.findById(problemId)
     problem?.level
-

 timeScore = (date) ->
     weeks = (new Date() - date)/MSEC_IN_WEEK
@@ -69,6 +68,12 @@
             if !weekOk[week]
                 weekOk[week] = 0
             weekOk[week]++
+        else if s.outcome == "DQ"
+            if !weekSolved[week]
+                weekSolved[week] = 0
+            weekSolved[week] -= 2
+            rating -= 2 * levelScore(level)
+            activity -= 2 * activityScore(level, submitDate)
     for level in ["1А", "1Б"]
         if (!user.baseLevel) or (level >= user.baseLevel)
             break


Revert "Handling dpr a bit better"

Revert "Handling dpr a bit better"

This reverts commit b253d20dd9468ae9cbe32a2ffecc1ad3ec28b0c2.

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -136,7 +136,6 @@

 		contentScaleFactor = @contentScale
 		contentScaleFactor = 1 if contentScaleFactor > 1
-		pixelRatio = @_device.devicePixelRatio ? 1
 		screenSizeChanged = false
 		if @_shouldRenderFullScreen()

@@ -152,11 +151,8 @@
 			@content.scale = contentScaleFactor
 			if @deviceType isnt "fullscreen" or Utils.isMobile()
 				screenSizeChanged = screenSizeChanged or @_context.devicePixelRatio isnt window.devicePixelRatio
-				if window.devicePixelRatio is pixelRatio and Utils.isDesktop()
-					@_context.renderUsingNativePixelRatio = true
-					@content.scale = pixelRatio
-				else
-					@_context.renderUsingNativePixelRatio = false
+				@_context.devicePixelRatio = window.devicePixelRatio
+
 		else
 			backgroundOverlap = 100

@@ -184,16 +180,13 @@

 			@setHand(@selectedHand) if @selectedHand and @_orientation is 0

+			pixelRatio = @_device.devicePixelRatio ? 1
 			screenSizeChanged = screenSizeChanged or @_context.devicePixelRatio isnt pixelRatio
 			@_context.devicePixelRatio = pixelRatio
 			if window.devicePixelRatio is pixelRatio and Utils.isDesktop()
 				# On desktop rendering natively without scaling looks better, so do that
 				@_context.renderUsingNativePixelRatio = true
 				@content.scale = pixelRatio
-			else
-				@_context.renderUsingNativePixelRatio = false
-				@content.scale = 1
-
 		if screenSizeChanged
 			Screen.emit("resize")


Fixed device size calculations when rendering fullscreen

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -147,13 +147,22 @@
 		contentScaleFactor = 1 if contentScaleFactor > 1
 		screenSizeChanged = false
 		if @_shouldRenderFullScreen()
+			clientWidth = document.documentElement.clientWidth
+			clientHeight = document.documentElement.clientHeight
 			if Utils.isMobile()
-				# This uses the actual screen size, which only make sense if the prototype takes up the whole screen of the device
+				# This uses the actual screen size, which only make sense if the prototype
+				# takes up the whole screen of the device
 				width = screen.width * window.devicePixelRatio
 				height = screen.height * window.devicePixelRatio
+				# If the width and height don't match up with the client width and height
+				# the device is rotated, so flip them
+				if clientWidth < clientHeight and width > height or clientWidth > clientHeight and width < height
+					newHeight = width
+					width = height
+					height = newHeight
 			else
-				width = document.documentElement.clientWidth / contentScaleFactor
-				height = document.documentElement.clientHeight / contentScaleFactor
+				width = clientWidth / contentScaleFactor
+				height = clientHeight / contentScaleFactor
 			screenSizeChanged = @content.width isnt width or @content.height isnt height
 			for layer in [@background, @hands, @phone, @viewport, @content, @screen, @screenMask]
 				layer.x = layer.y = 0


Keep linter happy

Keep linter happy

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -307,7 +307,7 @@
 		if /PhantomJS/.test(navigator.userAgent)
 			return

-		if @_shouldRenderFullScreen() || not @showBezel
+		if @_shouldRenderFullScreen() or not @showBezel
 			@phone.image  = ""
 			@hands.image  = ""
 		else if not @_deviceImageUrl(@_deviceImageName())


fix Screen backgroundColor reset on orientation change

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -135,8 +135,6 @@
 			@content.scale = contentScaleFactor
 			@_positionKeyboard()

-			@screen.backgroundColor = "white"
-
 		else
 			backgroundOverlap = 100

@@ -161,8 +159,6 @@
 			@screen.center()

 			@setHand(@selectedHand) if @selectedHand && @_orientation == 0
-
-			@screen.backgroundColor = "black"

 	_shouldRenderFullScreen: ->

@@ -253,6 +249,11 @@

 			# If we switch from fullscreen to a device, we should zoom to fit
 			shouldZoomToFit = @_deviceType is "fullscreen"
+
+			if deviceType is "fullscreen"
+				@screen.backgroundColor = "white"
+			else
+				@screen.backgroundColor = "black"

 			@_device = _.clone(device)
 			@_deviceType = deviceType


only allow hand toggling when in portrait

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -598,6 +598,7 @@
 		return @_device.hands != undefined

 	nextHand: ->
+		return if @orientation != 0
 		if @handSwitchingSupported()
 			hands = _.keys(@_device.hands)
 			if hands.length > 0


Make ScrollComponent.wrap a class method, so it also works for Page

--- framer/Components/ScrollComponent.coffee
+++ framer/Components/ScrollComponent.coffee
@@ -344,24 +344,25 @@
 		@_ignoreNextScrollEvent = false

-exports.ScrollComponent.wrap = (layer) ->
-
-	scroll = new exports.ScrollComponent
-		backgroundColor: null
-	scroll.frame = layer.frame
-
-	layerIndex = layer.index
-	scroll.superLayer = layer.superLayer
-	scroll.index = layerIndex
-
-	# Correct the position for the scroll content
-
-	contentFrame = layer.contentFrame()
-	layer.width =  Math.max(layer.contentFrame().width,  layer.width) + contentFrame.x
-	layer.height = Math.max(layer.contentFrame().height, layer.height) + contentFrame.y
-	layer.x = 0
-	layer.y = 0
-
-	scroll.content.addSubLayer(layer)
-
-	return scroll
+	@wrap = (layer) ->
+		# This function wraps the given layer into a scroll or page component
+
+		scroll = new @
+			backgroundColor: null
+		scroll.frame = layer.frame
+
+		layerIndex = layer.index
+		scroll.superLayer = layer.superLayer
+		scroll.index = layerIndex
+
+		# Correct the position for the scroll content
+
+		contentFrame = layer.contentFrame()
+		layer.width =  Math.max(layer.contentFrame().width,  layer.width) + contentFrame.x
+		layer.height = Math.max(layer.contentFrame().height, layer.height) + contentFrame.y
+		layer.x = 0
+		layer.y = 0
+
+		scroll.content.addSubLayer(layer)
+
+		return scroll


Fix expectation about collection keys being strings on arrays.

Recent upgrade of dependencies (possibly unit test library) made
the test fail. Probably some `==` moves to `===`. New expectation
seem reasonable anyway.

--- specs/unit/utils/collections/keys_spec.coffee
+++ specs/unit/utils/collections/keys_spec.coffee
@@ -35,7 +35,7 @@

     it 'returns all the indices of the array', ->
       obj      = ['a', 'b', null]
-      expected = ['0', '1', '2']
+      expected = [0, 1, 2]

       res = subject obj
       should(res).eql(expected)
@@ -48,7 +48,7 @@

     it 'returns all the characters positions of the string', ->
       str      = "foo"
-      expected = ['0', '1', '2']
+      expected = [0, 1, 2]

       res = subject str
       should(res).eql(expected)


be more descriptive

be more descriptive

--- src/app/common/resources/contributions.coffee
+++ src/app/common/resources/contributions.coffee
@@ -3,8 +3,8 @@
 angular.module('cobudget').service 'Contribution', (Restangular) ->

   #get details of contribution
-  get: (id) ->
-    Restangular.one('contributions', id).get()
+  get: (contribution_id) ->
+    Restangular.one('contributions', contribution_id).get()

   create: (contribution) ->
     Restangular.all('contributions').post(contribution)


:bug: Fix error raised when typing in a mini editor

--- lib/minimap-highlight-selected-view.coffee
+++ lib/minimap-highlight-selected-view.coffee
@@ -11,7 +11,7 @@

     getActiveMinimap: -> @minimap.getActiveMinimap()

-    getActiveTextEditor: -> @getActiveMinimap().getTextEditor()
+    getActiveTextEditor: -> @getActiveMinimap()?.getTextEditor()

     ['markBufferRange', 'scanInBufferRange', 'getEofBufferPosition', 'getSelections', 'getLastSelection', 'bufferRangeForBufferRow', 'getTextInBufferRange'].forEach (key) ->
       FakeEditor::[key] = -> @getActiveTextEditor()[key](arguments...)
@@ -25,3 +25,7 @@
       @fakeEditor = new FakeEditor(minimap)

     getActiveEditor: -> @fakeEditor
+
+    handleSelection: ->
+      return unless atom.workspace.getActiveTextEditor()?
+      super


Clean up

Clean up

- Major cleanup of apply options
- Keep track of proxied properties
- Allow properties to have depends
- Keep order for properties

--- framer/BaseClass.coffee
+++ framer/BaseClass.coffee
@@ -7,6 +7,7 @@
 CounterKey = "_ObjectCounter"
 DefinedPropertiesKey = "_DefinedPropertiesKey"
 DefinedPropertiesValuesKey = "_DefinedPropertiesValuesKey"
+DefinedPropertiesOrderKey = "_DefinedPropertiesOrderKey"

 capitalizeFirstLetter = (string) ->
 	string.charAt(0).toUpperCase() + string.slice(1)
@@ -18,26 +19,9 @@

 	@define = (propertyName, descriptor) ->

-		for i in ["enumerable", "exportable", "importable"]
-			if descriptor.hasOwnProperty(i)
-				throw Error("woops #{propertyName} #{descriptor[i]}") if not _.isBoolean(descriptor[i])
-
 		# See if we need to add this property to the internal properties class
 		if @ isnt BaseClass
-			descriptor.propertyName = propertyName
-
-			# Have the following flags set to true when undefined:
-			descriptor.enumerable ?= true
-			descriptor.exportable ?= true
-			descriptor.importable ?= true
-
-			# Toggle importable to false when there's no setter defined:
-			descriptor.importable = descriptor.importable and descriptor.set
-
-			# Only retain options that are importable, exportable or both:
-			if descriptor.exportable or descriptor.importable
-				@[DefinedPropertiesKey] ?= {}
-				@[DefinedPropertiesKey][propertyName] = descriptor
+			@_addDescriptor(propertyName, descriptor)

 		# Set the getter/setter as setProperty on this object so we can access and override it easily
 		getName = "get#{capitalizeFirstLetter(propertyName)}"
@@ -49,8 +33,51 @@
 			@::[setName] = descriptor.set
 			descriptor.set = @::[setName]

+		# Better readonly errors for debugging
+
+		# else
+		# 	descriptor.set = (value) ->
+		# 		throw Error("#{@constructor.name}.#{propertyName} is readonly")
+
 		# Define the property
 		Object.defineProperty(@prototype, propertyName, descriptor)
+
+	@_addDescriptor: (propertyName, descriptor) ->
+
+		# for key in ["enumerable", "exportable", "importable"]
+		# 	if descriptor.hasOwnProperty(key)
+		# 		throw Error("woops #{propertyName} #{descriptor[key]}") if not _.isBoolean(descriptor[key])
+
+		descriptor.propertyName = propertyName
+
+		# Have the following flags set to true when undefined:
+		descriptor.enumerable ?= true
+		descriptor.exportable ?= true
+		descriptor.importable ?= true
+
+		# We assume we don't import if there is no setter, because we can't
+		descriptor.importable = descriptor.importable and descriptor.set
+		# We also assume we don't export if there is no setter, because
+		# it is likely a calculated property, and we can't set it.
+		descriptor.exportable = descriptor.exportable and descriptor.set
+
+		# We assume that every property with an underscore is private
+		return if _.startsWith(propertyName, "_")
+
+		# Only retain options that are importable, exportable or both:
+		if descriptor.exportable or descriptor.importable
+			@[DefinedPropertiesKey] ?= {}
+			@[DefinedPropertiesKey][propertyName] = descriptor
+
+			# Set the order, insert it's dependants before, we'll check if they exist later
+			@[DefinedPropertiesOrderKey] ?= []
+
+			if descriptor.depends
+				for depend in descriptor.depends
+					if depend not in @[DefinedPropertiesOrderKey]
+						@[DefinedPropertiesOrderKey].push(depend)
+
+			@[DefinedPropertiesOrderKey].push(propertyName)

 	@simpleProperty = (name, fallback, options={}) ->
 		return _.extend options,
@@ -59,16 +86,20 @@
 			set: (value) -> @_setPropertyValue(name, value)

 	@proxyProperty = (keyPath, options={}) ->
+
 		# Allows to easily proxy properties from an instance object
 		# Object property is in the form of "object.property"
+
 		objectKey = keyPath.split(".")[0]
-		return _.extend options,
+
+		descriptor = _.extend options,
 			get: ->
 				return unless _.isObject(@[objectKey])
 				Utils.getValueForKeyPath(@, keyPath)
 			set: (value) ->
 				return unless _.isObject(@[objectKey])
 				Utils.setValueForKeyPath(@, keyPath, value)
+			proxy: true

 	_setPropertyValue: (k, v) =>
 		@[DefinedPropertiesValuesKey][k] = v
@@ -125,24 +156,51 @@
 		# Create a holder for the property values
 		@[DefinedPropertiesValuesKey] = {}

+		@_applyDefaults(options)
+
 		# Count the creation for these objects and set the id
 		@constructor[CounterKey] ?= 0
 		@constructor[CounterKey] += 1

+		# We set this last so if we print a layer during construction
+		# we don't get confused because the id changes from global to context
 		@_id = @constructor[CounterKey]

-		@_applyOptionsAndDefaults(options)
-
-	_applyOptionsAndDefaults: (options) ->
-		for key, descriptor of @_propertyList()
-			# For each known property (registered with @define) that has a setter, fetch
-			# the value from the options object, unless the prop is not importable.
-			# When there's no user value, apply the default value:
-			if descriptor.set
-				value = Utils.valueOrDefault(
-					(options?[key] if descriptor.importable),
-					@_getPropertyDefaultValue(key)
-				)
-
-				if not (value in [null, undefined])
-					@[key] = value
+	_applyDefaults: (options) ->
+
+		return unless @constructor[DefinedPropertiesOrderKey]
+		return unless options
+
+		for k in @constructor[DefinedPropertiesOrderKey]
+			@_applyDefault(k, options[k])
+
+	_applyProxyDefaults: (options) ->
+
+		return unless @constructor[DefinedPropertiesOrderKey]
+		return unless options
+
+		for k in @constructor[DefinedPropertiesOrderKey]
+			descriptor = @constructor[DefinedPropertiesKey][k]
+			continue unless descriptor?.proxy? is true
+			@_applyDefault(k, options[k])
+
+	_applyDefault: (key, optionValue) ->
+
+		descriptor = @constructor[DefinedPropertiesKey][key]
+
+		# If this was listed as a dependent property, but it did not get defined, we err.
+		throw Error("Missing dependant descriptor: #{key}") unless descriptor
+
+		# For each known property (registered with @define) that has a setter, fetch
+		# the value from the options object, unless the prop is not importable.
+		# When there's no user value, apply the default value:
+
+		return unless descriptor.set
+
+		value = optionValue if descriptor.importable
+		value = Utils.valueOrDefault(optionValue, @_getPropertyDefaultValue(key))
+
+		return if value in [null, undefined]
+
+		@[key] = value
+


more generic tree

more generic tree

--- app/graphite.coffee
+++ app/graphite.coffee
@@ -168,20 +168,26 @@
     finder.component

   TreeNodeComponent = React.createClass
+    displayName: 'TreeNodeComponent'
+    propTypes:
+      node: React.PropTypes.shape({
+        path: React.PropTypes.string.isRequired
+        is_leaf: React.PropTypes.bool.isRequired
+      }).isRequired
+      tree_state: React.PropTypes.object.isRequired
+      value: React.PropTypes.func.isRequired
+      create_node: React.PropTypes.func.isRequired
+      create_error_node: React.PropTypes.func.isRequired
     render: ->
       path = @props.node.path
       state = @props.tree_state[path]

       if state == 'open'
         child_nodes = _.map @props.value(path), (child) =>
-          parts = child.path.split '.'
-          name = parts[parts.length - 1]
-          TreeNodeComponent {value: @props.value, tree_state: @props.tree_state, node: child, toggle: @props.toggle, load: @props.load, name: name}
+          @props.create_node _.extend {}, @props, {node: child}
         child = React.DOM.ul null, _.sortBy child_nodes, (node) -> node.props.name
       else if state == 'failed'
-        child = React.DOM.div null,
-          React.DOM.i {className: 'fa fa-exclamation-triangle'}
-          ' Error loading metric names'
+        child = @props.create_error_node props
       else
         child = null
       if @props.node.is_leaf
@@ -195,7 +201,7 @@
       React.DOM.li null,
         React.DOM.span({onClick: @handle_click},
           React.DOM.i {className: toggle}
-          "#{@props.name}")
+          @props.children)
           React.DOM.div {className: 'child'}, child
     handle_click: ->
       path = @props.node.path
@@ -206,12 +212,13 @@
         @props.toggle path

   TreeComponent = React.createClass
-    statics:
-      subpath: (path) ->
-        if path == ''
-          '*'
-        else
-          "#{path}.*"
+    displayName: 'TreeComponent'
+    propTypes:
+      root: React.PropTypes.string.isRequired
+      load: React.PropTypes.func.isRequired
+      load_children: React.PropTypes.func.isRequired
+      create_node: React.PropTypes.func.isRequired
+      create_error_node: React.PropTypes.func.isRequired

     getDefaultProps: ->
       root: ''
@@ -228,7 +235,7 @@
         @close path

     value: (path) ->
-      @state.cache[path]?.inspect()?.value?.result
+      @state.cache[path]?.inspect()?.value

     open: (path) ->
       tree_state = _.clone @state.tree_state
@@ -241,7 +248,7 @@
         tree_state[path] = 'opening'

         if !state or state.isRejected()
-          promise = graphite.find(TreeComponent.subpath(path))
+          promise = @props.load_children path
           cache[path] = promise
           promise.then (result) =>
             return unless @state.cache[path] == promise
@@ -268,21 +275,39 @@
     componentWillMount: ->
       @open @props.root
     render: ->
-      if @props.root == ''
-        name = 'All Metrics'
-      else
-        name = @props.root
-      React.DOM.ul {className: 'simple-tree'}, TreeNodeComponent
-        value: @value
-        tree_state: @state.tree_state
-        node: {path: @props.root}
-        name: name
-        toggle: @toggle
-        load: @props.load
+      React.DOM.ul {className: 'simple-tree'},
+        @props.create_node {
+          value: @value
+          tree_state: @state.tree_state
+          node: {path: @props.root, is_leaf: false}
+          toggle: @toggle
+          load: @props.load
+          create_node: @props.create_node
+          create_error_node: @props.create_error_node
+        }, name

   component_fn 'tree', 'Generates a browsable tree of metrics', (ctx, query) ->
-    TreeComponent root: query ? '', load: (path) =>
-      ctx.run "q(#{JSON.stringify path})"
+    TreeComponent
+      root: query ? '',
+      load: (path) =>
+        ctx.run "q(#{JSON.stringify path})"
+      load_children: (path) ->
+        if path == ''
+          subpath = '*'
+        else
+          subpath = "#{path}.*"
+        graphite.find(subpath).get 'result'
+      create_node: (props) ->
+        if props.node.path == ''
+          name = 'All Metrics'
+        else
+          parts = props.node.path.split '.'
+          name = parts[parts.length - 1]
+        TreeNodeComponent _.extend({}, props, {name}), name
+      create_error_node: (props) ->
+        React.DOM.div null,
+          React.DOM.i {className: 'fa fa-exclamation-triangle'}
+          ' Error loading metric names'

   FindResultsComponent = React.createClass


fixed editor wackiness

fixed editor wackiness

--- lib/code-display.coffee
+++ lib/code-display.coffee
@@ -21,17 +21,15 @@
       @setFramesInEditor      editor
       @setBreakpointsInEditor editor

-      shadowRoot  = atom.views.getView(editor).shadowRoot
-      $shadowRoot = $ shadowRoot
-      lineNumbers = shadowRoot.querySelector '.line-numbers'
       lineNumberClick = (e) =>
         $tgt = $ e.target
         line = +$tgt.closest('.line-number').attr 'data-buffer-row'
         @ideView.toggleBreakpoint file, line
         false
-      lineNumbers.addEventListener 'click', lineNumberClick
-      @disposables.add new Disposable ->
-        lineNumbers.removeEventListener 'click', lineNumberClick
+
+      $shadowRoot = $ atom.views.getView(editor).shadowRoot
+      $lineNumbers = $shadowRoot.find '.line-numbers'
+      @subs.push $lineNumbers.click lineNumberClick

   getPath: (editor) ->
     path = editor.getPath()


NPM and CWD

NPM and CWD

The cwd needs to be passed in for the process, because npm requires it.
So.. we're doing that. Without it, npm gives odd results because it
looks up the directory chain for a `node_modules` directory and installs
the given directory *into* that directory. Which is like, the opposite
of what we want.

--- lib/deps/install.coffee
+++ lib/deps/install.coffee
@@ -18,9 +18,13 @@

   transport = opts.transport

+  # npm requires the cwd be the location of our installation directory.
+  transportOpts =
+    cwd: dir
+
   args = opts.command.split ' '
   args.push dir
-  transport args, (err, stdout, stderr) ->
+  transport args, transportOpts, (err, stdout, stderr) ->
     if err?
       switch err.code
         when 34 then callback new Error 'package.json not found'


Changed to used named_pipes instead of ports, this also fixes #10, also #6 is somewhat taken care of because the trivial solutions was to use 80 as column end number.

--- lib/linter-julia.coffee
+++ lib/linter-julia.coffee
@@ -1,9 +1,7 @@
 net = require 'net'
 {spawn} = require 'child_process'
-
-sleep = (ms) ->
-  start = new Date().getTime()
-  continue while new Date().getTime() - start < ms
+tmp = require('tmp')
+fs = require('fs')

 lintfromserver = (socket, str, fname) ->
   # This plus one probably because Julia starts index from 1
@@ -15,6 +13,7 @@
 doSomeMagic = (data,textEditor) ->
   filePath = textEditor.getPath()
   inptext = textEditor.getText()
+  column = parseInt(atom.config.get('linter-julia.column'),10)
   linteroutput = [ ]

   lines = data.split("\n")
@@ -31,7 +30,7 @@
       else
         type = "Error"
       text = numbers[1] + ":" + splittedline[2]
-      range = [[row_number, 0], [row_number,1]]
+      range = [[row_number, 0], [row_number, column]]
       fullmsg = {
         type
         text
@@ -46,20 +45,25 @@

 module.exports =
   config:
-    linterjuliaport:
-      title: 'Julia Lint Server Port'
+    column:
+      title: 'Error message column end, the column start is 0'
       type: 'string'
-      default: '2223'
-      description: "julia -e \"using Lint; lintserver(2223)\""
+      default: '80'
+      description: "Lint.jl doesn't return the error column, thus the whole line
+                    is used, meaning column end is 80. To get back to the
+                    previous behavior change the column end to 1"
       order: 1
-    linterjuliadomain:
-      title: 'Julia Lint Server Domain'
-      type: 'string'
-      default: 'localhost'
-      description: "julia -e \"using Lint; lintserver(2223)\""
-      order: 2
   activate: ->
-    spawn 'julia', ['-e', 'using Lint; lintserver(2223)']
+    global.named_pipe = tmp.tmpNameSync({ prefix:'lintserver',postfix: 'sock'})
+    jcode = "using Lint; lintserver(\"#{named_pipe}\")"
+    console.log jcode
+    jserver = spawn 'julia', ['-e', jcode]
+    jserver.stdout.on 'data', (data) -> console.log data.toString().trim()
+    jserver.stderr.on 'data', (data) -> console.log data.toString().trim()
+
+  deactivate: ->
+    # Removes the socket when shutting down
+    fs.unlinkSync(named_pipe)

   provideLinter: ->
     provider =
@@ -68,9 +72,7 @@
       scope: 'file'
       lintOnFly: true
       lint: (textEditor)->
-        port = parseInt(atom.config.get('linter-julia.linterjuliaport'),10)
-        domain = atom.config.get('linter-julia.linterjuliadomain')
-        connection = net.createConnection(port, domain)
+        connection = net.createConnection(named_pipe)

         return new Promise (resolve, reject) ->
           data = []


Stop propagation on PackageCard button events

--- lib/package-card.coffee
+++ lib/package-card.coffee
@@ -74,12 +74,15 @@
       @on 'click', =>
         @parents('.settings-view').view()?.showPanel(@pack.name, {back: opts?.back, pack: @pack})
       @settingsButton.on 'click', =>
+        event.stopPropagation()
         @parents('.settings-view').view()?.showPanel(@pack.name, {back: opts?.back, pack: @pack})

     @installButton.on 'click', =>
+      event.stopPropagation()
       @install()

-    @uninstallButton.on 'click', =>
+    @uninstallButton.on 'click', (event) =>
+      event.stopPropagation()
       @uninstall()

     @packageName.on 'click', (event) =>


take out scrolltop 0 for now

take out scrolltop 0 for now

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -137,7 +137,6 @@
     goToPage: (pageNumber, href) ->
       @model.setPage(pageNumber)
       router.navigate href, {trigger: false}, => @parent.parent.parent.trackAnalytics()
-      $(window).scrollTop(0)

     getOptionsForCoach: ->
       options =


display trick clean

display trick clean

--- src/scripts/ui-scroll-display-trick.coffee
+++ src/scripts/ui-scroll-display-trick.coffee
@@ -67,7 +67,7 @@

 								repeaterType = template[0].localName
 								if repeaterType in ['dl']
-									throw new Error "ng-scroll directive does not support <#{template[0].localName}> as a repeating tag: #{template[0].outerHTML}"
+									throw new Error "ui-scroll directive does not support <#{template[0].localName}> as a repeating tag: #{template[0].outerHTML}"
 								repeaterType = 'div' if repeaterType not in ['li', 'tr']

 								viewport = controllers[0] || angular.element(window)
@@ -178,6 +178,7 @@
 							# clip the invisible items off the bottom
 							bottomHeight = 0 #adapter.bottomPadding()
 							overage = 0
+
 							for i in [buffer.length-1..0]
 								item = buffer[i]
 								itemTop = item.element.offset().top
@@ -191,6 +192,7 @@
 								else
 									break if newRow
 									overage++
+
 							if overage > 0
 								adapter.bottomPadding(adapter.bottomPadding() + bottomHeight)
 								removeFromBuffer(buffer.length - overage, buffer.length)
@@ -270,27 +272,28 @@
 									# if not, increment scrollTop
 									viewport.scrollTop(viewport.scrollTop() + wrapper.element.outerHeight(true))

+						doAdjustment = (rid, scrolling, finalize)->
+							log "top {actual=#{adapter.topDataPos()} visible from=#{topVisiblePos()} bottom {visible through=#{bottomVisiblePos()} actual=#{adapter.bottomDataPos()}}"
+							if shouldLoadBottom()
+								enqueueFetch(rid, true, scrolling)
+							else
+								enqueueFetch(rid, false, scrolling) if shouldLoadTop()
+							finalize(rid) if finalize
+							if pending.length == 0
+								topHeight = 0
+								for item in buffer
+									itemTop = item.element.offset().top
+									newRow = rowTop isnt itemTop
+									rowTop = itemTop
+									itemHeight = item.element.outerHeight(true) if newRow
+									if (adapter.topDataPos() + topHeight + itemHeight < topVisiblePos())
+										if newRow
+											topHeight += itemHeight
+									else
+										topVisible(item) if newRow
+										break
+
 						adjustBuffer = (rid, scrolling, newItems, finalize)->
-							doAdjustment = ->
-								log "top {actual=#{adapter.topDataPos()} visible from=#{topVisiblePos()} bottom {visible through=#{bottomVisiblePos()} actual=#{adapter.bottomDataPos()}}"
-								if shouldLoadBottom()
-									enqueueFetch(rid, true, scrolling)
-								else
-									enqueueFetch(rid, false, scrolling) if shouldLoadTop()
-								finalize(rid) if finalize
-								if pending.length == 0
-									topHeight = 0
-									for item in buffer
-										itemTop = item.element.offset().top
-										newRow = rowTop isnt itemTop
-										rowTop = itemTop
-										itemHeight = item.element.outerHeight(true) if newRow
-										if (adapter.topDataPos() + topHeight + itemHeight < topVisiblePos())
-											if newRow
-												topHeight += itemHeight
-										else
-											topVisible(item) if newRow
-											break
 							if newItems
 								$timeout ->
 									rows = []
@@ -298,15 +301,14 @@
 										element = row.wrapper.element
 										element.css('display', element.displayTemp) if element.hasOwnProperty 'displayTemp'
 										itemTop = element.offset().top
-										newRow = rowTop isnt itemTop
-										if newRow
+										if rowTop isnt itemTop
 											rows.push(row)
 											rowTop = itemTop
 									for row in rows
 										adjustRowHeight(row.appended, row.wrapper)
-									doAdjustment()
-							else
-								doAdjustment()
+									doAdjustment(rid, scrolling, finalize)
+							else
+								doAdjustment(rid, scrolling, finalize)

 						finalize = (rid, scrolling, newItems)->
 							adjustBuffer rid, scrolling, newItems, ->
@@ -373,14 +375,6 @@

 						viewport.bind 'scroll', scrollHandler

-						wheelHandler = (e) ->
-							if !bof and viewport[0].scrollTop is 0
-								e.preventDefault()
-							if !eof and viewport[0].scrollTop is (viewport[0].scrollHeight - viewport[0].offsetHeight)
-								e.preventDefault()
-
-						viewport.parent().bind 'mousewheel', wheelHandler
-
 						$scope.$watch datasource.revision,
 							-> reload()

@@ -393,7 +387,6 @@
 							eventListener.$destroy()
 							viewport.unbind 'resize', resizeHandler
 							viewport.unbind 'scroll', scrollHandler
-							viewport.parent().unbind 'mousewheel', wheelHandler

 						eventListener.$on "update.items", (event, locator, newItem)->
 							if angular.isFunction locator


translate "apply" and "cancel" to Spanish

--- locales/pivot.es.coffee
+++ locales/pivot.es.coffee
@@ -28,6 +28,9 @@
             totals: "Totales"
             vs: "vs"
             by: "por"
+            apply: "Aplicar"
+            cancel: "Cancelar"
+
         aggregators:
             "Cuenta":                             tpl.count(frFmtInt)
             "Cuenta de valores únicos":          tpl.countUnique(frFmtInt)


fixups

fixups

--- test/core/driver/legacyDriver.mocha.coffee
+++ test/core/driver/legacyDriver.mocha.coffee
@@ -14,18 +14,18 @@

 describe "legacyDriver", ->
   ex = facet()
-    .def("Diamonds", facet('SOME_DATA').filter(facet("color").is('D')))
-    .apply('Count', facet('Diamonds').count())
-    .apply('TotalPrice', facet('Diamonds').sum('$price'))
+    .def("diamonds", facet('SOME_DATA').filter(facet("color").is('D')))
+    .apply('Count', facet('diamonds').count())
+    .apply('TotalPrice', facet('diamonds').sum('$price'))
     .apply('Cuts',
-      facet("Diamonds").group("$cut").label('Cut')
-        .def('Diamonds', facet('Diamonds').filter(facet('cut').is('$^Cut')))
-        .apply('Count', facet('Diamonds').count())
+      facet("diamonds").group("$cut").label('Cut')
+        .def('diamonds', facet('diamonds').filter(facet('cut').is('$^Cut')))
+        .apply('Count', facet('diamonds').count())
         .sort('$Count', 'descending')
         .limit(2)
         .apply('Carats',
-          facet("Diamonds").split(facet("carat").numberBucket(0.25), 'Carat', 'Diamonds')
-            .apply('Count', facet('Diamonds').count())
+          facet("diamonds").split(facet("carat").numberBucket(0.25), 'Carat', 'diamonds')
+            .apply('Count', facet('diamonds').count())
             .sort('$Count', 'descending')
             .limit(3)
         )


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -206,13 +206,13 @@
             #    return arc b
         #)
         path.transition().duration(2000).attrTween('d', (d,i) ->
-            if myInstanceId = 'resolved' and i = 0
+            if myInstanceDataId = 'resolved' and i = 0
                 console.log('in tween - OLD DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])
             #console.log('1. in tween - d: ',d)
             myInterpolater = d3.interpolate(window.oldPieData[myInstanceDataId][i] ? d, d)
             #console.log('myInterpolater(0): ',myInterpolater(0))
             window.oldPieData[myInstanceDataId][i] = myInterpolater(0) ? d
-            if myInstanceId = 'resolved' and i = 0
+            if myInstanceDataId = 'resolved' and i = 0
                 console.log('in tween - NEW DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])
             #console.log('2. in tween - d: ',d)
             return (t) ->


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -179,7 +179,7 @@
             #    b = i(t)
             #    return arc b
         #)
-        path.transition().duration(750).attrTween('a', (d,i,a) ->
+        path.transition().duration(750).attrTween('d', (d,i,a) ->
             `var i`
             i = d3.interpolate(@_current, a)
             @_current = i(0)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -230,7 +230,7 @@
       d.x
     ).attr 'y', (d) ->
       d.y
-    #pointers = pointerGroup.selectAll('path.pointer').data(piedata)
+    pointers = pointerGroup.selectAll('path.pointer').data(piedata)
     #pointers.enter().append('path').attr('class', 'pointer').style('fill', 'none').style('stroke', 'black').attr 'marker-end', 'url(#circ)'
     #pointers.exit().remove()
     #pointers.transition().attr 'd', (d) ->


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -3,8 +3,8 @@

   onData: (data) ->
     $(@node).fadeOut().fadeIn()
-    #@render(data.value)
-    @update(data.value)
+    @render(data.value)
+    #@update(data.value)

   render: (data) ->
     if !data


Back out 3gb change

Back out 3gb change

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -87,7 +87,7 @@
         default         : no
       diskSizeInMB      :
         type            : Number
-        default         : 3072
+        default         : 1200

   suspend: (callback)->
     @update { $set: { hostKite: '(banned)' } }, (err)=>


Forked plunks parent may be null if the parent was subsequently deleted.

--- resources/plunks.coffee
+++ resources/plunks.coffee
@@ -437,9 +437,10 @@
   return next(new Error("request.plunk is required for update()")) unless req.plunk

   if req.plunk.fork_of then loadPlunk req.plunk.fork_of, (err, parent) ->
-    parent.forks.remove(req.plunk.fork_of)
-    parent.forked--
-    parent.save()
+    if parent
+      parent.forks.remove(req.plunk.fork_of)
+      parent.forked--
+      parent.save()

   unless ownsPlunk(req.currentSession, req.plunk) then next(new apiErrors.NotFound)
   else req.plunk.remove ->


added initial support for existing images

--- Modules/PhotoPicker/PhotoPickerTable.coffee
+++ Modules/PhotoPicker/PhotoPickerTable.coffee
@@ -2,6 +2,7 @@
   constructor:(options = {}) ->
     @settings = root._.extend({
       backgroundColor: '#eee'
+      photos: []
       onUpdate: (photos, thumbnails) ->
     }, options)

@@ -9,16 +10,10 @@
     @thumbnails = []

     @view = Ti.UI.createView @settings
-    @label = Ti.UI.createLabel { left: 10, top: 2 }
-    @view.add @label

     @addFromGalleryButton = Ti.UI.createButton { title: '+ Gallery', top: 34, left: 2 }
     @addFromGalleryButton.addEventListener 'click', @addFromGallery
     @view.add @addFromGalleryButton
-
-    @addCatButton = Ti.UI.createButton { title: '+ Cat', top: 34, left: 140 }
-    @addCatButton.addEventListener 'click', @addCat
-    @view.add @addCatButton

     @addFromCameraButton = Ti.UI.createButton { title: '+ Camera', top: 34, right: 2 }
     @addFromCameraButton.addEventListener 'click', @addFromCamera
@@ -29,16 +24,19 @@
       cellWidth: 100
       cellHeight: 100
       cellMargin: 5
-      onRemove: (photo, thumbnail) =>
+      onRemove: (photo, thumbnail) =>  #TODO: GJ: record existing image deletes
         @photos = @photos.without photo
         @thumbnails = @thumbnails.without thumbnail
-        @update() #TODO: GJ: should we delete files?
+        @update()
     }
     @view.add @grid.view
+
+    for existingPhoto in @settings.photos
+      @grid.addPhoto existingPhoto, existingPhoto #TODO: GJ: thumbnail?
+
     @update()

   update: =>
-    @label.text = "There are #{@photos.length} photos"
     @settings.onUpdate @photos, @thumbnails

   addFromGallery: =>
@@ -55,15 +53,7 @@
       cancel: ->
       error: ->
     }
-
-  addCat: => #TEMP: GJ: adding remote images so that we can test on simulator
-    url = "http://placekitten.com/g/#{[100,110,120,130].random()}/#{[100,110,120,130].random()}"
-
-    @photos.push url
-    @thumbnails.push url
-    @grid.addPhoto url, url
-    @update()
-
+
   addMedia: (image) =>
     thumbnail = image.imageAsThumbnail(100)
     image = image.imageAsResized(1600, 1200) #TODO: GJ: support other orientations


Rewrite spec about destructuring assignment

1st level assignment is not working.

--- spec/Parser-spec.coffee
+++ spec/Parser-spec.coffee
@@ -206,12 +206,10 @@

     it 'should support destructuring assignment statement of `Object`', ->
       parser.parse """
-      a = b = c = 1
-      { a: { b: c } } = obj
+      a = b = c = d = 1
+      { a: { b: c }, d } = obj
       func = ({ a: { b: c } }) ->
         a = b = c = 2
-      func = ({ a }) ->
-        a 1
       """
       expectNoRefs parser, new Range([1, 2], [1, 3])
       expectNoRefs parser, new Range([1, 7], [1, 8])
@@ -229,14 +227,14 @@
         new Range([1, 10], [1, 11])
       expectEqualRefs parser, new Range([1, 10], [1, 11]),
         new Range([0, 8], [0, 9])
+      expectEqualRefs parser, new Range([0, 12], [0, 13]),
+        new Range([1, 15], [1, 16])
+      expectEqualRefs parser, new Range([1, 15], [1, 16]),
+        new Range([0, 12], [0, 13])
       expectEqualRefs parser, new Range([2, 18], [2, 19]),
         new Range([3, 10], [3, 11])
       expectEqualRefs parser, new Range([3, 10], [3, 11]),
         new Range([2, 18], [2, 19])
-      expectEqualRefs parser, new Range([4, 10], [4, 11]),
-        new Range([5, 2], [5, 3])
-      expectEqualRefs parser, new Range([5, 2], [5, 3]),
-        new Range([4, 10], [4, 11])

     it 'should recognize the scope of variable', ->


:fire: fdescribe

:fire: fdescribe

--- spec/config-spec.coffee
+++ spec/config-spec.coffee
@@ -1750,7 +1750,7 @@
           expect(atom.config.set('foo.bar.str_options', 'One')).toBe false
           expect(atom.config.get('foo.bar.str_options')).toEqual 'two'

-  fdescribe "when .set/.unset is called prior to .loadUserConfig", ->
+  describe "when .set/.unset is called prior to .loadUserConfig", ->
     beforeEach ->
       atom.config.settingsLoaded = false
       fs.writeFileSync atom.config.configFilePath, """


tests: Ensure there is time difference in participant discovery

--- spec/coordinator.coffee
+++ spec/coordinator.coffee
@@ -69,8 +69,10 @@
           # send two times
           client.registerParticipant definition, (err) ->
             return done err if err
-            client.registerParticipant definition, (err) ->
-              return done err if err
+            setTimeout () ->
+              client.registerParticipant definition, (err) ->
+                return done err if err
+            , 20 # ensure there is time difference

         after (done) ->
           coordinator.removeListener 'participant', eventListener
@@ -87,7 +89,7 @@
           d = updated[0]
           chai.expect(d.id).to.equal definition.id
           diff = d.extra.lastSeen.getTime() - added[0].extra.lastSeen.getTime()
-          chai.expect(diff).to.be.above 0
+          chai.expect(diff, 'time difference').to.be.above 0

       describe.skip 'discovery for multiple participants with same role', ->