Calculator fixes. #3739040 , #3739033

Calculator fixes. #3739040 , #3739033

Fixed problem with subespression parsing/evaluation
multiplication symbols will be added in front right parentheses and pi.

--- calculator_spec.coffee
+++ calculator_spec.coffee
@@ -91,19 +91,35 @@
       @handle.press_buttons("( 2 ) 1 0 0 =")
       expect(@handle.output_content()).toEqual("200")

+    it "correctly handles side by side parentheticals", ->
+      @handle.press_buttons("8 ( 9 ) ( 8 ) =")
+      expect(@handle.output_content()).toEqual("576")
+
   it "divides", ->
     @handle.press_buttons("1 0 / 2 =")
     expect(@handle.output_content()).toEqual("5")
-

   describe "pi", ->
     it "does pi", ->
       @handle.press_buttons("pi =")
       expect(@handle.output_content()).toMatch /3.14/

+    it "multiplies pis side by side", ->
+      @handle.press_buttons("pi pi =")
+      expect(@handle.output_content()).toMatch /9.86/
+
+    it "handles the sample instance presented by mario", ->
+      @handle.press_buttons("2 pi ( 1 + 3 ) =")
+      expect(@handle.output_content()).toMatch /25.13/
+
+    it "handles another compilcated example", ->
+      @handle.press_buttons("pi 2 pi ( 1 + 3 ) ( 1 0 ) =")
+      expect(@handle.output_content()).toMatch /789.57/
+
     it "multiplies pi", ->
       @handle.press_buttons("5 pi =")
       expect(@handle.output_content()).toMatch /15.708/
+
     it "multiplies pi reversed", ->
       @handle.press_buttons("pi 5 =")
       expect(@handle.output_content()).toMatch /15.708/


Return Disposables from mocked File event subscription methods

These methods have other callers, so we also ensure we only save off
the callback for the expected call. It would be better to have a fake
file system or something similar.

--- spec/archive-editor-view-spec.coffee
+++ spec/archive-editor-view-spec.coffee
@@ -1,4 +1,5 @@
 fs = require 'fs-plus'
+{Disposable} = require 'atom'
 {File} = require 'pathwatcher'
 {$} = require 'atom-space-pen-views'

@@ -6,8 +7,13 @@
   [archiveView, onDidDeleteCallback, onDidChangeCallback] = []

   beforeEach ->
-    spyOn(File::, 'onDidDelete').andCallFake (callback) -> onDidDeleteCallback = callback
-    spyOn(File::, 'onDidChange').andCallFake (callback) -> onDidChangeCallback = callback
+    spyOn(File::, 'onDidDelete').andCallFake (callback) ->
+      onDidDeleteCallback = callback if @getPath().match /.tar/
+      new Disposable
+
+    spyOn(File::, 'onDidChange').andCallFake (callback) ->
+      onDidChangeCallback = callback if @getPath().match /.tar/
+      new Disposable

     waitsForPromise ->
       atom.packages.activatePackage('archive-view')


Add integration tests for enable/disable step buttons

--- spec/integration/connect-spec.coffee
+++ spec/integration/connect-spec.coffee
@@ -3,45 +3,57 @@
 # TODO: add test for backtrace here (need breakpoint add/remove support though)
 # TODO: add test for local/global variables
 describe "Connecting to rdebug-ide", ->
-
+
   [workspaceElement, activationPromise, file, process] = []

   beforeEach ->
     workspaceElement = atom.views.getView(atom.workspace)
     activationPromise = atom.packages.activatePackage('ruby-debugger')
     file = require.resolve('../fixtures/simple.rb')
-
+
   afterEach ->
     process.kill()
-
+
+  testTheButton = (buttonSelector) ->
+    # 1. start rdebug-ide with Ruby-script
+    waitsFor "start of rdebug-ide", (done) ->
+      process = startDebuggerProcess(file, done)
+
+    # 2. open the Ruby-file
+    waitsForPromise ->
+      atom.workspace.open(file)
+
+    # 3. activate package
+    runs ->
+      atom.commands.dispatch workspaceElement, 'ruby-debugger:toggle'
+
+    waitsForPromise ->
+      activationPromise
+
+    runs ->
+      # 4. Play/start button should not be active
+      expect(workspaceElement.querySelector(buttonSelector)).toBeDisabled()
+
+      # 5. click on Connect button
+      workspaceElement.querySelector('.ruby-debugger .connect').click()
+
+    waitsFor ->
+      workspaceElement.querySelector('.ruby-debugger .connect').textContent.trim() == 'Disconnect'
+
+    runs ->
+      # 6. Start button should now be active
+      expect(workspaceElement.querySelector(buttonSelector)).not.toBeDisabled()
+
+
   describe "when activating package and connecting to a started instance of rdebug-ide", ->
     it "enables the play button", ->
-
-      # 1. start rdebug-ide with Ruby-script
-      waitsFor "start of rdebug-ide", (done) ->
-        process = startDebuggerProcess(file, done)
+      testTheButton '.ruby-debugger .play'

-      # 2. open the Ruby-file
-      waitsForPromise ->
-        atom.workspace.open(file)
-
-      # 3. activate package
-      runs ->
-        atom.commands.dispatch workspaceElement, 'ruby-debugger:toggle'
+    it "enables the step-over button", ->
+      testTheButton '.ruby-debugger .step-over'

-      waitsForPromise ->
-        activationPromise
+    it "enables the step in button", ->
+      testTheButton '.ruby-debugger .step-in'

-      runs ->
-        # 4. Play/start button should not be active
-        expect(workspaceElement.querySelector('.ruby-debugger .play')).toBeDisabled()
-
-        # 5. click on Connect button
-        workspaceElement.querySelector('.ruby-debugger .connect').click()
-
-      waitsFor ->
-        workspaceElement.querySelector('.ruby-debugger .connect').textContent.trim() == 'Disconnect'
-
-      runs ->
-        # 6. Start button should now be active
-        expect(workspaceElement.querySelector('.ruby-debugger .play')).not.toBeDisabled()
+    it "enables the step-out button", ->
+      testTheButton '.ruby-debugger .step-out'


Debug HTTPS redirect loop

Debug HTTPS redirect loop

--- controllers/app.coffee
+++ controllers/app.coffee
@@ -3,6 +3,7 @@
   console.log req.secure
   console.log process.env.NODE_ENV
   if !req.secure && process.env.NODE_ENV == 'production'
+    console.log "https://#{req.headers.host}#{req.url}"
     res.redirect "https://#{req.headers.host}#{req.url}"
   else
     next()


Microns to Micrometers

Microns to Micrometers

Closes #7

--- app/lib/en-us.coffee
+++ app/lib/en-us.coffee
@@ -323,10 +323,10 @@
       answer: '''If one person misclassifies these images it doesn't matter too much. The power of the project is that the crowd agrees and finds interesting and reliable data.. With multiple independent classifications we can statistically calculate the confidence in our work, and that's a large part of the power of Milky Way Project.'''
       question: '''Why do you need so many people to help?'''
     q10:
-      answer: '''Light is a wave, specifically a wave of electromagnetic energy, and it comes in a wide range of wavelengths. Light that we see, or visible light, are electromagnetic waves that oscillate back and forth every 0.4-0.7 microns (there are a thousand microns in a millimeter, or a million microns in a meter). Infrared is light that has longer wavelengths, from 0.7 microns all the way up to about 300 microns.  The shorter-wavelength end of that range is called near infrared because it is near to visible light, and the longer-wavelength range is called far infrared.'''
+      answer: '''Light is a wave, specifically a wave of electromagnetic energy, and it comes in a wide range of wavelengths. Light that we see, or visible light, are electromagnetic waves that oscillate back and forth every 0.4-0.7 micrometers (there are a thousand micrometers in a millimeter, or a million micrometers in a meter). Infrared is light that has longer wavelengths, from 0.7 micrometers all the way up to about 300 micrometers.  The shorter-wavelength end of that range is called near infrared because it is near to visible light, and the longer-wavelength range is called far infrared.'''
       question: '''What is infrared?'''
     q11:
-      answer: '''These images come from two instruments, both aboard the Spitzer Space Telescope.  One of the instruments took images at wavelengths in the 3-8 micron range (and that light is colored green in these images) and the other took images at a wavelength of 24 microns, which is colored red.'''
+      answer: '''These images come from two instruments, both aboard the Spitzer Space Telescope.  One of the instruments took images at wavelengths in the 3-8 micron range (and that light is colored green in these images) and the other took images at a wavelength of 24 micrometers, which is colored red.'''
       question: '''What wavelength are these images?'''
     q00:
       answer: '''All the stars you see in the night sky are part of a very large group of stars (a few hundred billion) that we call the Milky Way Galaxy. If you've ever looked at the night sky from a dark location, you've probably seen the band of light that we also call the 'Milky Way'. This band of light contains most of the stars in the Milky Way Galaxy, and the images you're inspecting all come from a narrow band only a few full moons wide along the Milky Way.'''


Seventh batch of Swedish translations. (#6133)

--- app/locale/sv.coffee
+++ app/locale/sv.coffee
@@ -1187,130 +1187,130 @@
     jobs_benefit_10: "Mammaledighet: 12 veckor betald, nästa 6 @ 55% lön"
     jobs_benefit_11: "Pappaledighet: 12 veckor betald"
     jobs_custom_title: "Skapa din egen"
-#    jobs_custom_description: "Are you passionate about CodeCombat but don't see a job listed that matches your qualifications? Write us and show how you think you can contribute to our team. We'd love to hear from you!"
-#    jobs_custom_contact_1: "Send us a note at"
-#    jobs_custom_contact_2: "introducing yourself and we might get in touch in the future!"
-#    contact_title: "Press & Contact"
-#    contact_subtitle: "Need more information? Get in touch with us at"
-#    screenshots_title: "Game Screenshots"
-#    screenshots_hint: "(click to view full size)"
-#    downloads_title: "Download Assets & Information"
-#    about_codecombat: "About CodeCombat"
-#    logo: "Logo"
-#    screenshots: "Screenshots"
-#    character_art: "Character Art"
-#    download_all: "Download All"
-#    previous: "Previous"
-#    location_title: "We're located in downtown SF:"
-
-#  teachers:
-#    licenses_needed: "Licenses needed"
-
-#  special_offer:
-#    special_offer: "Special Offer"
-#    project_based_title: "Project-Based Courses"
-#    project_based_description: "Web and Game Development courses feature shareable final projects."
-#    great_for_clubs_title: "Great for clubs and electives"
-#    great_for_clubs_description: "Teachers can purchase up to __maxQuantityStarterLicenses__ Starter Licenses."
-#    low_price_title: "Just __starterLicensePrice__ per student"
-#    low_price_description: "Starter Licenses are active for __starterLicenseLengthMonths__ months from purchase."
-#    three_great_courses: "Three great courses included in the Starter License:"
-#    license_limit_description: "Teachers can purchase up to __maxQuantityStarterLicenses__ Starter Licenses. You have already purchased __quantityAlreadyPurchased__. If you need more, contact __supportEmail__. Starter Licenses are valid for __starterLicenseLengthMonths__ months."
-#    student_starter_license: "Student Starter License"
-#    purchase_starter_licenses: "Purchase Starter Licenses"
-#    purchase_starter_licenses_to_grant: "Purchase Starter Licenses to grant access to __starterLicenseCourseList__"
-#    starter_licenses_can_be_used: "Starter Licenses can be used to assign additional courses immediately after purchase."
-#    pay_now: "Pay Now"
-#    we_accept_all_major_credit_cards: "We accept all major credit cards."
-#    cs2_description: "builds on the foundation from Introduction to Computer Science, diving into if-statements, functions, events and more."
-#    wd1_description: "introduces the basics of HTML and CSS while teaching skills needed for students to build their first webpage."
-#    gd1_description: "uses syntax students are already familiar with to show them how to build and share their own playable game levels."
-#    see_an_example_project: "see an example project"
-#    get_started_today: "Get started today!"
-#    want_all_the_courses: "Want all the courses? Request information on our Full Licenses."
-#    compare_license_types: "Compare License Types:"
-#    cs: "Computer Science"
-#    wd: "Web Development"
-#    wd1: "Web Development 1"
-#    gd: "Game Development"
-#    gd1: "Game Development 1"
-#    maximum_students: "Maximum # of Students"
-#    unlimited: "Unlimited"
-#    priority_support: "Priority support"
-#    yes: "Yes"
-#    price_per_student: "__price__ per student"
-#    pricing: "Pricing"
-#    free: "Free"
-#    purchase: "Purchase"
-#    courses_prefix: "Courses"
-#    courses_suffix: ""
-#    course_prefix: "Course"
-#    course_suffix: ""
-
-#  teachers_quote:
-#    subtitle: "Learn more about CodeCombat with an interactive walk through of the product, pricing, and implementation!"
-#    email_exists: "User exists with this email."
-#    phone_number: "Phone number"
-#    phone_number_help: "What's the best number to reach you?"
-#    primary_role_label: "Your Primary Role"
-#    role_default: "Select Role"
-#    primary_role_default: "Select Primary Role"
-#    purchaser_role_default: "Select Purchaser Role"
-#    tech_coordinator: "Technology coordinator"
-#    advisor: "Curriculum Specialist/Advisor"
-#    principal: "Principal"
-#    superintendent: "Superintendent"
-#    parent: "Parent"
-#    purchaser_role_label: "Your Purchaser Role"
-#    influence_advocate: "Influence/Advocate"
-#    evaluate_recommend: "Evaluate/Recommend"
-#    approve_funds: "Approve Funds"
-#    no_purchaser_role: "No role in purchase decisions"
-#    district_label: "District"
-#    district_name: "District Name"
-#    district_na: "Enter N/A if not applicable"
-#    organization_label: "School"
-#    school_name: "School Name"
-#    city: "City"
-#    state: "State / Region"
-#    country: "Country / Region"
-#    num_students_help: "How many students will use CodeCombat?"
-#    num_students_default: "Select Range"
-#    education_level_label: "Education Level of Students"
-#    education_level_help: "Choose as many as apply."
-#    elementary_school: "Elementary School"
-#    high_school: "High School"
-#    please_explain: "(please explain)"
-#    middle_school: "Middle School"
-#    college_plus: "College or higher"
-#    referrer: "How did you hear about us?"
-#    referrer_help: "For example: from another teacher, a conference, your students, Code.org, etc."
-#    referrer_default: "Select One"
-#    referrer_conference: "Conference (e.g. ISTE)"
-#    referrer_hoc: "Code.org/Hour of Code"
-#    referrer_teacher: "A teacher"
-#    referrer_admin: "An administrator"
-#    referrer_student: "A student"
-#    referrer_pd: "Professional trainings/workshops"
-#    referrer_web: "Google"
-#    referrer_other: "Other"
-#    anything_else: "What kind of class do you anticipate using CodeCombat for?"
-#    anything_else_helper: ""
-#    thanks_header: "Request Received!"
-#    thanks_sub_header: "Thanks for expressing interest in CodeCombat for your school."
-#    thanks_p: "We'll be in touch soon! If you need to get in contact, you can reach us at:"
-#    back_to_classes: "Back to Classes"
-#    finish_signup: "Finish creating your teacher account:"
-#    finish_signup_p: "Create an account to set up a class, add your students, and monitor their progress as they learn computer science."
-#    signup_with: "Sign up with:"
-#    connect_with: "Connect with:"
-#    conversion_warning: "WARNING: Your current account is a Student Account. Once you submit this form, your account will be updated to a Teacher Account."
-#    learn_more_modal: "Teacher accounts on CodeCombat have the ability to monitor student progress, assign licenses and manage classrooms. Teacher accounts cannot be a part of a classroom - if you are currently enrolled in a class using this account, you will no longer be able to access it once you update to a Teacher Account."
-#    create_account: "Create a Teacher Account"
-#    create_account_subtitle: "Get access to teacher-only tools for using CodeCombat in the classroom.  Set up a class, add your students, and monitor their progress!"
-#    convert_account_title: "Update to Teacher Account"
-#    not: "Not"
-#    full_name_required: "First and last name required"
+    jobs_custom_description: "Brinner du för CodeCombat men ser inte ett jobb som matchar dina kvalifikationer? Skriv oss och visa hur du tror att du kan bidra till vårt team. Vi skulle älska att höra från dig!"
+    jobs_custom_contact_1: "Skicka oss ett meddelande på"
+    jobs_custom_contact_2: "presenterar dig själv och vi kan komma i kontakt i framtiden!"
+    contact_title: "Press och kontakta"
+    contact_subtitle: "Behöver du mer information? Kontakta oss på"
+    screenshots_title: "Spelskärmbilder"
+    screenshots_hint: "(klicka för att se full storlek)"
+    downloads_title: "Ladda ner saker och information"
+    about_codecombat: "Om CodeCombat"
+    logo: "Logotyp"
+    screenshots: "Skärmdumpar"
+    character_art: "Karaktär Art"
+    download_all: "Ladda ner allt"
+    previous: "Föregående"
+    location_title: "Vi finns i centrala SF:"
+
+  teachers:
+    licenses_needed: "Licenser krävs"
+
+  special_offer:
+    special_offer: "Specialerbjudande"
+    project_based_title: "Projektbaserade kurser"
+    project_based_description: "Webb- och spelutvecklingskurser med delbara slutprojekt."
+    great_for_clubs_title: "Perfekt för klubbar och utvalda"
+    great_for_clubs_description: "Lärare kan köpa upp till __maxQuantityStarterLicenses__ startlicenser."
+    low_price_title: "Bara __starterLicensePrice__ per student"
+    low_price_description: "Startlicenser är aktiva i __starterLicenseLengthMonths__ månader från köpet."
+    three_great_courses: "Tre fantastiska kurser ingår i startlicensen:"
+    license_limit_description: "Lärare kan köpa upp till __maxQuantityStarterLicenses__ startlicenser. Du har redan köpt __quantityAlreadyPurchased__. Kontakta __supportEmail__ om du behöver mer. Startlicenser är giltiga i __starterLicenseLengthMonths__ månader."
+    student_starter_license: "Studentstartlicens"
+    purchase_starter_licenses: "Köp startlicenser"
+    purchase_starter_licenses_to_grant: "Köp startlicenser för att ge åtkomst till __starterLicenseCourseList__"
+    starter_licenses_can_be_used: "Startlicenser kan användas för att tilldela ytterligare kurser direkt efter köpet."
+    pay_now: "Betala nu"
+    we_accept_all_major_credit_cards: "Vi accepterar alla större betalkort."
+    cs2_description: "bygger på grunden av Introduktion till datavetenskap, ner dykning i if-satser, funktioner, händelser och mer."
+    wd1_description: "introducerar grunderna för HTML och CSS samtidigt som de undervisar färdigheter som behövs för studenter att bygga sin första webbsida."
+    gd1_description: "använder syntax som elever redan är bekanta med för att visa dem hur man bygger och delar sina egna spelbara spelnivåer."
+    see_an_example_project: "se ett exempelprojekt"
+    get_started_today: "Kom igång idag!"
+    want_all_the_courses: "Vill du ha alla kurser? Begär information om våra fullständiga licenser."
+    compare_license_types: "Jämför licenstyper:"
+    cs: "Datavetenskap"
+    wd: "Webbutveckling"
+    wd1: "Webbutveckling 1"
+    gd: "Spelutveckling"
+    gd1: "Spelutveckling 1"
+    maximum_students: "Max # antal studenter"
+    unlimited: "Obegränsat"
+    priority_support: "Prioriterat stöd"
+    yes: "Ja"
+    price_per_student: "__pris__ per elev"
+    pricing: "Prissättning"
+    free: "Gratis"
+    purchase: "Inköp"
+    courses_prefix: "Kurser"
+    courses_suffix: ""
+    course_prefix: "Kurs"
+    course_suffix: ""
+
+  teachers_quote:
+    subtitle: "Lär dig mer om CodeCombat med en interaktiv genomgång av produkten, prissättning och implementering!"
+    email_exists: "Användaren med denna e-post finns redan."
+    phone_number: "Telefonnummer"
+    phone_number_help: "Vilket är det bästa numret för att nå dig på?"
+    primary_role_label: "Din primära roll"
+    role_default: "Välj roll"
+    primary_role_default: "Välj primär roll"
+    purchaser_role_default: "Välj köparroll"
+    tech_coordinator: "Teknik samordnare"
+    advisor: "Läroplans specialist/rådgivare"
+    principal: "Rektor"
+    superintendent: "Föreståndare"
+    parent: "Förälder"
+    purchaser_role_label: "Din köpare roll"
+    influence_advocate: "Påverkare / förespråkare"
+    evaluate_recommend: "Utvärdera / rekommendera"
+    approve_funds: "Godkänner betalningar"
+    no_purchaser_role: "Ingen roll vid köpbeslut"
+    district_label: "Distrikt"
+    district_name: "Distrikt namn"
+    district_na: "Ange N/A om inte applicerbart"
+    organization_label: "Skola"
+    school_name: "Skolnamn"
+    city: "Stad"
+    state: "Län / region"
+    country: "Land / region"
+    num_students_help: "Hur många elever kommer att använda CodeCombat?"
+    num_students_default: "Välj omfång"
+    education_level_label: "Utbildningsnivå för studenter"
+    education_level_help: "Välj så många som gäller."
+    elementary_school: "Grundskola"
+    high_school: "Gymnasium"
+    please_explain: "(Vänligen förklara)"
+    middle_school: "Grundskola"
+    college_plus: "Univertitet eller Högskola"
+    referrer: "Hur hörde du talas om oss?"
+    referrer_help: "For example: from another teacher, a conference, your students, Code.org, etc."
+    referrer_default: "Välj en"
+    referrer_conference: "Konferens (t.ex. ISTE)"
+    referrer_hoc: "Code.org/Hour of Code"
+    referrer_teacher: "En lärare"
+    referrer_admin: "En administratör"
+    referrer_student: "En elev"
+    referrer_pd: "Professionella utbildningar / workshops"
+    referrer_web: "Google"
+    referrer_other: "Övrigt"
+    anything_else: "Vilken typ av klass räknar du med att använda CodeCombat för?"
+    anything_else_helper: ""
+    thanks_header: "Förfrågan mottagen!"
+    thanks_sub_header: "Tack för att du uttryckte intresse för CodeCombat för din skola."
+    thanks_p: "Vi kommer snart att kontakta er! Om du behöver komma i kontakt kan du nå oss på:"
+    back_to_classes: "Tillbaka till lektioner"
+    finish_signup: "Slutför att skapa ditt lärarkonto:"
+    finish_signup_p: "Skapa ett konto för att skapa en klass, lägg till dina elever och överse deras framsteg när de lär sig datavetenskap."
+    signup_with: "Registrera med:"
+    connect_with: "Anslut till:"
+    conversion_warning: "VARNING: Ditt nuvarande konto är ett elevkonto. När du har skickat det här formuläret uppdateras ditt konto till ett lärarkonto."
+    learn_more_modal: "Lärarkonton på CodeCombat har möjlighet att överse elevernas framsteg, tilldela licenser och hantera klassrum. Lärarkonton kan inte vara en del av ett klassrum - om du för närvarande är registrerad i en klass med det här kontot kommer du inte längre att kunna komma åt det när du har uppdaterat till ett lärarkonto."
+    create_account: "Skapa ett lärarkonto"
+    create_account_subtitle: "Få tillgång till lärarverktyg för att använda CodeCombat i klassrummet. Konfigurera en klass, lägg till dina elever och överse deras framsteg!"
+    convert_account_title: "Uppdatering av lärarkonto"
+    not: "Inte"
+    full_name_required: "För- och efternamn krävs"

   versions:
     save_version_title: "Spara ny version"
@@ -1396,12 +1396,12 @@
     scrub_execution: "Spela genom nuvarande magiska trollformel."
     toggle_debug: "Toggla debug display."
     toggle_grid: "Toggla grid overlay."
-#    toggle_pathfinding: "Toggle pathfinding overlay."
-#    beautify: "Beautify your code by standardizing its formatting."
+    toggle_pathfinding: "Växla till översikt för sökvägen."
+    beautify: "Försköna din kod genom att standardisera dess formatering."
     maximize_editor: "Maximera/minimera kodredigeraren."

-#  cinematic:
-#    click_anywhere_continue: "click anywhere to continue"
+  cinematic:
+    click_anywhere_continue: "klicka var som helst för att fortsätta"

   community:
     main_title: "CodeCombat Community"
@@ -1418,13 +1418,13 @@
     social_discource: "Gå med i diskussionerna i vårt forum"
     social_facebook: "Gilla CodeCombat på Facebook"
     social_twitter: "Följ CodeCombat på Twitter"
-#    social_slack: "Chat with us in the public CodeCombat Slack channel"
+    social_slack: "Chatta med oss i den offentliga CodeCombat Slack-kanalen"
     contribute_to_the_project: "Bidra till projektet"

   clans:
-#    title: "Join CodeCombat Clans - Learn to Code in Python, JavaScript, and HTML"
-#    clan_title: "__clan__ - Join CodeCombat Clans and Learn to Code"
-#    meta_description: "Join a Clan or build your own community of coders. Play multiplayer arena levels and level up your hero and your coding skills."
+    title: "Gå med i CodeCombat-klaner - Lär dig att koda i Python, JavaScript och HTML"
+    clan_title: "__clan__ - Gå med i CodeCombat-klaner och lär dig att koda"
+    meta_description: "Gå med i en klan eller bygg din egen grupp av kodare. Spela arenanivåer för flera spelare och utveckla din hjälte och dina kodningsförmåga."
     clan: "Klan"
     clans: "Klaner"
     new_name: "Namn på ny klan"
@@ -1438,7 +1438,7 @@
     my_clans: "Mina klaner"
     clan_name: "Klanens namn"
     name: "Namn"
-#    chieftain: "Chieftain"
+    chieftain: "Hövding"
     edit_clan_name: "Ändra klanens namn"
     edit_clan_description: "Ändra beskrivningen av klanen"
     edit_name: "ändra namn"
@@ -1446,7 +1446,7 @@
     private: "(privat)"
     summary: "Summering"
     average_level: "Medelnivå"
-#    average_achievements: "Average Achievements"
+    average_achievements: "Genomsnittliga prestationer"
     delete_clan: "Ta bort klan"
     leave_clan: "Lämna klan"
     join_clan: "Gå med i klan"
@@ -1468,20 +1468,20 @@
     latest_achievement: "Senaste prestation"
     playtime: "Speltid"
     last_played: "Senast spelad"
-#    leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances."
-#    track_concepts1: "Track concepts"
-#    track_concepts2a: "learned by each student"
-#    track_concepts2b: "learned by each member"
-#    track_concepts3a: "Track levels completed for each student"
-#    track_concepts3b: "Track levels completed for each member"
-#    track_concepts4a: "See your students'"
-#    track_concepts4b: "See your members'"
-#    track_concepts5: "solutions"
-#    track_concepts6a: "Sort students by name or progress"
-#    track_concepts6b: "Sort members by name or progress"
-#    track_concepts7: "Requires invitation"
-#    track_concepts8: "to join"
-#    private_require_sub: "Private clans require a subscription to create or join."
+    leagues_explanation: "Spela i en liga mot andra klanmedlemmar i dessa instanser med flera spelare."
+    track_concepts1: "Spåra koncept"
+    track_concepts2a: "lärde sig av varje elev"
+    track_concepts2b: "lärde sig av varje medlem"
+    track_concepts3a: "Spårnivåer slutförda för varje elev"
+    track_concepts3b: "Spårnivåer slutförda för varje medlem"
+    track_concepts4a: "Se dina elevers"
+    track_concepts4b: "Se dina medlemars"
+    track_concepts5: "lösningar"
+    track_concepts6a: "Sortera elever efter namn eller framsteg"
+    track_concepts6b: "Sortera medlemar efter namn eller framsteg"
+    track_concepts7: "Kräver inbjudan"
+    track_concepts8: "att ansluta sig"
+    private_require_sub: "Privata klaner kräver ett abonnemang för att skapa eller gå med i."

   courses:
     create_new_class: "Skapa ny klass"


Addition of Sublists and move of Talk definitions

--- app/pages/lab/glossary.cjsx
+++ app/pages/lab/glossary.cjsx
@@ -33,12 +33,6 @@

       + **Project builder** - This is the web tool that researchers use to create Zooniverse projects. It can be found at [http://www.zooniverse.org/lab](/lab)  There is documentation to help with this process on the Project Builder page.
-
-
-      + **Talk** - is the object-orientated discussion tool associated with your project. Talk enables volunteers to comment on the subjects they've reviewed and promotes discussion amongst the volunteer community.  Talk is also a place where the research team and project volunteers can interact. Talk has a series of message boards for longer discussions. Additionally, each subject has a dedicated page on its project Talk where a registered volunteer can write a comment, add searchable Twitter-like hashtags, or  link multiple subjects together into groups called collections
-
-
-      + **Talk Tags** - Tags are used to help note something as relating to a particular topic. For example, one way of using it would be the following:  you’ve found an image that you think would be good for the Daily Zoo, and so you post it in Talk and then include the tag #dailyzoo in the post. This makes it easier for it to be found as a suggestion, because you can do a search for a particular tag using the search bar. A list of popular tags is displayed on the right-hand side of the Talk page and clicking one will take you to instances of that tag in the Talk boards. So for example if you clicked on #dailyzoo, you’d then be shown all the different suggestions that people have made for it that they’ve tagged.

       + **Zooniverse** - the Zooniverse encompasses the team, website, projects and our codebase. The Zooniverse is an open web-based platform for large scale citizen science research projects, capable of supporting tens of thousands of simultaneous users. The Zooniverse hosts the largest collection of online citizen science projects in the world, supporting over 1.5 million registered users and containing projects in astronomy, ecology, humanities, physics, and beyond.
@@ -80,10 +74,6 @@

       ### Project-Specific Terms

-
-      + **Active workflow** - these are workflows that are currently being presented to volunteers. They can’t be edited whilst they are active. You can make a workflow active and inactive by ticking the relevant box on the Visibility tab on the Project Builder page
-
-
       + **Aggregation** - Each subject in a Zooniverse project is independently classified by multiple people. Aggregation is the process of combining these multiple assessments together.

@@ -93,12 +83,6 @@
       + **Classification interface** - This is the web interface where volunteers are review subjects (subjects being data that volunteers are presented to in projects) and perform the desired project assessments and tasks. Researchers can add in various things to this interface such as tutorials on how to use it, and also mini-courses that give you information about the science behind the project. Each workflow has its own separate classification interface. You can access the classification interface through the main landing page of the project.

-      + **Default workflow** -  The default workflow is the workflow that appears if the volunteers doesn’t choose one from the offered list on the front page of the project  but just clicks on the Classify tab. If a project has more than one active workflow, the project owner and project collaborators can choose which one will be the default workflow.
-
-
-      + **Drawing Task** - A task where the volunteers are asked to directly highlight or mark something on an image (e.g. drawing a circle around a penguin if visible in the image presented)
-
-
       + **Export** - An export is how to get the volunteer classifications and other relevant information about your project that is stored in the Zooniverse databases. It is essentially a data dump from the Zooniverse database. Project owners and collaborators can request data exports from their projects through the Project Builder. Project data is supplied in CSV/JSON format and Talk data is supplied in JSON format, and the two most commonly used exports are the subject data export, providing all the information stored about the subjects you’ve uploaded for your project, and the classification export, which has information stored for each project classification. You can request a data export by going to the Project Builder, going to the Data Exports tab and then selecting from where whichever data export you’d like, provided you have the right permissions.

@@ -106,9 +90,6 @@

       + **Gold standard data** - This is data from classifications made by ‘Experts’. ‘Expert’ is a role assigned by the project owner, and their data can be used as a standard to compare the rest of the data against. There is currently an experimental feature which allows you to create a training set and provide in-classification feedback using gold standard data.
-
-
-      + **Inactive workflow** - Inactive workflows are ones that aren’t currently being shown to volunteers, but can be edited. You can make a workflow active and inactive by ticking the relevant box on the Visibility tab on the Project Builder page

       + **Landing page** - front page of your project’s website. This is where people are directly when they go to your project’s url. The landing page is always accessible from your project’s website by clicking on the project’s avatar.
@@ -126,22 +107,26 @@
       + **Project Tags** - These help define which field of research your project belongs to, and determines which category your project will sit under on the Projects page in the categories section (it will still appear on the main Project page regardless). Users can also search by tag to find projects.

-      + **Question Task** - A task where the volunteers are asked to assess the image and respond to a multiple choice question. In the Project Builder, a question task can allow the volunteer to choose a single response or select multiple answers to the question posed.
-
-
       + **Subject** - The chunk of data/thing a volunteer on a Zooniverse project is being presented with and asked to review and analyze.  It typically is an image, graph, photo, audio recording, video,  or a collection of these different things.

       + **Subject Set** - This is a group of subjects (subjects being data that volunteers are presented to in projects). Subjects are uploaded into subject sets through the Project Builder, and it is subject sets that can be linked to workflows in order to get the desired subjects showing on your project’s webpage. You can group subjects into subject sets however you wish. You might want to group Subjects together, for example to represent a season’s worth of images in Snapshot Serengeti or a particular cell dye staining as in Cell Slider.

-      + **Sub-task** - Sometimes when you are asked to do a task, such as drag a circle around an element in a picture, you are then asked a further task and what is contained in the circle. This is a subtask. For example, you may be asked to circle penguins in an image, a sub-task would be identifying whether the penguin circled is an adult or chick.
+      + **Talk** - is the object-orientated discussion tool associated with your project. Talk enables volunteers to comment on the subjects they've reviewed and promotes discussion amongst the volunteer community.  Talk is also a place where the research team and project volunteers can interact. Talk has a series of message boards for longer discussions. Additionally, each subject has a dedicated page on its project Talk where a registered volunteer can write a comment, add searchable Twitter-like hashtags, or  link multiple subjects together into groups called collections

-
-      + **Survey task** - A survey task is a task where you identify something by selection from many options and then are asked a variety of questions about what you’ve just identified, like behaviour, number or color.  For example, you could be asked to identify an animal in an image and then answer questions on how many legs you can see, which way it is facing and whether it is an adult or baby. An example of such a project is Camera CATalog.
+        + **Talk Tags** - Tags are used to help note something as relating to a particular topic. For example, one way of using it would be the following:  you’ve found an image that you think would be good for the Daily Zoo, and so you post it in Talk and then include the tag #dailyzoo in the post. This makes it easier for it to be found as a suggestion, because you can do a search for a particular tag using the search bar. A list of popular tags is displayed on the right-hand side of the Talk page and clicking one will take you to instances of that tag in the Talk boards. So for example if you clicked on #dailyzoo, you’d then be shown all the different suggestions that people have made for it that they’ve tagged.

       + **Tasks** - A task could be listing how many of a particular thing a volunteer sees in an image and then drawing circles around them, identifying the various animals they can see in an image or identifying where abouts in an image something is. There are a wide variety of tools to help create a wide variety of different tasks in the Project Builder tool. One or more tasks make up a workflow.
+
+        + **Drawing Task** - A task where the volunteers are asked to directly highlight or mark something on an image (e.g. drawing a circle around a penguin if visible in the image presented)
+
+        + **Question Task** - A task where the volunteers are asked to assess the image and respond to a multiple choice question. In the Project Builder, a question task can allow the volunteer to choose a single response or select multiple answers to the question posed.
+
+        + **Sub-task** - Sometimes when you are asked to do a task, such as drag a circle around an element in a picture, you are then asked a further task and what is contained in the circle. This is a subtask. For example, you may be asked to circle penguins in an image, a sub-task would be identifying whether the penguin circled is an adult or chick.
+
+        + **Survey task** - A survey task is a task where you identify something by selection from many options and then are asked a variety of questions about what you’ve just identified, like behaviour, number or color.  For example, you could be asked to identify an animal in an image and then answer questions on how many legs you can see, which way it is facing and whether it is an adult or baby. An example of such a project is Camera CATalog.

       + **Tools** - tools enable volunteers draw or highlight a particular area of a subject image presented on the classification interface (e.g. draw a circle, draw a line, place a pointer). You can design your project such that a single task in the Project Builder can have one or more tools available for volunteers to mark or identify different features found in your subject images.
@@ -151,6 +136,12 @@

       + **Workflow** - This is a series of tasks and assessments that a volunteer is asked to do  when presented with data in a Zooniverse’s project classification interface. This can be either one task or multiple tasks, depending on the project.
+
+        + **Active workflow** - these are workflows that are currently being presented to volunteers. They can’t be edited whilst they are active. You can make a workflow active and inactive by ticking the relevant box on the Visibility tab on the Project Builder page
+
+        + **Default workflow** -  The default workflow is the workflow that appears if the volunteers doesn’t choose one from the offered list on the front page of the project  but just clicks on the Classify tab. If a project has more than one active workflow, the project owner and project collaborators can choose which one will be the default workflow.
+
+        + **Inactive workflow** - Inactive workflows are ones that aren’t currently being shown to volunteers, but can be edited. You can make a workflow active and inactive by ticking the relevant box on the Visibility tab on the Project Builder page

       + **Zooniverse approved project** - this is a project that has undergone beta testing and been approved by the Zooniverse team. Details of this process can be found on the Policy Page. Zooniverse approved projects that are currently live can be seen on the Projects page.


inline comments in tests

inline comments in tests

 On branch master

	modified:   test/test-executable.coffee

--- test/test-executable.coffee
+++ test/test-executable.coffee
@@ -26,6 +26,7 @@
 it 'should run on command', (done) ->
     @expectCount(7)

+    # Collect stdout for testing
     whenRunning = (serverProc) ->
         writeCount = 0
         buff = ''
@@ -38,6 +39,7 @@
             return done(new Error(chunk))
         return

+    # Test after getting stdout
     test = (buff, serverProc) ->
         lines = buff.split('\n')
         proxyMessage = JSON.parse(lines[0])
@@ -48,6 +50,7 @@
         expect(rpcMessage.level).toBe(30)
         expect(rpcMessage.msg).toBe("started rpc server on 127.0.0.1:7272")

+        # Make sure only one process is running
         PROC.findProcess(gProcTitle).then (found) ->
             foundProc = found[0] or {}
             expect(found.length).toBe(1)
@@ -56,6 +59,7 @@
             return done()
         return

+    # Start Maestro
     opts =
         command: 'dist/cli.js'
         args: ['localhost']
@@ -67,9 +71,10 @@

 it 'should log application output', (done) ->
     @expectCount(1)
+    timeout = null

-    timeout = null
-    self = @
+    # After Maestro is running, register a remote app
+    # (which will throw an error)
     whenRunning = (serverProc) ->
         DNODE.connect DEFAULT_MONITOR_PORT, '127.0.0.1', (remote) ->
             app =
@@ -82,6 +87,7 @@
                 return
         return

+    # Ensure errors are reported on stderr
     test = (serverStderr) ->
         serverStderr.on 'data', (chunk) ->
             check = /Error: test fatal error/.test(chunk)
@@ -98,6 +104,7 @@
             return
         return

+    # Start Maestro
     opts =
         command: 'dist/cli.js'
         args: ['localhost', FIXTURES]


Replace hard coded form schema by dynamic service

Gets an angular-dynamic-forms compatible form description from a
Facility given a facility code.

--- app/scripts/services.coffee
+++ app/scripts/services.coffee
@@ -16,130 +16,32 @@
     apiResource $resource, 'waterpoints'
   .factory 'Facility', ($resource) ->
     apiResource $resource, 'facilities'
-  .factory 'Form', () ->
-    fields =
-      date_recorded:
-        type: "datetime"
-        label: "Date recorded"
-      company:
-        type: "text"
-        label: "Company"
-      region:
-        type: "text"
-        label: "Region"
-      district:
-        type: "text"
-        label: "District"
-      lga_name:
-        type: "text"
-        label: "LGA Name"
-      ward:
-        type: "text"
-        label: "Ward"
-      village:
-        type: "text"
-        label: "Village"
-      subvillage:
-        type: "text"
-        label: "Subvillage"
-      wpt_name:
-        type: "text"
-        label: "Waterpoint Name"
-      wpt_code:
-        type: "text"
-        label: "Waterpoint Code"
-      population:
-        type: "number"
-        label: "Population"
-      scheme_name:
-        type: "text"
-        label: "Scheme Name"
-      water_perm:
-        type: "text"
-        label: "Water Permission"
-      catchment:
-        type: "text"
-        label: "Catchment"
-      funder:
-        type: "text"
-        label: "Funder"
-      installer:
-        type: "text"
-        label: "Installer"
-      construction_year:
-        type: "datetime"
-        label: "Construction Year"
-      source_type:
-        type: "text"
-        label: "Source Type"
-      extraction:
-        type: "text"
-        label: "Extraction"
-      waterpoint:
-        type: "text"
-        label: "Waterpoint"
-      status_detail:
-        type: "text"
-        label: "Status Detail"
-      status:
-        type: "select"
-        label: "Status"
-        options:
-          "Functional":
-            label: "Functional"
-          "Not functional":
-            label: "Not Functional"
-      breakdown_year:
-        type: "datetime"
-        label: "Breakdown Year"
-      hardware_defect:
-        type: "text"
-        label: "Hardware Defect"
-      reason_wpt:
-        type: "text"
-        label: "Reason for Defect"
-      water_quantity:
-        type: "text"
-        label: "Water Quantity"
-      water_quality:
-        type: "text"
-        label: "Water Quality"
-      scheme_management:
-        type: "text"
-        label: "Scheme Management"
-      wp_management:
-        type: "text"
-        label: "Waterpoint Management"
-      water_payment:
-        type: "text"
-        label: "Water Payment"
-      amount_tsh:
-        type: "text"
-        label: "Amount (Tanzanian shilling)"
-      public_meeting:
-        type: "text"
-        label: "Public Meeting"
-      comment:
-        type: "text"
-        label: "Comment"
-      gps_height:
-        type: "text"
-        label: "GPS Height"
-      latitude:
-        type: "text"
-        minValue: -90
-        maxValue: 90
-        label: "Latitude"
-      longitude:
-        type: "text"
-        minValue: -180
-        maxValue: 180
-        label: "Longitude"
-    for f of fields
-      fields[f].class = "form-control"
-      fields[f].wrapper = '

'
-    fields.submit =
-      type: "submit"
-      label: "Save"
-      class: "btn btn-primary"
-    return fields
+  # Get an angular-dynamic-forms compatible form description from a Facility
+  # given a facility code
+  .factory 'Form', (Facility) ->
+    (facility_code) ->
+      Facility.get(facility_code: facility_code)
+        # Return a promise since dynamic-forms needs the form template in
+        # scope when the controller is invoked
+        .$promise.then (facility) ->
+          fields = facility._items[0].fields
+          for f of fields
+            # Use the field name as label if no label was specified
+            if not fields[f].label?
+              fields[f].label = f
+            fields[f].type = 'text' if fields[f].type == 'string'
+            # FIXME a number field assumes integers, therefore use text
+            fields[f].type = 'text' if fields[f].type == 'float'
+            fields[f].type = 'number' if fields[f].type == 'integer'
+            if fields[f].allowed?
+              fields[f].type = 'select'
+              options = {}
+              options[label] = label: label for label in fields[f].allowed
+              fields[f].options = options
+            fields[f].class = "form-control"
+            fields[f].wrapper = '

'
+          fields.submit =
+            type: "submit"
+            label: "Save"
+            class: "btn btn-primary"
+          return fields


add authorization to lemgram_count-call

add authorization to lemgram_count-call

--- app/scripts/services.coffee
+++ app/scripts/services.coffee
@@ -388,13 +388,13 @@

                 lemgram = karpLemgrams.join(",")
                 corpora = corporaIDs.join(",")
+                headers = model.getAuthorizationHeader()
+                headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
                 $http(
                     method: 'POST'
                     url: settings.cgiScript
                     data : "command=lemgram_count&lemgram=#{lemgram}&count=lemgram&corpus=#{corpora}"
-                    headers : {
-                        'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
-                    }
+                    headers : headers
                 ).then (response) =>
                     data = response.data
                     delete data.time


remove debugging

remove debugging

--- test/acceptance/coffee/AppendingUpdatesTests.coffee
+++ test/acceptance/coffee/AppendingUpdatesTests.coffee
@@ -6,8 +6,6 @@
 ObjectId = mongojs.ObjectId
 Settings = require "settings-sharelatex"
 request = require "request"
-console.log "hiiiiis"
-console.log Settings.redis.history
 rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now

 TrackChangesApp = require "./helpers/TrackChangesApp"
@@ -87,7 +85,6 @@

 		describe "when the updates are recent and from the same user", ->
 			beforeEach (done) ->
-				console.log 1
 				TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
 					op: [{ i: "b", p: 6 }]
 					meta: { ts: Date.now(), user_id: @user_id }
@@ -101,13 +98,9 @@
 					meta: { ts: Date.now(), user_id: @user_id }
 					v: 8
 				}], (error) =>
-					console.log 2, error
-					throw error if error?
-					console.log 3
+					throw error if error?
 					TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
-						console.log 4, error
 						throw error if error?
-						console.log 5
 						done()

 			it "should combine all the updates into one pack", ->


code cleanup, time decreased

code cleanup, time decreased

--- client/coffeescript/templates/conversation.coffee
+++ client/coffeescript/templates/conversation.coffee
@@ -1,11 +1,11 @@
 carousel = ->
   parent = Session.get('carousel_parent')
   cur_reply = Session.get("reply_#{parent._id}")
-  if parent? && cur_reply?
+  if parent? and cur_reply?
     all_replies = Posts.find( parent_id: parent._id ).fetch()
     idx = 0
     for reply, i in all_replies
-      if reply._id == cur_reply
+      if reply._id is cur_reply
         idx = (i + 1) % all_replies.length
     Session.set("reply_#{parent._id}", all_replies[idx]._id)

@@ -18,7 +18,7 @@

     #Carousel implementation
     Session.set('carousel_parent', cur_post)
-    carousel_handle = Meteor.setInterval(carousel, 5000)
+    carousel_handle = Meteor.setInterval(carousel, 3000)
     Session.set('carousel_handle', carousel_handle)

     posts = []


Workspace: Added use http link to workspace browser.

--- client/landing/Workspace/panes/previewpane.coffee
+++ client/landing/Workspace/panes/previewpane.coffee
@@ -18,10 +18,32 @@

     @container.addSubView @previewer = new PreviewerView viewerOptions

-    @container.addSubView new KDCustomHTMLView
-      tagName  : "p"
+    @container.addSubView @secureInfo = new KDCustomHTMLView
+      tagName  : "div"
       cssClass : "tw-browser-splash"
-      partial  : """ Sorry, you can only preview links starting with "https" """
+      partial  : """
+
You can only preview links starting with "https".
 In order to view http content you can """
+
+    httpLink   = new KDCustomHTMLView
+      tagName  : "a"
+      cssClass : "tw-http-link"
+      partial  : "click here"
+      click    : => @useHttp()
+
+    infoLink   = new KDCustomHTMLView
+      tagName  : "a"
+      partial  : " — why?"
+      cssClass : "tw-secure-info"
+      attributes:
+        href   : "http://security.stackexchange.com/questions/38317/specific-risks-of-embedding-an-https-iframe-in-an-http-page"
+
+    @secureInfo.addSubView httpLink
+    @secureInfo.addSubView infoLink
+
+  useHttp: ->
+    KD.getSingleton("appManager").quitByName "Teamwork"
+    KD.getSingleton("router").handleRoute "/Activity"
+    $.cookie "kdproxy-usehttp", "1"

   pistachio: ->
     """


Set display property directly

Set display property directly

--- lib/grammar-status-view.coffee
+++ lib/grammar-status-view.coffee
@@ -40,6 +40,6 @@
         grammarName = grammar.name ? grammar.scopeName
       @grammarLink.element.textContent = grammarName
       @grammarLink.element.dataset.grammar = grammarName
-      @show()
+      @element.style.display = ''
     else
-      @hide()
+      @element.style.display = 'none'


Fixed opening issue

Fixed opening issue

--- lib/handler/config_view.coffee
+++ lib/handler/config_view.coffee
@@ -1,4 +1,4 @@
-Do{CompositeDisposable} = require 'event-kit'
+{CompositeDisposable} = require 'event-kit'
 {ScrollView} = require 'atom-space-pen-views'
 {$$, TextEditorView} = require 'atom-space-pen-views'
 query = require './query'


Add turn off air filter

Add turn off air filter

--- scripts/lockstep-cron.coffee
+++ scripts/lockstep-cron.coffee
@@ -24,3 +24,9 @@
   , null, false, 'Asia/Bangkok'

   cronJob.start()
+
+  cronJob = new CronJob '00 18 * * 1-5', =>
+    robot.messageRoom '#general', '@here Hey, turn off air filter, NOW!!!'
+  , null, false, 'Asia/Bangkok'
+
+  cronJob.start()


Fix queue positioning

Fix queue positioning

--- modules/player/index.coffee
+++ modules/player/index.coffee
@@ -104,11 +104,11 @@
       # Try to use a WebHook for the "added to queue" message
       @webHooks.getForChannel(msg.channel, true)
       .then (hook)=>
-        hook.execSlack @hud.addItemWebhook(msg.guild, qI.requestedBy, qI, queue.items.length+1)
+        hook.execSlack @hud.addItemWebhook(msg.guild, qI.requestedBy, qI, queue.items.length)
         msg.delete()
       # The old method
       .catch (e)=>
-        msg.channel.sendMessage @hud.addItem msg.guild, qI.requestedBy, qI, queue.items.length+1
+        msg.channel.sendMessage @hud.addItem msg.guild, qI.requestedBy, qI, queue.items.length
         .then (m)=>
           omsg = m;
           setTimeout ->


Doc fix

Doc fix

--- server/packages/express/Manager.coffee
+++ server/packages/express/Manager.coffee
@@ -11,7 +11,7 @@

 {defaultLogger} = require 'logging'

-# An implementation of [HttpManager](./manager.html) using the
+# An implementation of [HttpManager](../http/manager.html) using the
 # Express framework.
 module.exports = class Express extends (require '../http/manager')


Correct socket port

Correct socket port

--- client/app/main.coffee
+++ client/app/main.coffee
@@ -1,5 +1,5 @@
 $                 = require('jquery')
-socket            = require('socket.io-client').connect('http://utuchat.exobald.es:5000')
+socket            = require('socket.io-client').connect('http://utuchat.exobald.es:5001')

 messagesContainer = require('./scripts/messages.coffee').messagesContainer
 usersContainer    = require('./scripts/users.coffee').usersContainer


Increase timeouts to decrease server resources consumption

--- server/server_timeout.coffee
+++ server/server_timeout.coffee
@@ -11,7 +11,7 @@
             Sessions.insert({user_id: user_id, heartbeat: Date.now()})
    })

-   interval = Meteor.settings.purgeInterval || 3000
+   interval = Meteor.settings.purgeInterval || 60 * 1000
    Meteor.setInterval(() ->
       loggedOutUsersIds = _.pluck(Meteor.users.find('services.resume.forceLogout': true).fetch(), '_id')
       if loggedOutUsersIds?.length
@@ -20,7 +20,7 @@
             $unset: {'services.resume.forceLogout': ''}})

       timeout = Date.now()
-      timeout -= Meteor.settings.inactivityTimeout || 300000
+      timeout -= Meteor.settings.inactivityTimeout || 15 * 60 * 1000
       usersToLogOut = _.pluck(Sessions.find({heartbeat: {$lt: timeout}}).fetch(), 'user_id')
       if usersToLogOut?.length
          Meteor.users.update({_id: {$in: usersToLogOut}}, {$set: {'services.resume.forceLogout': true}})


Fixed: proper __DEV__ management

Fixed: proper __DEV__ management

--- client/gulpfile.coffee
+++ client/gulpfile.coffee
@@ -59,7 +59,7 @@
                 .pipe gulpif isProd(), uglify()

             .pipe sourceMaps.write('.')
-            .pipe gReplace /__DEV__/g, 'false'
+            .pipe gReplace /__DEV__/g, "#{not isProd()}"
             .pipe gulp.dest './public/js'

     if global.isWatching


Planning client side js

Planning client side js

--- client/src/main.coffee
+++ client/src/main.coffee
@@ -1,3 +1,39 @@
+
+
+class DiceFace
+class NumberDiceFace extends DiceFace
+    ###*
+     * Assigns the number to the DiceFace object.
+     * @param {int} @number The number that appears on the dice face
+    ###
+    constructor: (@number) ->
+
+class BracketsFace extends DiceFace
+class BinaryOperatorDiceFace extends DiceFace
+class UnaryOperatorDiceFace extends DiceFace
+class PlusDiceFace extends BinaryOperatorDiceFace
+
+
+
+###
+    ClientAnswer - A class for storing what dice the user is currently
+###
+class ClientAnswer
+    answerDice : []  # array DiceFace
+    addBrackets : (start, extends) ->
+        #answerDice = answerDice[0..start].concat (answerDice.) answerDice[start..extends] concat
+
+
+###
+    ClientGoal - A class for the goal. If the player is first, he sets the goal.
+    If he is not first, then he receives the goal from another player.
+###
+class ClientGoal
+    settingGoalDice : []  # array DiceFace, an array used while array is setting the dice
+    setGoal : (start, extends) ->
+        answerDice = numbers[0..2]
+
+

 $(document).ready ->
   $('#unallocated ul li').click(->
@@ -16,4 +52,8 @@

     $('.mat').toggleClass('glow')

+
+
+
+
   )


Flesh out initialization tests

Flesh out initialization tests

--- spec/indentation-indicator-spec.coffee
+++ spec/indentation-indicator-spec.coffee
@@ -16,6 +16,18 @@
     it 'displays in the status bar', ->
       expect(atom.workspaceView.find('.indentation-indicator').length).toBe 1

+    it 'has placeholder text if there is no file open', ->
+      view = atom.workspaceView.find('.indentation-indicator')
+      expect(view.text()).toBe 'foo:42'
+
+    it 'reflects the editor settings if there is a file open', ->
+      waitsForPromise ->
+        atom.workspace.open('sample.js')
+
+      runs ->
+        view = atom.workspaceView.find('.indentation-indicator')
+        expect(view.text()).toBe 'Spaces:2'
+
   describe '.deactivate', ->
     it 'removes the indicator view', ->
       view = atom.workspaceView.find('.indentation-indicator')


support for jsx html blocks

support for jsx html blocks

--- lib/auto-id-class.coffee
+++ lib/auto-id-class.coffee
@@ -24,7 +24,7 @@
   # Insert ID attribute method
   ###
   insert_id_attribute: ->
-    if @cursor_inside_html_tag()
+    if @cursor_inside_html_tag() || @cursor_inside_jsx_html_tag()
       @insert_attribute('id')
     else
       event.abortKeyBinding()
@@ -36,6 +36,8 @@
   insert_class_attribute: ->
     if @cursor_inside_html_tag()
       @insert_attribute('class')
+    else if @cursor_inside_jsx_html_tag()
+      @insert_attribute('className')
     else
       event.abortKeyBinding()

@@ -48,7 +50,7 @@

     # Get editor
     editor = atom.workspace.getActiveTextEditor()
-    if !editor then return false;
+    if !editor then return false

     # Get cursor
     if editor.cursors then cursor = editor.cursors[0] else return false
@@ -91,6 +93,49 @@
     return true

+  cursor_inside_jsx_html_tag: ->
+
+    # Get editor
+    editor = atom.workspace.getActiveTextEditor()
+    if !editor then return false
+
+    # Get cursor
+    if editor.cursors then cursor = editor.cursors[0] else return false
+
+    # Get cursor position and scope
+    cursorBufferPos = cursor.getBufferPosition()
+    cursorScopes = editor.scopeDescriptorForBufferPosition(cursorBufferPos).scopes
+
+    # Checking scope strings against the scope descriptions.
+    # First, check if cursor within HTML scope description:
+    if cursorScopes[0] && cursorScopes[0].search('source.js') < 0
+      return false
+
+    # Within tag types:
+    if cursorScopes[3] && cursorScopes[3].search('meta\.tag\.jsx') < 0
+      return false
+
+    # Invalid cursor scope positions:
+    if cursorScopes[5]
+      switch cursorScopes[5]
+        when "string.quoted.double.js" then return false
+        when "entity.other.attribute-name.js" then return false
+
+    # Finally is the cursor truly within an html tag? Check for < and > chars now
+    # Get the cursors current column and line of buffer for evaluation
+    cursorColumn = cursorBufferPos.column
+    bufferLine = cursor.getCurrentBufferLine()
+    codeLeftOfColumn = bufferLine.substring(0, cursorColumn)
+    codeRightOfColumn = bufferLine.substring(cursorColumn, bufferLine.length)
+
+    # Is the cursor within < and > ? Exit if not
+    if(codeLeftOfColumn.lastIndexOf('<') <= codeLeftOfColumn.lastIndexOf('>'))
+      return false
+    if(codeRightOfColumn.lastIndexOf('>') <= codeRightOfColumn.lastIndexOf('<'))
+      return false
+
+    return true
+
   ###
   # Insert attribute by string
   ###


moved preloading images to own function

moved preloading images to own function

--- coffee/renderer.coffee
+++ coffee/renderer.coffee
@@ -122,6 +122,8 @@
     deathAnimation.setPos entity.getGraphic().getCenter()
     @deathAnimations_.push deathAnimation

-images = (m for m in EntitySpriteFactory.getImagesToPreload())
-images.push (m for m in GraphicFactory.getImagesToPreload())...
-Sprite.preloadImages images
+preloadImages = ->
+  images = (m for m in EntitySpriteFactory.getImagesToPreload())
+  images.push (m for m in GraphicFactory.getImagesToPreload())...
+  Sprite.preloadImages images
+preloadImages()


:fire:

:fire:

--- spec/text-editor-component-spec.coffee
+++ spec/text-editor-component-spec.coffee
@@ -383,7 +383,6 @@

       newLineNodes = componentNode.querySelectorAll(".line")
       expect(oldLineNodes).not.toEqual(newLineNodes)
-      oldLineNodes = newLineNodes

       wrapperNode.setContinuousReflow(false)
       advanceClock(component.presenter.minimumReflowInterval)
@@ -839,7 +838,6 @@

       newLineNodes = componentNode.querySelectorAll(".line-number")
       expect(oldLineNodes).not.toEqual(newLineNodes)
-      oldLineNodes = newLineNodes

       wrapperNode.setContinuousReflow(false)
       advanceClock(component.presenter.minimumReflowInterval)


Fix text-editor-component spec

Fix text-editor-component spec

--- spec/text-editor-component-spec.coffee
+++ spec/text-editor-component-spec.coffee
@@ -1284,7 +1284,6 @@
       item = document.createElement('div')
       item.classList.add 'overlay-test'
       item.style.background = 'red'
-
       gutterWidth = componentNode.querySelector('.gutter').offsetWidth

     describe "when the marker is empty", ->
@@ -1302,29 +1301,6 @@
         overlay = component.getTopmostDOMNode().querySelector('atom-overlay .overlay-test')
         expect(overlay).toBe null

-      it "renders in the correct position on initial display and when the marker moves", ->
-        editor.setCursorBufferPosition([2, 5])
-
-        marker = editor.getLastCursor().getMarker()
-        decoration = editor.decorateMarker(marker, {type: 'overlay', item})
-        nextAnimationFrame()
-        nextAnimationFrame()
-
-        position = wrapperNode.pixelPositionForBufferPosition([2, 5])
-
-        overlay = component.getTopmostDOMNode().querySelector('atom-overlay')
-        expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
-        expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
-
-        editor.moveRight()
-        editor.moveRight()
-        nextAnimationFrame()
-
-        position = wrapperNode.pixelPositionForBufferPosition([2, 7])
-
-        expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
-        expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
-
     describe "when the marker is not empty", ->
       it "renders at the head of the marker by default", ->
         marker = editor.displayBuffer.markBufferRange([[2, 5], [2, 10]], invalidate: 'never')
@@ -1387,14 +1363,6 @@

         expect(overlay.style.left).toBe windowWidth - itemWidth + 'px'
         expect(overlay.style.top).toBe position.top + editor.getLineHeightInPixels() + 'px'
-        editor.insertNewline()
-        nextAnimationFrame()
-
-        position = wrapperNode.pixelPositionForBufferPosition([5, 0])
-
-        expect(overlay.style.left).toBe position.left + gutterWidth + 'px'
-        expect(overlay.style.top).toBe position.top - itemHeight + 'px'
-

   describe "hidden input field", ->
     it "renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused", ->


:lipstick: Use a more explicit method name

--- lib/color-project.coffee
+++ lib/color-project.coffee
@@ -21,7 +21,7 @@
   loadVariables: ->
     new Promise (resolve, reject) =>
       @loadPaths().then (paths) =>
-        @scanPaths paths, (results) =>
+        @scanPathsForVariables paths, (results) =>
           @variables = results
           resolve(results)

@@ -49,11 +49,11 @@

   loadVariablesForFile: (path) ->
     new Promise (resolve, reject) =>
-      @scanPaths [path], (results) =>
+      @scanPathsForVariables [path], (results) =>
         @variables = @variables.concat(results)
         resolve(results)

-  scanPaths: (paths, callback) ->
+  scanPathsForVariables: (paths, callback) ->
     PathsScanner.startTask paths, (results) ->
       res.relativePath = atom.project.relativize(res.path) for res in results
       callback(results)


Disable cmd history in run dialog

Disable cmd history in run dialog

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -183,25 +183,25 @@
                 localStorage.setItem(@localStorageCommandLineInputKeyPrefix + "historyIndex", 0);
                 localStorage.setItem(@localStorageCommandLineInputKeyPrefix + "historyLength", commands.length);

-                $('#commandLineInput').keydown (e) =>
-                    inc = 0
-                    switch e.which
-                      when 38
-                          inc = 1 # up
-                      when 40
-                          inc = -1 # down
-                      else
-                          return
-                    index = parseInt(localStorage.getItem(@localStorageCommandLineInputKeyPrefix + 'historyIndex'))
-                    length = parseInt(localStorage.getItem(@localStorageCommandLineInputKeyPrefix + 'historyLength'))
-                    index += inc
-                    if index < 0
-                        index = 0
-                    else if index > length - 1
-                        index = length - 1
-                    localStorage.setItem @localStorageCommandLineInputKeyPrefix + 'historyIndex', index
-                    $('#commandLineInput').val commands[index]
-                    e.preventDefault()
+                # $('#commandLineInput').keydown (e) =>
+                #     inc = 0
+                #     switch e.which
+                #       when 38
+                #           inc = 1 # up
+                #       when 40
+                #           inc = -1 # down
+                #       else
+                #           return
+                #     index = parseInt(localStorage.getItem(@localStorageCommandLineInputKeyPrefix + 'historyIndex'))
+                #     length = parseInt(localStorage.getItem(@localStorageCommandLineInputKeyPrefix + 'historyLength'))
+                #     index += inc
+                #     if index < 0
+                #         index = 0
+                #     else if index > length - 1
+                #         index = length - 1
+                #     localStorage.setItem @localStorageCommandLineInputKeyPrefix + 'historyIndex', index
+                #     $('#commandLineInput').val commands[index]
+                #     e.preventDefault()

             callback: (data) =>
                 @data = data


Add Portuguese

Add Portuguese

--- app/main.coffee
+++ app/main.coffee
@@ -10,6 +10,7 @@
   translations:
     en: label: 'English', strings: enUs
     es: label: 'Español'
+    pt: label: 'Português'

 languageManager.on 'change-language', (e, code, strings) ->
   t7e.load strings


Use upgrade instead of install text

Use upgrade instead of install text

--- lib/package-update-view.coffee
+++ lib/package-update-view.coffee
@@ -80,8 +80,8 @@
     @status.destroyTooltip()
     switch iconName
       when 'check'
-        @status.setTooltip(_.capitalize("#{@type} installed"))
+        @status.setTooltip(_.capitalize("#{@type} upgrade"))
       when 'alert'
-        @status.setTooltip(_.capitalize("#{@type} failed to install"))
+        @status.setTooltip(_.capitalize("#{@type} failed to upgrade"))
       when 'cloud-download'
-        @status.setTooltip(_.capitalize("#{@type} installing"))
+        @status.setTooltip(_.capitalize("#{@type} upgraded"))


Refresh the clients when .i18n.json files change

--- lib/plugin/compile-i18n.coffee
+++ lib/plugin/compile-i18n.coffee
@@ -1,11 +1,17 @@
 _ = Npm.require 'underscore'
 path = Npm.require 'path'
 fs = Npm.require 'fs'
+crypto = Npm.require 'crypto'

 project_root = process.cwd()

 langauges_tags_regex = globals.langauges_tags_regex
 fallback_language = globals.fallback_language
+
+sha1 = (contents) ->
+  hash = crypto.createHash('sha1')
+  hash.update(contents)
+  hash.digest('hex')

 # package-tap.i18n Schemas
 packageTapI18nSchema =
@@ -553,6 +559,18 @@
   # Build the project files
   buildFilesOnce compileStep

+  # Add the sha1 of the .i18n.json files as a comment to the build js, so
+  # Meteor will know when it was changed and will refresh the clients.
+  # This is needed because (except for the fallback language) the content of
+  # the .i18n.json files isn't added to the built js and therefore Meteor can't
+  # tell when they are changed, and won't refresh the client unless we'll
+  # indicate that.
+  compileStep.addJavaScript
+    path: compileStep.inputPath,
+    sourcePath: compileStep.inputPath,
+    data: "// #{compileStep.inputPath}: #{sha1(fs.readFileSync compileStep._fullInputPath)}",
+    bare: true
+
   # See the above note titled: templatesRegistrationsNeeded and the process of
   # tap-i18n package-specific templates registration
   if templatesRegistrationsNeeded


Run processes detached and kill the process group

--- lib/process/lein-runner.coffee
+++ lib/process/lein-runner.coffee
@@ -35,13 +35,13 @@
         leinExec = path.join(leinPath, "lein.bat")
       envPath = filteredEnv["Path"] || ""
       filteredEnv["Path"] = envPath + path.delimiter + leinPath
-      replProcess = childProcess.spawn leinExec, args, cwd: currentWorkingDir, env: filteredEnv, shell: true
+      replProcess = childProcess.spawn leinExec, args, cwd: currentWorkingDir, env: filteredEnv, shell: true, detached: true
     else
       # Mac/Linux
       leinExec = "lein"
       envPath = filteredEnv["PATH"] || ""
       filteredEnv["PATH"] = envPath + path.delimiter + leinPath
-      replProcess = childProcess.spawn leinExec, args, cwd: currentWorkingDir, env: filteredEnv
+      replProcess = childProcess.spawn leinExec, args, cwd: currentWorkingDir, env: filteredEnv, detached: true

     replProcess.stdout.on 'data', processData
     replProcess.stderr.on 'data', processData
@@ -62,6 +62,6 @@
         when 'input'
           replProcess.stdin.write(text)
         when 'kill'
-          replProcess.kill("SIGKILL")
+          process.kill(-replProcess.pid, 'SIGKILL')
     catch error
       console.error error


:art: Render view if EditorLinter messages are updated programatically

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -39,9 +39,11 @@

   deleteMessages: (linter) ->
     @_messages.delete(linter)
+    @linter.views.render() if @editor is atom.workspace.getActiveTextEditor()

   setMessages: (linter, messages) ->
     @_messages.set(linter, Helpers.validateResults(messages))
+    @linter.views.render() if @editor is atom.workspace.getActiveTextEditor()

   # Called on package deactivate
   destroy: ->
@@ -82,7 +84,6 @@
           @linter.setProjectMessages(linter, results)
         else
           @setMessages(linter, results)
-          @linter.views.render() if @editor is atom.workspace.getActiveTextEditor()
           @_emitter.emit 'did-update'
       ).catch (error) ->
         atom.notifications.addError error.message, {detail: error.stack, dismissable: true}


fix undef `el`

fix undef `el`

--- lib/editor/result.coffee
+++ lib/editor/result.coffee
@@ -74,9 +74,8 @@
       class: 'ink-underlay'
       invalidate: 'never'
     @expDecoration = @editor.decorateMarker @expMarker, mark
-    el = @editor.editorElement
     setTimeout (=>
-      elRect = el.getBoundingClientRect()
+      elRect = @editor.editorElement.getBoundingClientRect()
       w = elRect.width + elRect.left - 40 -
           @view.parentElement.getBoundingClientRect().left
       if w < 100 then w = 100
@@ -87,7 +86,7 @@
     @expMarker?.destroy()
     @decorateMarker()
     setTimeout (=>
-      elRect = el.getBoundingClientRect()
+      elRect = @editor.editorElement.getBoundingClientRect()
       w = elRect.width + elRect.left - 40 -
           @view.parentElement.getBoundingClientRect().left
       if w < 100 then w = 100


don't cache and filter suggestions

don't cache and filter suggestions

--- lib/runtime/completions.coffee
+++ lib/runtime/completions.coffee
@@ -13,7 +13,7 @@
   scopeSelector: '.source.julia'
   textEditorSelectors: 'atom-text-editor'
   labels: ['workspace-center']
-  filterSuggestions: true
+  filterSuggestions: false
   excludeLowerPriority: false

   load: ->
@@ -51,21 +51,21 @@
     return [] unless client.isActive() and @validScope data.scopeDescriptor
     if data.prefix.length < @minWordLength then return []
     cs = @rawCompletions(data).then ({completions, prefix, mod}) =>
-      return @fromCache mod, prefix if not completions?
+      # return @fromCache mod, prefix if not completions?
       @processCompletions completions, prefix
-    Promise.race([cs, @sleep(1).then(->[])])
+    cs

-  cache: {}
+  # cache: {}

-  updateCache_: (mod) ->
-    cacheCompletions(mod).then (cs) =>
-      @cache[mod] = cs
-
-  updateCache: debounce ((mod) -> @updateCache_ mod), 1000, true
-
-  fromCache: (mod, prefix) ->
-    @updateCache mod
-    @processCompletions(@cache[mod] or [], prefix)
+  # updateCache_: (mod) ->
+  #   cacheCompletions(mod).then (cs) =>
+  #     @cache[mod] = cs
+  #
+  # updateCache: debounce ((mod) -> @updateCache_ mod), 1000, true
+  #
+  # fromCache: (mod, prefix) ->
+  #   @updateCache mod
+  #   @processCompletions(@cache[mod] or [], prefix)

   onDidInsertSuggestion: ({editor, suggestion: {type}}) ->
     if type is 'function'


Notification: Notification text fix

Notification: Notification text fix

--- client/Main/CommonViews/messagesList.coffee
+++ client/Main/CommonViews/messagesList.coffee
@@ -188,7 +188,7 @@
           return callback err if err or not origin?
           originatorName = KD.utils.getFullnameFromAccount origin
           @activityPlot = if post.originId is KD.whoami()?.getId() then "your"
-          else if @group.length == 1 and @group[0].id is origin.getId() then "their"
+          else if @group.length == 1 and @group[0].id is origin.getId() then "their own"
           else "#{originatorName}'s"
           @activityPlot += " #{activityNameMap[constructorName]}"
           callback null


Tests: Teams: Helpers: Added createInvitation helper.

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -143,6 +143,37 @@
     @loginToTeam(browser, user)

     return user
+
+
+  createInvitation: (browser, user, callback) ->
+
+    adminLink      = '.avatararea-popup a[href="/Admin"]'
+    inviteLink     = '.teaminvite.AppModal-navItem'
+    teamInvitePage = '.TeamInvite'
+    inviteButton   = "#{teamInvitePage} button"
+    sendMailPage   = "#{teamInvitePage} .kdscrollview"
+    sendMailButton = "#{teamInvitePage} button.green:not(.hidden)"
+    notification   = '.kdnotification.main'
+
+    helpers.openAvatarAreaModal(browser)
+    browser
+      .waitForElementVisible  adminLink, 20000
+      .click                  adminLink
+      .waitForElementVisible  inviteLink, 20000
+      .click                  inviteLink
+      .waitForElementVisible  teamInvitePage, 20000
+      .setValue               "#{teamInvitePage} textarea.text", user.email
+      .waitForElementVisible  inviteButton, 20000
+      .click                  inviteButton
+      .waitForElementVisible  sendMailPage, 20000
+      .assert.containsText    sendMailPage, user.email # Assertion
+      .waitForElementVisible  sendMailButton, 20000
+      .click                  sendMailButton
+      .waitForElementVisible  notification, 20000
+      .assert.containsText    notification, 'Invitations sent!'
+      .pause                  2000
+      .getAttribute           "#{sendMailPage} a" , 'href', (result) ->
+        callback result.value

   openTeamSettingsModal: (browser) ->


Removing unnecessary selectors

Removing unnecessary selectors

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -641,9 +641,6 @@

   createPrivateChat: (browser) ->

-    sidebarModal                = ".kdview.with-sidebar.desktop .Sidebar-ReactView .SidebarSection.SidebarMessagesSection .SidebarSection-header"
-    sidebarSelector             = "#{sidebarModal}"
-    createMessageButton         = "#{sidebarModal} .SidebarSection-secondaryLink"
     emptyInviteMembersInputText = '.CreateChannel-Modal .CreateChannel-content .channelName input'
     createChatButton            = '.CreateChannel-Modal .Modal-buttons button:first-child'
     markedAsInvalidInputText    = '.CreateChannel-content .channelName.invalid'


Add in null check for user when getting hue in chat

--- services/web/public/coffee/ide/chat/controllers/ChatMessageController.coffee
+++ services/web/public/coffee/ide/chat/controllers/ChatMessageController.coffee
@@ -4,5 +4,8 @@
 ], (App, ColorManager) ->
 	App.controller "ChatMessageController", ["$scope", "ide", ($scope, ide) ->
 		$scope.hue = (user) ->
-			ColorManager.getHueForUserId(user.id)
+			if !user?
+				return 0
+			else
+				return ColorManager.getHueForUserId(user.id)
 	]


added a menu and context menu entry for typechecking

--- menus/language-idris.cson
+++ menus/language-idris.cson
@@ -1,8 +1,29 @@
+'menu':
+  [
+    {
+      'label': 'Packages'
+      'submenu':
+        [
+          'label': 'Idris'
+          'submenu':
+            [
+              {
+                'label': 'Typecheck'
+                'command': 'language-idris:typecheck'
+              }
+            ]
+        ]
+    }
+  ]
 'context-menu':
   'atom-text-editor[data-grammar~=\"idris\"]': [
     {
       'label': 'Idris'
       'submenu': [
+        {
+          'label': 'Typecheck'
+          'command': 'language-idris:typecheck'
+        }
         {
           'label': 'Type search'
           'command': 'language-idris:type-of'


fix message handler

fix message handler

--- scripts/sns-notifications.coffee
+++ scripts/sns-notifications.coffee
@@ -1,7 +1,11 @@
 module.exports = (robot) ->
+  # Handle all notifications
   robot.on "sns:notification", (msg) ->
-    """ Received notification:
-        TopicArn: #{msg.topicArn}
-        Topic: #{msg.topic} Message Id: #{msg.messageId}
-        Subject: #{msg.subject}
-        Message: #{msg.message} """+    """
+    Received notification:
+      TopicArn:   #{msg.topicArn}
+      Topic:      #{msg.topic}
+      Message Id: #{msg.messageId}
+      Subject:    #{msg.subject}
+      Message:    #{msg.message}
+    """


remove debug logs

remove debug logs

--- server/mails/mail_handler.coffee
+++ server/mails/mail_handler.coffee
@@ -40,16 +40,12 @@
         needSaving = false
         CozyInstance.getURL (err, domain) =>
             if err
+                log.error 'Cannot get Cozy instance'
                 console.log err.stack
                 return callback()

             async.forEach guests, (guest, cb) =>
                 ismail = guest.status is 'INVITATION-NOT-SENT' or (guest.status is 'ACCEPTED' and dateChanged)
-                console.log dateChanged
-
-                console.log guest
-
-                console.log ismail

                 if guest.status is 'INVITATION-NOT-SENT' or
                 (guest.status is 'ACCEPTED' and dateChanged)


Possibility to open an image without inserting it immediately in the document

--- lib/mixins/images.coffee
+++ lib/mixins/images.coffee
@@ -9,24 +9,27 @@
     if typeof x is 'object'
       options = x
       x = null
-
+
     x = x ? options.x ? @x
     y = y ? options.y ? @y
-
+
     unless Buffer.isBuffer(src)
       image = @_imageRegistry[src]
-
+
     if not image
-      image = PDFImage.open src, 'I' + (++@_imageCount)
+      if src.width and src.height
+        image = src
+      else
+        image = @openImage src
+
+    unless image.obj
       image.embed this
-      unless Buffer.isBuffer(src)
-        @_imageRegistry[src] = image
-
+
     @page.xobjects[image.label] ?= image.obj

     w = options.width or image.width
     h = options.height or image.height
-
+
     if options.width and not options.height
       wp = w / image.width
       w = image.width * wp
@@ -68,12 +71,12 @@
         x = x + bw / 2 - w / 2
       else if options.align is 'right'
         x = x + bw - w
-
+
       if options.valign is 'center'
         y = y + bh / 2 - h / 2
       else if options.valign is 'bottom'
         y = y + bh - h
-
+
     # Set the current y position to below the image if it is in the document flow
     @y += h if @y is y

@@ -81,5 +84,16 @@
     @transform w, 0, 0, -h, x, y + h
     @addContent "/#{image.label} Do"
     @restore()
-
+
     return this
+
+  openImage: (src) ->
+    unless Buffer.isBuffer(src)
+      image = @_imageRegistry[src]
+
+    if not image
+      image = PDFImage.open src, 'I' + (++@_imageCount)
+      unless Buffer.isBuffer(src)
+        @_imageRegistry[src] = image
+
+    return image


Fix lang

Fix lang

--- server/ssr/renderOnServer.coffee
+++ server/ssr/renderOnServer.coffee
@@ -108,9 +108,9 @@
     if cookie
         return cookie
     else if host == "algoprog.org"
+        return "en"
+    else
         return "ru"
-    else
-        return "en"

 export default renderOnServer = (linkClientJsCss) => (req, res, next) =>
     # https://github.com/HenningM/express-ws/issues/64


spam flickr if it returns no photos

spam flickr if it returns no photos

--- assets/js/app.coffee
+++ assets/js/app.coffee
@@ -11,7 +11,7 @@
     #console.log page
     $.get '/load_new_photos?page='+page, (resp) ->
       if resp.length == 0
-        if reqs_count < 5
+        if reqs_count < 3
           reqs_count += 1
           get_new_photos()
         else


add className to partial div

add className to partial div

--- lib/assets/javascripts/stark/view_partial.js.coffee
+++ lib/assets/javascripts/stark/view_partial.js.coffee
@@ -49,7 +49,8 @@
     # unless first element is a tr, wrap in a span
     first = @$el.children().first()
     if @wrap && first && first[0].tagName != "TR"
-      @$el.html( "
" + @$el.html() + "
" )
+      c = @className || ""
+      @$el.html( "
" + @$el.html() + "
" )
     else
       first.attr("id", @id)


Upgrade stage hunk file view

Upgrade stage hunk file view

--- lib/views/select-stage-hunk-file-view.coffee
+++ lib/views/select-stage-hunk-file-view.coffee
@@ -1,4 +1,5 @@
-{$$, BufferedProcess, SelectListView} = require 'atom'
+{BufferedProcess} = require 'atom'
+{$$, SelectListView} = require 'atom-space-pen-views'
 SelectStageHunks = require './select-stage-hunks-view'
 git = require '../git'

@@ -7,13 +8,21 @@

   initialize: (items) ->
     super
-    @addClass 'overlay from-top'
+    @show()

     @setItems items
-    atom.workspaceView.append this
     @focusFilterEditor()

   getFilterKey: -> 'path'
+
+  show: ->
+    @panel ?= atom.workspace.addModalPanel(item: this)
+    @panel.show()
+
+  cancelled: -> @hide()
+
+  hide: ->
+    @panel?.hide()

   viewForItem: (item) ->
     $$ ->


Updated create schema to reflect object style for files

--- lib/schema/create.coffee
+++ lib/schema/create.coffee
@@ -1,22 +1,24 @@
 module.exports =
   type: "object"
+  additionalProperties: false
   properties:
     description:
       type: "string"
     index:
       type: "string"
       default: "index.html"
+    additionalProperties: false
     files:
       required: true
-      type: [
-        type: "array"
-        minItems: 1
-        items:
+      type: "object"
+      minProperties: 1
+      additionalProperties:
+        type: [
+          type: "string"
+        ,
           type: "object"
+          additionalProperties: false
           properties:
-            filename:
-              type: "string"
-              required: true
             content:
               type: "string"
               required: true
@@ -24,9 +26,4 @@
               type: "string"
             encoding:
               type: "string"
-      ,
-        type: "object"
-        minProperties: 1
-        items:
-          type: "string"
-      ]+        ]


add a fix for loader

add a fix for loader

--- BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
@@ -36,7 +36,7 @@
           links: viewContext.options.elements.get('links')
           cid: viewContext.cid
         )
-        viewContext.options.domContainer.append viewContext.$el
+        viewContext.options.domContainer.html viewContext.$el
     $('.js-widget-title', @options.domContainer).text @options.title
     for element of @options.elements.get(@options.elements.get('collection_name'))
       @addElementToView (@options.elements.get(@options.elements.get('collection_name'))[element])


Fixed time zone issues with date.

Fixed time zone issues with date.

--- app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
@@ -292,11 +292,11 @@
           unixtime = new Date( Date.parse( "#{attribute.value}T00:00:00Z" ) )
         else
           unixtime = new Date( attribute.value )
-        year     = unixtime.getYear() + 1900
-        month    = unixtime.getMonth() + 1
-        day      = unixtime.getDate()
-        hour     = unixtime.getHours()
-        minute   = unixtime.getMinutes()
+        year     = unixtime.getUTCFullYear()
+        month    = unixtime.getUTCMonth() + 1
+        day      = unixtime.getUTCDate()
+        hour     = unixtime.getUTCHours()
+        minute   = unixtime.getUTCMinutes()
       item = $( App.view('generic/date')(
         attribute: attribute
         year:      year
@@ -322,10 +322,10 @@
           time = new Date( Date.parse( "#{year}-#{format(month)}-#{format(day)}T00:00:00Z" ) )
         else
           time = new Date()
-        time.setMinutes( time.getMinutes() + diff + time.getTimezoneOffset() )
-        item.closest('.form-group').find("[name=\"{date}#{name}___day\"]").val( time.getDate() )
-        item.closest('.form-group').find("[name=\"{date}#{name}___month\"]").val( time.getMonth()+1 )
-        item.closest('.form-group').find("[name=\"{date}#{name}___year\"]").val( time.getFullYear() )
+        #time.setMinutes( time.getMinutes() + diff + time.getTimezoneOffset() )
+        item.closest('.form-group').find("[name=\"{date}#{name}___day\"]").val( time.getUTCDate() )
+        item.closest('.form-group').find("[name=\"{date}#{name}___month\"]").val( time.getUTCMonth()+1 )
+        item.closest('.form-group').find("[name=\"{date}#{name}___year\"]").val( time.getUTCFullYear() )

       item.find('.js-today').bind('click', (e) ->
         e.preventDefault()
@@ -430,7 +430,7 @@
           unixtime = new Date( Date.parse( attribute.value ) )
         else
           unixtime = new Date( attribute.value )
-        year     = unixtime.getYear() + 1900
+        year     = unixtime.getFullYear()
         month    = unixtime.getMonth() + 1
         day      = unixtime.getDate()
         hour     = unixtime.getHours()
@@ -2325,11 +2325,11 @@

         if !param[ namespace[0] ]
           dateKey = "{date}#{namespace[0]}___"
-          year        = param[ "#{dateKey}year" ]
-          month       = param[ "#{dateKey}month" ]
-          day         = param[ "#{dateKey}day" ]
-          timezone    = (new Date()).getTimezoneOffset()/60
-          if lookupForm.find('[data-name="' + namespace[0] + '"]').hasClass('is-hidden')
+          year    = param[ "#{dateKey}year" ]
+          month   = param[ "#{dateKey}month" ]
+          day     = param[ "#{dateKey}day" ]
+
+          if lookupForm.find('[data-name = "' + namespace[0] + '"]').hasClass('is-hidden')
             param[ namespace[0] ] = null
           else if year && month && day && day
             format = (number) ->
@@ -2340,8 +2340,7 @@
               time = new Date( Date.parse( "#{year}-#{format(month)}-#{format(day)}T00:00:00Z" ) )
               if time && time.toString() is 'Invalid Date'
                 throw "Invalid Date #{year}-#{format(month)}-#{format(day)}"
-              time.setMinutes( time.getMinutes() + time.getTimezoneOffset() )
-              param[ namespace[0] ] = "#{time.getFullYear()}-#{format(time.getMonth()+1)}-#{format(time.getDate())}"
+              param[ namespace[0] ] = "#{time.getUTCFullYear()}-#{format(time.getUTCMonth()+1)}-#{format(time.getUTCDate())}"
             catch err
               param[ namespace[0] ] = undefined
               console.log('ERR', err)
@@ -2366,7 +2365,7 @@
           day         = param[ "#{datetimeKey}day" ]
           hour        = param[ "#{datetimeKey}hour" ]
           minute      = param[ "#{datetimeKey}minute" ]
-          timezone    = (new Date()).getTimezoneOffset()/60
+
           if lookupForm.find('[data-name="' + namespace[0] + '"]').hasClass('is-hidden')
             param[ namespace[0] ] = null
           else if year && month && day && hour && minute


set exit code on tester

set exit code on tester

--- tests/support/tester.coffee
+++ tests/support/tester.coffee
@@ -163,6 +163,8 @@

   log line + line

+  exit_code = 0
+
   if failed is 0 and timedout is 0 and errors is 0 and uncaught.length is 0
     print "#{green}All passing#{reset} | "
   else
@@ -170,11 +172,12 @@
     print "#{red}#{timedout} not reached#{reset} | " if timedout > 0
     print "#{red}#{errors} error(s)#{reset} | " if errors > 0
     print "#{red}#{uncaught.length} uncaught error(s)#{reset} | " if uncaught.length > 0
+    exit_code = 1

   print "#{_(tests).size()} tests | "
   log "#{new Date - start} ms\n"

-  process.exit()
+  process.exit exit_code

 process.on 'uncaughtException', (err) ->
   uncaught.push err


Introducing state machine

Introducing state machine

--- bin/heyu-run.coffee
+++ bin/heyu-run.coffee
@@ -42,6 +42,14 @@
   appl.display_state()
   appl.switchOff()
   appl.display_state()
+  appl2 = new Appl()
+  print "HERE"
+  print appl2.availableStates()
+  appl2.display_state()
+  appl2.switchOn()
+  appl2.display_state()
+  appl2.switchOff()
+  appl2.display_state()
   print 'Tests passed'

 # ---- Parse the command line


input pipe stream can't be default, as there is no way to tell when there is none

--- bin/precious.coffee
+++ bin/precious.coffee
@@ -77,6 +77,21 @@
 args = process.argv.splice(2)
 if args.length > 0 then switch args[0]

+  # Stream pipe, unix style.
+  when '-s', '--stream'
+    process.stdin.resume()
+    process.stdin.setEncoding("utf8")
+
+    # A good parser.
+    parser = JSONStream.parse /./
+    process.stdin.pipe parser
+
+    parser.on "data", (data) ->
+      (ephemeris convenient data).stdout.pipe process.stdout
+
+
+  # The rest of the options are just for convenience / variety.
+
   when '-f', '--file'
     unless args[1]?
       console.error "Usage: precious -f <file>".red
@@ -101,18 +116,9 @@
     else man "precious"

   else man "precious", 1, ->
-    console.error "Usage confusion -- some help is above.\n".red
+    console.error "Usage confusion, see help above.\n".red

+# Another way to get help.
+# It's common to type a command by itself and expect some usage info.
+else man "precious"

-# Stream pipe expected as default.
-else
-  process.stdin.resume()
-  process.stdin.setEncoding("utf8")
-
-  # A good parser.
-  parser = JSONStream.parse /./
-  process.stdin.pipe parser
-
-  parser.on "data", (data) ->
-    (ephemeris convenient data).stdout.pipe process.stdout
-


Workspace: Defensive coding for editor save req.

--- client/landing/app/CommonViews/workspace/panes/collaborativeeditorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativeeditorpane.coffee
@@ -19,7 +19,9 @@
           @codeMirrorEditor.scrollTo 0, 0

       @ref.on "value", (snapshot) =>
-        return @save()  if snapshot.val().WaitingSaveRequest is yes
+        value = snapshot.val()
+        return unless value
+        return @save()  if value.WaitingSaveRequest is yes

       @ref.onDisconnect().remove()  if @amIHost


Include updateFill on init when explicitly setting the value within the constructor

--- framer/Components/SliderComponent.coffee
+++ framer/Components/SliderComponent.coffee
@@ -53,6 +53,7 @@
 		@knob.borderRadius = "50%"

 		@_updateFrame()
+		@_updateFill()

 		@on("change:frame", @_updateFrame)
 		@on("change:borderRadius", @_setRadius)


Allow deleting plans

Allow deleting plans

--- workers/social/lib/social/models/recurly/index.coffee
+++ workers/social/lib/social/models/recurly/index.coffee
@@ -88,6 +88,11 @@
     data.code       = "groupplan_#{group._id}_#{data.name}_0"

     payment.addPlan data, callback
+
+  @deleteGroupPlan = (group, data, callback)->
+    prefix = "groupplan_#{group._id}_"
+    if data.code.indexOf(prefix) > -1
+      payment.deletePlan data, callback

   @getPlans = secure (client, filter..., callback)->
     [prefix, category, item] = filter


add localization plumbings to the modals

--- library/semantic/modals.coffee
+++ library/semantic/modals.coffee
@@ -26,6 +26,7 @@
 {Zombie} = require "../nucleus/zombie"
 {external} = require "../membrane/remote"
 {Preflight} = require "../membrane/preflight"
+{Localized} = require "../exposure/localized"
 {GoogleFonts} = require "../exposure/fonting"

 # This is an abstract base class compound that exhibits the zombie
@@ -48,6 +49,7 @@
     # Try not to put constraints on the domain, unless necessary.
     # Also, the compounds for the composition system belong here.
     @compose Preflight
+    @compose Localized

     # This method is invoked right after the window skeleton has
     # been created. It takes care of filling in the actions segment
@@ -63,8 +65,8 @@
         $(@negative).click (e) => @emit "negative"; stop e
         $(@positive).click (e) => @emit "positive"; stop e
         @positive.append $ "<i>", class: "checkmark icon"
-        this.negative.prepend $("<span>").text "dismiss"
-        this.positive.prepend $("<span>").text "confirm"
+        this.negative.prepend $("<span>").text @t "dismiss"
+        this.positive.prepend $("<span>").text @t "confirm"
         assert @positive.addClass "right labeled icon"
         assert @actions.append @negative, @positive
         @emit "actions", @actions, @window; this


Add change log entry for 2.0.1

Add change log entry for 2.0.1

--- src/coffee/cilantro/changelog.coffee
+++ src/coffee/cilantro/changelog.coffee
@@ -5,6 +5,9 @@
 define ->

     return [
+        version: '2.0.1'
+        changes: []
+    ,
         version: '2.0.0'
         changes: [
             summary: 'Initial Release'


small improvements

small improvements

--- src/coffee/message_processing.coffee
+++ src/coffee/message_processing.coffee
@@ -20,12 +20,10 @@
     @sourceProjects = util.parseProjectsCredentials @credentialsConfig, @argv.sourceProjects

     sphereServicesPs = _.map @sourceProjects, (project) =>
-      project.user_agent = @processorName
-
       messageCriteria =
         if @messageType?
-          ids = if ids? then " and id in (#{_.map(ids, (id) -> '"' + id + '"').join(', ')})" else ''
-          "resource(typeId=\"#{@messageType}\"#{ids})"
+          idsQuery = if _.isArray(ids) then " and id in (#{_.map(ids, (id) -> '"' + id + '"').join(', ')})" else ''
+          "resource(typeId=\"#{@messageType}\"#{idsQuery})"
         else
           @messageCriteria

@@ -39,6 +37,7 @@
         processorName: @processorName
         logger: @rootLogger
         connector:
+          user_agent: @processorName
           config: project

     Q.all sphereServicesPs


reverting footer button related changes in favor of jing's #509

--- src/components/task-plan/footer.cjsx
+++ src/components/task-plan/footer.cjsx
@@ -11,10 +11,6 @@
   propTypes:
     id: React.PropTypes.string.isRequired
     courseId: React.PropTypes.string.isRequired
-
-  getInitialState: ->
-    saving: false
-    publishing: false

   saved: ->
     courseId = @props.courseId
@@ -37,16 +33,8 @@
     {id, courseId} = @props
     @context.router.transitionTo('viewStats', {courseId, id})

-  onPublish: ->
-    @setState(publishing: true)
-    @props.onPublish()
-
-  onSave: ->
-    @setState(saving: true)
-    @props.onSave()
-
   render: ->
-    {id, courseId, clickedSelectProblem} = @props
+    {id, courseId, clickedSelectProblem, onPublish, onSave} = @props

     plan = TaskPlanStore.get(id)

@@ -59,9 +47,8 @@
         <AsyncButton
           bsStyle='primary'
           className='-publish'
-          disabled={isWaiting}
-          onClick={@onPublish}
-          isWaiting={isWaiting and @state.publishing}
+          onClick={onPublish}
+          isWaiting={isWaiting}
           isFailed={isFailed}
           waitingText='Publishing…'
           >
@@ -76,9 +63,8 @@
       saveLink =
           <AsyncButton
             className='-save'
-            disabled={isWaiting}
-            onClick={@onSave}
-            isWaiting={isWaiting and @state.saving}
+            onClick={onSave}
+            isWaiting={isWaiting}
             isFailed={isFailed}
             waitingText='Saving…'
             >


Implemented archivedSites create method; Fixed comments referring to posts

--- src/controllers/archivedSites.coffee
+++ src/controllers/archivedSites.coffee
@@ -2,19 +2,28 @@

 # ArchivedSite model's CRUD controller.
 module.exports =
-  # Lists all posts
+  # Lists all archivedSites
   index: (req, res) ->
     ArchivedSite.find {}, (err, sites) ->
       res.send sites

-  # Creates new post with data from `req.body`
+  # Creates new archivedSite with data from `req.body`
   create: (req, res) ->
+    site = new ArchivedSite req.body
+    site.save (err, site) ->
+      if not err
+        res.send site
+        res.statusCode = 201
+      else
+        res.send err
+        res.statusCode = 500

-  # Gets post by id
+
+  # Gets archivedSite by id
   get: (req, res) ->

-  # Updates post with data from `req.body`
+  # Updates archivedSite with data from `req.body`
   update: (req, res) ->

-  # Deletes post by id
+  # Deletes archivedSite by id
   delete: (req, res) ->