Update ja.coffee (#4960)

Update ja.coffee (#4960)

Merged the previous PR's into one. Finished most teacher translations and all HOC translations

--- app/locale/ja.coffee
+++ app/locale/ja.coffee
@@ -56,12 +56,14 @@
 #    courses_available_in: "Courses are available in JavaScript and Python. Web Development courses utilize HTML, CSS, and jQuery."
 #    boast: "Boasts riddles that are complex enough to fascinate gamers and coders alike."
 #    winning: "A winning combination of RPG gameplay and programming homework that pulls off making kid-friendly education legitimately enjoyable."
-#    run_class: "Everything you need to run a computer science class in your school today, no CS background required."
-#    goto_classes: "Go to My Classes"
-#    view_profile: "View My Profile"
+    run_class: "すべてCSクラスに必要なものを今日教えませんか？CSの知識は無くても大丈夫です。"
+    goto_classes: "クラスを見る"
+    view_profile: "プロファイルを見る"
 #    view_progress: "View Progress"
 #    go_to_courses: "Go to My Courses"
-#    want_coco: "Want CodeCombat at your school?"
+    want_coco: "学校でCodeCombat使って見ませんか？"
+
+

   nav:
     map: "マップ"
@@ -119,8 +121,8 @@

   play:
     play_as: "プレイモード" # Ladder page
-#    get_course_for_class: "Assign Game Development and more to your classes!"
-#    request_licenses: "Contact our school specialists for details."
+    get_course_for_class: "ゲームデベロプメントや様々なことをクラスに教えよう！"
+    request_licenses: "詳しくはスペシアリストへ"
     compete: "コンプリート!" # Course details page
     spectate: "観戦" # Ladder page
     players: "プレイヤー" # Hover over a level on /play
@@ -138,12 +140,12 @@
     poll: "投票" # Tooltip on poll button from /play
     next: "次へ" # Go from choose hero to choose inventory before playing a level
     change_hero: "ヒーローの選択" # Go back from choose inventory to choose hero
-#    change_hero_or_language: "Change Hero or Language"
+    change_hero_or_language: "へーローやプラグラミングを変える"
     buy_gems: "ジェムを購入"
     subscribers_only: "サブスクリプション限定!"
     subscribe_unlock: "サブスクリプションで解放!"
-#    subscriber_heroes: "Subscribe today to immediately unlock Amara, Hushbaum, and Hattori!"
-#    subscriber_gems: "Subscribe today to purchase this hero with gems!"
+    subscriber_heroes: "いまサブスクライブして、Amara, Hushbaum, と Hattori Hanzōをアンロックしよう！"
+    subscriber_gems: "いまサブスクライブしてジェムを使ってこのヒーローをアンロックしよう！"
     anonymous: "名無しのプレイヤー"
     level_difficulty: "難易度: "
     awaiting_levels_adventurer_prefix: "私たちは毎週新しいレベルをリリースします"
@@ -247,22 +249,22 @@
     connected_gplus_p: "Google+での登録が完了しました。"
     connected_facebook_header: "Facebookとの連携に成功しました!"
     connected_facebook_p: "Facebookでの登録が完了しました。."
-#    hey_students: "Students, enter the class code from your teacher."
-#    birthday: "Birthday"
+    hey_students: "クラスコードを入力して下さい"
+    birthday: "生年月日"
 #    parent_email_blurb: "We know you can't wait to learn programming — we're excited too! Your parents will receive an email with further instructions on how to create an account for you. Email {{email_link}} if you have any questions."
 #    classroom_not_found: "No classes exist with this Class Code. Check your spelling or ask your teacher for help."
-#    checking: "Checking..."
-#    account_exists: "This email is already in use:"
-#    sign_in: "Sign in"
-#    email_good: "Email looks good!"
-#    name_taken: "Username already taken! Try {{suggestedName}}?"
-#    name_available: "Username available!"
-#    name_is_email: "Username may not be an email"
-#    choose_type: "Choose your account type:"
-#    teacher_type_1: "Teach programming using CodeCombat!"
-#    teacher_type_2: "Set up your class"
-#    teacher_type_3: "Access Course Guides"
-#    teacher_type_4: "View student progress"
+    checking: "確認しています..."
+    account_exists: "このe-mailはとられています"
+    sign_in: "Sign inする"
+    email_good: "このe-mailは使えます!"
+    name_taken: "このユーザーネームはとられています!{{suggestedName}}　を使って見ませんか？"
+    name_available: "このユーザーネームは使えます!"
+    name_is_email: "e-mailはユーザーネームに使えません"
+    choose_type: "アカウントのタイプを選んで下さい:"
+    teacher_type_1: "CodeCombatを使ってプログラミングを教えよう！"
+    teacher_type_2: "クラスをセットアップして下さい"
+    teacher_type_3: "コースガイドをアクセスする"
+    teacher_type_4: "生徒たちの進捗を見る"
 #    signup_as_teacher: "Sign up as a Teacher"
 #    student_type_1: "Learn to program while playing an engaging game!"
 #    student_type_2: "Play with your class"
@@ -2631,22 +2633,21 @@
 #    custom_message: "I just found CodeCombat and thought it'd be a great program for your classroom! It's a computer science learning platform with standards-aligned curriculum.\n\nComputer literacy is so important and I think this would be a great way to get students engaged in learning to code."
 #    send: "Send Email"

-#  hoc_2018:
-#    learn_more_copy: "CodeCombat is a comprehensive program built for all teachers. Want to take Hour of Code to the next level?"
-#    learn_more: "Learn More"
-#    page_heading: "Teach your students how to build their own arcade game!"
-#    get_started: "Get Started:"
-#    download_lesson: "Download Lesson Plan:"
-#    lesson_copy: "This PDF will take you through how to introduce and close out the Hour of Code lesson, the learning objectives, key terms and concepts, and discussion questions to inspire more coding curiosity."
-#    download_pdf: "Download PDF"
-#    ready_play: "Ready to play?"
-#    play_activity: "Play Activity"
-#    about: "About CodeCombat:"
-#    about_copy: "CodeCombat is a game-based computer science program for teaching real Python and JavaScript. CodeCombat’s standards-aligned curriculum is built around a game that students love. Over 12 million students have learned to code on CodeCombat!"
-#    point1: "✓ Scaffolded"
-#    point2: "✓ Differentiated"
-#    point3: "✓ Formative and Summative Assessments"
-#    point4: "✓ Project-based courses"
-#    point5: "✓ Student tracking"
-#    point6: "✓ Full lesson plans"
-#    request_demo: "Create Teacher Account"
+  hoc_2018:
+    learn_more_copy: "CodeCombatは全ての人のために作られた素晴らしいプログラムです。HOCを次のレベルに行きたいですか？"
+    learn_more: "もっと詳しく"
+    page_heading: "Teach your students how to build their own arcade game!生徒に自分のゲームの作り方を教えよう！"
+    get_started: "始まる:"
+    download_lesson: "レッスンプランをダウンロード:"
+    lesson_copy: "このPDFはHOCのレッスンのやり方や大事なところをじっくり教えてくれます。さらに、クラスで話し合うことがかいてあります。"
+    ready_play: "準備できた？"
+    play_activity: "アクティビティをあそぶ"
+    about: "Codecombatについて:"
+    about_copy: "CodeCombat はゲーム見たいなCSを教えてくれるプログラムです。CodeCombatはPythonとJavaScriptを教えてくれます。CodeCombatはちゃんとプログラミングコンセプトを教えながらみんながたのしめるようにできています。1.2千万人以上CodeCombatを使っています。"
+    point1: "✓ ベースが分かりやすい"
+    point2: "✓ 生徒に合うように調整する"
+    point3: "✓ コンセプトのテストもある"
+    point4: "✓ プロジェクトのためのコース"
+    point5: "✓ 生徒のプログレスがわかる"
+    point6: "✓ 全てのコンセプトを教えてくれる"
+    request_demo: "いまCSを教えよう！"


Update nl.coffee (#5162)

Update nl.coffee (#5162)

--- app/locale/nl.coffee
+++ app/locale/nl.coffee
@@ -1693,7 +1693,7 @@
     changed: "Veranderd"
     available_credits: "Beschikbare licenties"
     pending_credits: "Aangevraagde licenties"
-    empty_credits: "Licenties zijn gebruikt"
+    empty_credits: "Licenties in gebruik"
     license_remaining: "licentie over"
     licenses_remaining: "licenties over"
     one_license_used: "er is 1 licentie gebruikt" # {change}


Update ru.coffee

Update ru.coffee

--- app/locale/ru.coffee
+++ app/locale/ru.coffee
@@ -2034,26 +2034,26 @@
     boolean_and: "Булево И"
     boolean_equality: "Булева равность"
     boolean_greater_less: "Булево больше/меньше"
-#    boolean_logic_shortcircuit: "Boolean Logic Shortcircuiting"
+    boolean_logic_shortcircuit: "Сокращения в булевой логике"
     boolean_not: "Булево НЕТ"
-#    boolean_operator_precedence: "Boolean Operator Precedence"
+    boolean_operator_precedence: "Приоритеты булевых операторов"
     boolean_or: "Булево ИЛИ"
-#    bootstrap: "Bootstrap"
+    bootstrap: "Bootstrap"
     break_statements: "Оператор \"break\""
     classes: "Классы"
     continue_statements: "Оператор \"continue\""
     dom_events: "События DOM"
     dynamic_styling: "Динамические стили"
-#    event_concurrency: "Event Concurrency"
+    event_concurrency: "Параллелизм событий"
     event_data: "Данные событий"
     event_handlers: "Обработчики событий"
     for_loops: "Циклы \"for\""
     for_loops_nested: "Вложенные циклы \"for\""
     for_loops_range: "Диапазон циклов \"for\""
     functions: "Функции"
-#    game_ai: "Game AI"
+    game_ai: "Игровой ИИ"
     game_goals: "Цели игры"
-#    game_spawn: "Game Spawn"
+    game_spawn: "Генерация игры"
     graphics: "Графика"
     graphs: "Графы"
     heaps: "Кучи"
@@ -2061,7 +2061,7 @@
     if_statements: "Условные операторы"
     if_statements_nested: "Вложенные условные операторы"
     indexing: "Индексы масива"
-#    input_handling_flags: "Input Handling - Flags"
+    input_handling_flags: "Обработка ввода - флаги"
     input_handling_keyboard: "Обработка ввода - клавиатура"
     input_handling_mouse: "Обработка ввода - мышь"
     intermediate_css_rules: "Средний уровень CSS правил"
@@ -2115,7 +2115,7 @@
     alter_existing_web_pages: "Изменяй существующие веб-страницы"
     create_sharable_web_page: "Создай веб-страницу, которой можно поделиться"
     basic_input_handling: "Основы обработки ввода"
-#    basic_game_ai: "Basic Game AI"
+    basic_game_ai: "Основы игрового ИИ"
     basic_javascript: "Основы JavaScript"
     basic_event_handling: "Основы обработки событий"
     create_sharable_interactive_web_page: "Создай интерактивную веб-страницу, которой можно поделиться"
@@ -2230,18 +2230,18 @@
 #    victory_sign_up_poke: "Create a free account to save your code & be entered for a chance to win prizes!"
 #    victory_sign_up: "Sign up & be entered to win $2,500"

-#  server_error:
-#    email_taken: "Email already taken"
-#    username_taken: "Username already taken"
-
-#  esper:
-#    line_no: "Line $1: "
-#    x_not_a_function: "`$1` is not a function"
-#    type_error: "TypeError: "
-#    reference_error: "ReferenceError: "
-#    spelling_issues: "Look out for spelling issues: did you mean `$1` instead of `$2`?"
-#    capitalization_issues: "Look out for capitalization: `$1` should be `$2`."
-#    py_empty_block: "Empty $1. Put 4 spaces in front of statements inside the $2."
-#    fx_missing_paren: "If you want to call `$1` as function, you need `()`'s"
-#    unmatched_token: "Unmatched `$1`.  Every opening `$2` needs a closing `$3` to match it."
-#    unterminated_string: "Unterminated string. Add a matching `\"` at the end of your string."
+  server_error:
+    email_taken: "Данный емейл уже занят"
+    username_taken: "Данное имя пользователя уже занято"
+
+  esper:
+    line_no: "Строка $1: "
+    x_not_a_function: "`$1` не является функцией"
+    type_error: "TypeError (ошибка типа данных): "
+    reference_error: "ReferenceError (ошибка указателя): "
+    spelling_issues: "Проверьте правильность написания: может вы хотели написать `$1` вместо `$2`?"
+    capitalization_issues: "Проверьте заглавные и прописные буквы: `$1` должно быть `$2`."
+    py_empty_block: "Пустой $1. Добавьте 4 пробела перед инструкциями внутри $2."
+    fx_missing_paren: "Если вы хотите использовать `$1` как функцию, то нужно добавить `()`"
+    unmatched_token: "Не совпадают `$1`.  Каждый(ая) `$2` должен(на) закрываться `$3`."
+    unterminated_string: "Незавершенная строка. Добавьте `\"` в конце строки при переносе."


delete help

delete help

--- scripts/help.coffee
+++ scripts/help.coffee
@@ -1,85 +0,0 @@
-# Description:
-#   Generates help commands for Hubot.
-#
-# Commands:
-#   hubot help - Displays all of the help commands that Hubot knows about.
-#   hubot help <query> - Displays all help commands that match <query>.
-#
-# URLS:
-#   /hubot/help
-#
-# Notes:
-#   These commands are grabbed from comment blocks at the top of each file.
-
-helpContents = (name, commands) ->
-
-  """
-<!DOCTYPE html>
-
-
-  <meta charset="utf-8">
-  <title>#{name} Help</title>
-
-
-
-
#{name} Help

-

-      #{commands}
-

-
-
-  """
-
-module.exports = (robot) ->
-  robot.respond /help\s*(.*)?$/i, (msg) ->
-    cmds = robot.helpCommands()
-    filter = msg.match[1]
-
-    if filter
-      cmds = cmds.filter (cmd) ->
-        cmd.match new RegExp(filter, 'i')
-      if cmds.length == 0
-        msg.send "No available commands match #{filter}"
-        return
-
-    prefix = robot.alias or robot.name
-    cmds = cmds.map (cmd) ->
-      cmd = cmd.replace /hubot/ig, robot.name
-      cmd.replace new RegExp("^#{robot.name}"), prefix
-
-    emit = cmds.join "\n"
-
-    msg.send emit
-
-  robot.router.get "/#{robot.name}/help", (req, res) ->
-    cmds = robot.helpCommands().map (cmd) ->
-      cmd.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
-
-    emit = "
#{cmds.join '

'}
"
-
-    emit = emit.replace /hubot/ig, "#{robot.name}"
-
-    res.setHeader 'content-type', 'text/html'
-    res.end helpContents robot.name, emit


Update uk.coffee (#5924)

Update uk.coffee (#5924)

* Update uk.coffee

* Update uk.coffee

Co-authored-by: Valentin Bryukhanov <53545457af642b0f8712bb2cc004f120ce4436f4@gmail.com>

--- app/locale/uk.coffee
+++ app/locale/uk.coffee
@@ -1,11 +1,11 @@
 module.exports = nativeDescription: "Українська", englishDescription: "Ukrainian", translation:

   new_home:
-#    title: "CodeCombat - Coding games to learn Python and JavaScript"
+    title: "CodeCombat - Кодування ігор для вивчення Python та JavaScript"
 #    meta_keywords: "CodeCombat, python, javascript, Coding Games"
 #    meta_description: "Learn typed code through a programming game. Learn Python, JavaScript, and HTML as you solve puzzles and learn to make your own coding games and websites."
 #    meta_og_url: "https://codecombat.com"
-#    become_investor: "to become an investor in CodeCombat"
+    become_investor: "стати інвестором у CodeCombat"
 #    built_for_teachers_title: "A Coding Game Built with Teachers in Mind"
 #    built_for_teachers_blurb: "Teaching kids to code can often feel overwhelming. CodeCombat helps all educators teach students how to code in either JavaScript or Python, two of the most popular programming languages. With a comprehensive curriculum that includes six computer science units and reinforces learning through project-based game development and web development units, kids will progress on a journey from basic syntax to recursion!"
 #    built_for_teachers_subtitle1: "Computer Science"
@@ -17,7 +17,7 @@
 #    century_skills_title: "21st Century Skills"
 #    century_skills_blurb1: "Students Don't Just Level Up Their Hero, They Level Up Themselves"
 #    century_skills_quote1: "You mess up…so then you think about all of the possible ways to fix it, and then try again. I wouldn't be able to get here without trying hard."
-#    century_skills_subtitle1: "Critical Thinking"
+    century_skills_subtitle1: "Критичне мислення"
 #    century_skills_subblurb1: "With coding puzzles that are naturally scaffolded into increasingly challenging levels, CodeCombat's programming game ensures kids are always practicing critical thinking."
 #    century_skills_quote2: "Everyone else was making mazes, so I thought, ‘capture the flag’ and that’s what I did."
 #    century_skills_subtitle2: "Creativity"
@@ -34,7 +34,7 @@
 #    classroom_in_box_blurb3: "Inspire all school leaders to create a world-class computer science program."
 #    classroom_in_box_blurb4: ""
 #    covid19_banner_blurb: "We are offering free access to our full curriculum to educators and schools affected by school closures."
-#    click_here: "Click here"
+    click_here: "Тиснить тут"
 #    creativity_rigor_title: "Where Creativity Meets Rigor"
 #    creativity_rigor_subtitle1: "Make coding fun and teach real-world skills"
 #    creativity_rigor_blurb1: "Students type real Python and JavaScript while playing games that encourage trial-and-error, critical thinking, and creativity. Students then apply the coding skills they’ve learned by developing their own games and websites in project-based courses."
@@ -43,7 +43,7 @@
 #    creativity_rigor_subtitle3: "Built for all teachers, regardless of experience"
 #    creativity_rigor_blurb3: "CodeCombat’s self-paced, standards-aligned curriculum makes teaching computer science possible for everyone. CodeCombat equips teachers with the training, instructional resources, and dedicated support to feel confident and successful in the classroom."
 #    featured_partners_title1: "Featured In"
-#    featured_partners_title2: "Awards & Partners"
+    featured_partners_title2: "Нагороди та партнери"
 #    featured_partners_blurb1: "CollegeBoard Endorsed Provider"
 #    featured_partners_blurb2: "Best Creativity Tool for Students"
 #    featured_partners_blurb3: "Top Pick for Learning"
@@ -76,14 +76,14 @@
 #    global_subtitle1: "Learners"
 #    global_subtitle2: "Lines of Code"
     global_subtitle3: "Вчителі"
-#    global_subtitle4: "Countries"
-#    go_to_my_classes: "Go to my classes"
-#    go_to_my_courses: "Go to my courses"
+    global_subtitle4: "Країни"
+    go_to_my_classes: "Йти до мого класу"
+    go_to_my_courses: "Йти до моїх курсів"
 #    quotes_quote1: "Name any program online, I’ve tried it. None of them match up to CodeCombat. Any teacher who wants their students to learn how to code... start here!"
 #    quotes_quote2: " I was surprised about how easy and intuitive CodeCombat makes learning computer science. The scores on the AP exam were much higher than I expected and I believe CodeCombat is the reason why this was the case."
 #    quotes_quote3: "CodeCombat has been the most beneficial for teaching my students real-life coding capabilities. My husband is a software engineer and he has tested out all of my programs. He put this as his top choice."
 #    quotes_quote4: "The feedback … has been so positive that we are structuring a computer science class around CodeCombat. The program really engages the students with a gaming style platform that is entertaining and instructional at the same time. Keep up the good work, CodeCombat!"
-#    see_example: "See example"
+    see_example: "Дивитись приклад"
     slogan: "Найзахопливіша гра для вивчення програмування." # {change}
 #    teach_cs1_free: "Teach CS1 Free"
 #    teachers_love_codecombat_title: "Teachers Love CodeCombat"
@@ -92,7 +92,7 @@
 #    teachers_love_codecombat_blurb3: "Say that CodeCombat helps them support students’ problem solving abilities"
 #    teachers_love_codecombat_subblurb: "In partnership with McREL International, a leader in research-based guidance and evaluations of educational technology."
 #    top_banner_blurb: "Parents, book your child's first live online coding class for free!"
-#    try_the_game: "Try the game"
+    try_the_game: "Спробуй гру"
     classroom_edition: "Класна версія:"
     learn_to_code: "Вчитися кодувати:"
     play_now: "Грати зараз"
@@ -163,12 +163,12 @@
 #    ozaria: "Ozaria"
 #    codecombat_blurb: "Our original coding game. Recommended for parents, individuals, educators, and students who want to experience one of the most-loved coding games in the world."
 #    ozaria_blurb: "An adventure game and Computer Science program where students master the lost magic of coding to save their world. Recommended for educators and students."
-#    try_codecombat: "Try CodeCombat"
-#    try_ozaria: "Try Ozaria"
+    try_codecombat: "Спробуй CodeCombat"
+    try_ozaria: "Спробуй Ozaria"

   nav:
 #    educators: "Educators"
-#    follow_us: "Follow Us"
+    follow_us: "Слідкуй за нами"
 #    general: "General"
     map: "Карта"
     play: "Грати" # The top nav bar entry where players choose which levels to play
@@ -228,7 +228,7 @@
     subscribe_as_diplomat: "Записатися в Дипломати"

   play:
-#    title: "Play CodeCombat Levels - Learn Python, JavaScript, and HTML"
+    title: "Грай CodeCombat-ові рівні - вивчай Python, JavaScript та HTML"
 #    meta_description: "Learn programming with a coding game for beginners. Learn Python or JavaScript as you solve mazes, make your own games, and level up. Challenge your friends in multiplayer arena levels!"
 #    level_title: "__level__ - Learn to Code in Python, JavaScript, HTML"
 #    video_title: "__video__ | Video Level"
@@ -236,11 +236,14 @@
 #    web_development_title: "__level__ | Web Development"
     anon_signup_title_1: "CodeCombat має"
     anon_signup_title_2: "Класний Версію!"
+    anon_signup_enter_code: "Введить код класу:"
+    anon_signup_ask_teacher: "Ще немає? Запитай у свого вчителя!"
+    anon_signup_create_class: "Бажаєте створити клас?"
 #    anon_signup_enter_code: "Enter Class Code:"
 #    anon_signup_ask_teacher: "Don't have one? Ask your teacher!"
 #    anon_signup_create_class: "Want to create a class?"
 #    anon_signup_setup_class: "Set up a class, add your students, and monitor progress!"
-#    anon_signup_create_teacher: "Create free teacher account"
+    anon_signup_create_teacher: "Створити безкоштовний акаунт вчителя"
     play_as: "Грати за" # Ladder page
     get_course_for_class: "Призначте розробку ігор та інше для ваших класів!"
     request_licenses: "Зверніться до наших шкільних фахівців для подробиць."
@@ -430,10 +433,10 @@
     teacher_additional_questions: "Отакої! Якщо вам потрібна додаткова допомога або у вас є питання, зв'яжіться з __supportEmail__."
     dont_use_our_email_silly: "Не пишіть нашу електронний адресу здесь! Введіть електронну адресу вашого батька."
     want_codecombat_in_school: "Хочете грати в CodeCombat весь час?"
-#    eu_confirmation: "I agree to allow CodeCombat to store my data on US servers."
-#    eu_confirmation_place_of_processing: "Learn more about the possible risks"
-#    eu_confirmation_student: "If you are not sure, ask your teacher."
-#    eu_confirmation_individual: "If you do not want us to store your data on US servers, you can always keep playing anonymously without saving your code."
+    eu_confirmation: "Я згоден дозволити CodeCombat зберігати мої дані на серверах США."
+    eu_confirmation_place_of_processing: "Дізнайтеся більше про можливі ризики"
+    eu_confirmation_student: "Якщо ви не впевнені, дізнайтеся у вашого вчителя."
+    eu_confirmation_individual: "Якщо ви не бажаєте сберігати свої дані на серверах США, то ви завжди можете грати анонімно без збереження свого коду."

   recover:
     recover_account_title: "Відновити обліківку"
@@ -449,7 +452,7 @@
     books: "Книги"

   common:
-#    default_title: "CodeCombat - Coding games to learn Python and JavaScript"
+    default_title: "CodeCombat - Кодування ігор для вивчення Python та JavaScript"
 #    default_meta_description: "Learn typed code through a programming game. Learn Python, JavaScript, and HTML as you solve puzzles and learn to make your own coding games and websites."
     back: "Назад" # When used as an action verb, like "Navigate backward"
     coming_soon: "Очікуйте скоро!"
@@ -482,9 +485,9 @@
     submit_changes: "Надіслати зміни"
     save_changes: "Зберегти зміни"
     required_field: "обов'язкове поле"
-#    submit: "Submit"
-#    replay: "Replay"
-#    complete: "Complete"
+    submit: "Надіслати"
+    replay: "Переграти"
+    complete: "Завершити"

   general:
     and: "та"
@@ -586,7 +589,7 @@
 #    concept_challenge_complete: "Concept Challenge Complete!"
 #    combo_challenge_complete: "Combo Challenge Complete!"
 #    combo_challenge_complete_body: "Great job, it looks like you're well on your way to understanding __concept__!"
-#    replay_level: "Replay Level"
+    replay_level: "Переграти рівень"
 #    combo_concepts_used: "__complete__/__total__ Concepts Used"
 #    combo_all_concepts_used: "You used all concepts possible to solve the challenge. Great job!"
 #    combo_not_all_concepts_used: "You used __complete__ out of the __total__ concepts possible to solve the challenge. Try to get all __total__ concepts next time!"
@@ -643,7 +646,7 @@
     tome_cast_button_run: "Виконати"
     tome_cast_button_running: "Виконання"
     tome_cast_button_ran: "Виконати"
-#    tome_cast_button_update: "Update"
+    tome_cast_button_update: "Відновити"
     tome_submit_button: "Підтвердити"
     tome_reload_method: "Відновити оригінальний код щоб перезапустити рівень"
     tome_available_spells: "Доступні заклинання"
@@ -655,8 +658,8 @@
     skip_tutorial: "Пропустити (esc)"
     keyboard_shortcuts: "Клавіатурні скорочення"
     loading_start: "Розпочати рівень"
-#    loading_start_combo: "Start Combo Challenge"
-#    loading_start_concept: "Start Concept Challenge"
+    loading_start_combo: "Розпочати Combo Випробування"
+    loading_start_concept: "Розпочати Concept Випробування"
     problem_alert_title: "Виправте код"
     time_current: "Зараз:"
     time_total: "Найбільше:"
@@ -748,8 +751,8 @@
 #    concept_unlocked: "Concept Unlocked"
 #    use_at_least_one_concept: "Use at least one concept: "
 #    command_bank: "Command Bank"
-#    learning_goals: "Learning Goals"
-#    start: "Start"
+    learning_goals: "Цілі навчання"
+    start: "Розпочати"
 #    vega_character: "Vega Character"
     click_to_continue: "Клацніть, щоб продовжити"

@@ -757,8 +760,8 @@
     methods: "Методи"
     events: "Події"
 #    handlers: "Handlers"
-#    properties: "Properties"
-#    snippets: "Snippets"
+    properties: "Властивості"
+    snippets: "Сніппети"
     spawnable: "Породжуваний"
     html: "HTML"
     math: "Математика"
@@ -773,7 +776,7 @@
     number: "Число"
     webjavascript: "JavaScript"

-#  amazon_hoc:
+  amazon_hoc:
 #    title: "Keep Learning with Amazon!"
 #    congrats: "Congratulations on conquering that challenging Hour of Code!"
 #    educate_1: "Now, keep learning about coding and cloud computing with AWS Educate, an exciting, free program from Amazon for both students and teachers. With AWS Educate, you can earn cool badges as you learn about the basics of the cloud and cutting-edge technologies such as gaming, virtual reality, and Alexa."
@@ -784,10 +787,10 @@
 #    future_eng_4: "Amazon Future Engineer"
 #    future_eng_5: "program which creates learning and work opportunities for all K-12 students in the United States who wish to pursue computer science."

-#  live_class:
-#    title: "Thank you!"
-#    content: "Amazing! We just launched live online classes."
-#    link: "Ready to get ahead on your coding?"
+  live_class:
+#    title: "Дякую!"
+    content: "Вражаюче! Ми зараз запустили онлайн-клас."
+    link: "Вже готові прямувати до кодування?"

 #  code_quest:
 #    great: "Great!"
@@ -979,7 +982,7 @@
   premium_features:
     get_premium: "Отримайте
CodeCombat
Преміум" # Fit into the banner on the /features page
     master_coder: "Станьте майстром програмування придбавши підписку вже сьогодні!"
-#    paypal_redirect: "You will be redirected to PayPal to complete the subscription process."
+    paypal_redirect: "Вас спрямують до PayPal для завершення підписки."
     subscribe_now: "Підписатися зараз"
 #    hero_blurb_1: "Get access to __premiumHeroesCount__ super-charged subscriber-only heroes! Harness the unstoppable power of Okar Stompfoot, the deadly precision of Naria of the Leaf, or summon \"adorable\" skeletons with Nalfar Cryptor."
 #    hero_blurb_2: "Premium Warriors unlock stunning martial skills like Warcry, Stomp, and Hurl Enemy. Or, play as a Ranger, using stealth and bows, throwing knives, traps! Try your skill as a true coding Wizard, and unleash a powerful array of Primordial, Necromantic or Elemental magic!"
@@ -1037,7 +1040,7 @@
     restricted_to_certain_heroes: "Тільки певні герої можуть грати в цьому рівні."

   char_customization_modal:
-#    heading: "Customize Your Hero"
+    heading: "Налаштуйте свого героя"
     body: "Тіло"
     name_label: "Ім'я героя"
     hair_label: "Колір волосся"
@@ -1286,7 +1289,7 @@
     create_account_subtitle: "Отримайте доступ до інструментів, доступних лише вчителям, для використання CodeCombat в класі. Налаштуйте клас, додайте учнів та слідкуйте за їхнім прогресом!"
     convert_account_title: "Оновити до обліківки вчителя"
     not: "Ні"
-#    full_name_required: "First and last name required"
+    full_name_required: "Необхідні ім'я та призвище"

   versions:
     save_version_title: "Зберегти нову версію"
@@ -1502,10 +1505,10 @@
     teacher: "Вчитель"
     arena: "Арена"
     available_levels: "Доступні рівні"
-#    started: "started"
-#    complete: "complete"
-#    practice: "practice"
-#    required: "required"
+    started: "розпочато"
+    complete: "закінчено"
+    practice: "практикування"
+    required: "необхідно"
     welcome_to_courses: "Шукачі пригод, ласкаво просимо до курсів!"
     ready_to_play: "Готові грати?"
     start_new_game: "Розпочати нову гру"
@@ -1517,9 +1520,9 @@
     welcome_to_page: "Моя учнівська панель"
     my_classes: "Поточні класи"
     class_added: "Клас успішно додано!"
-#    view_map: "view map"
-#    view_videos: "view videos"
-#    view_project_gallery: "view my classmates' projects"
+    view_map: "дивитись карту"
+    view_videos: "дивитись відео"
+    view_project_gallery: "дивитись проекти однокласників"
     join_class: "Приєднайтесь до класу"
     join_class_2: "Приєднайтесь до класу"
     ask_teacher_for_code: "Запитайте у вашого вчителя, чи є у вас код класу CodeCombat! Якщо так, введіть його нижче:"
@@ -1538,7 +1541,7 @@
     are_you_sure: "Ви впевнені, що хочете видалити цього учня з цього класу?"
     remove_description1: "Учень втратить доступ до цієї класної кімнати та призначених класів. Прогрес НЕ буде втрачено і учня можна буде додати знову до класу в будь-який момент."
     remove_description2: "Активовану платну ліцензію не буде повернено."
-#    license_will_revoke: "This student's paid license will be revoked and made available to assign to another student."
+    license_will_revoke: "Ця студентська сплачена ліцензія буде відкликана та буде доступною для призначення іншому студенту."
     keep_student: "Зберегти учня"
     removing_user: "Видалення користувача"
     subtitle: "Переглянути опис та рівні курсу" # Flat style redesign
@@ -1566,8 +1569,8 @@
     student_age_range_to: "до"
 #    estimated_class_dates_label: "Estimated Class Dates"
 #    estimated_class_frequency_label: "Estimated Class Frequency"
-#    classes_per_week: "classes per week"
-#    minutes_per_class: "minutes per class"
+    classes_per_week: "класів на тиждень"
+    minutes_per_class: "хвилин на клас"
     create_class: "Створити клас"
     class_name: "Назва класу"
     teacher_account_restricted: "Ваша обліківка - вчительська і не дає доступу до учнівських матеріалів."
@@ -1595,7 +1598,7 @@
     update_account_students_can3: "Змагайтеся з однокласниками на аренах"
     update_account_students_can4: "Отримувати доступ до нового учнівського функціоналу як тільки ми випускаємо його"
     update_account_students_warning: "УВАГА: Ви не зможете створювати нових та керувати жодним з існуючих класів, які ви раніше створили."
-#    unsubscribe_warning: "Warning: You will be unsubscribed from your monthly subscription."
+    unsubscribe_warning: "Увага: ви будете відписані від щомісячної підписки."
     update_account_remain_student: "Залишитися учнем"
     update_account_update_student: "Оновити до обліківки учня"
     need_a_class_code: "Вам буде потрібен Код Класу для класу, до якого ви долучаєтесь:"
@@ -1615,7 +1618,7 @@
     select_your_hero_description: "Ви завжди можете змінити вашого героя на сторінці ваших курсів клікнувши \"Змінити героя\""
     select_this_hero: "Обрати цього героя"
     current_hero: "Поточний герой:"
-#    current_hero_female: "Current Hero:"
+    current_hero_female: "Поточна геройка:"
     web_dev_language_transition: "Програма HTML/JavaScript для всіх класів для цього курсу. Класи, що використовували Python, розпочнуть з додаткових вступних рівнів JavaScript, щоб полегшити перехід. Для класів, що вже використовують JavaScript, вступні рівні буде пропущено."
     course_membership_required_to_play: "Вам потрібно приєднатись до курсу щоб зіграти в цей рівень."
     license_required_to_play: "Попроси свого вчителя призначити тобі ліцензію для продовження гри на CodeCombat!"


Update vi.coffee

Update vi.coffee

--- app/locale/vi.coffee
+++ app/locale/vi.coffee
@@ -723,9 +723,9 @@
 #    social_discource: "Join the discussion on our Discourse forum"
     social_facebook: "Hãy thích trang Facebook của CodeCombat"
     social_twitter: "Theo dõi CodeCombat trên Twitter"
-#    social_gplus: "Join CodeCombat on Google+"
-#    social_hipchat: "Chat with us in the public CodeCombat Slack channel"
-#    contribute_to_the_project: "Contribute to the project"
+    social_gplus: "Gia nhập CodeCombat trên Google+"
+    social_hipchat: "Tán gẫu với chúng tôi trên kênh cộng đồng của CodeCombat trên Slack"
+    contribute_to_the_project: "Đóng góp vào dự án này"

   clans:
     clan: "Clan"


Increase timeout for informatics login form

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -50,7 +50,7 @@
     _login: () ->
         logger.info "Logging in new InformaticsUser ", @username
         try
-            page = await @download('https://informatics.msk.ru/login/index.php')
+            page = await @download('https://informatics.msk.ru/login/index.php', {timeout: 30 * 1000})
             token = /<input type="hidden" name="logintoken" value="([^"]*)">/.exec(page)?[1]
             page = await @download("https://informatics.msk.ru/login/index.php", {
                 method: 'POST',


server: return 500 if url is not parseable

--- servers/lib/server/image_cache.coffee
+++ servers/lib/server/image_cache.coffee
@@ -24,6 +24,10 @@
              "url=#{encodeURIComponent(url)}"

   url   = parser.parse url
+
+  unless url.pathname?
+    return res.status(500).end()
+
   ext   = url.pathname.split(".")[-1] or "jpeg"
   noExt = "#{url.host}#{url.pathname}"


Remove logging

Remove logging

--- demo/app/views.coffee
+++ demo/app/views.coffee
@@ -9,7 +9,6 @@

   render: ->
     @$el.html ''
-    console.log @collection
     for model in @collection.models
       if not model.attributes.image? then model.attributes.image = 'tyrion.jpg'
       @$el.append @template(model.toJSON())


always add archiving root in

always add archiving root in

--- services/docstore/app.coffee
+++ services/docstore/app.coffee
@@ -21,8 +21,7 @@
 app.post '/project/:project_id/doc/:doc_id', bodyParser.json(limit: "2mb"), HttpController.updateDoc
 app.del  '/project/:project_id/doc/:doc_id', HttpController.deleteDoc

-if Settings.filestore?.backend == "s3"
-	app.get  '/project/:project_id/archive', HttpController.archiveAllDocs
+app.get  '/project/:project_id/archive', HttpController.archiveAllDocs

 app.get '/status', (req, res)->
 	res.send('docstore is alive')


Not sure how this git messed up

Not sure how this git messed up

--- framer/Framer.coffee
+++ framer/Framer.coffee
@@ -68,7 +68,6 @@

 Framer.Extras.MobileScrollFix.enable() if Utils.isMobile()
 Framer.Extras.TouchEmulator.enable() if not Utils.isTouch()
+Framer.Extras.ErrorDisplay.enable() if not Utils.isFramerStudio()
 # Framer.Extras.Hints.enable() if not Utils.isFramerStudio()
-Framer.Extras.ErrorDisplay.enable() if not Utils.isFramerStudio()
-Framer.Extras.TouchEmulator.enable() if not Utils.isFramerStudio()
 # Framer.Extras.Preloader.enable() if not Utils.isFramerStudio()


NEW: parentFitSquare option for size

NEW: parentFitSquare option for size

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -202,6 +202,7 @@
     childrenSize:                             cs:1
     parentHeightSquare:                       hh:1, wh:1
     parentWidthSquare:                        hw:1, ww:1
+    parentFitSquare:                          hh:1, wh:1, max: hw:1, ww: 1
     childrenSizeMaxParentWidth:               cs:1, max: ww: 1
     parentHeightChildrenWidth:                hh:1, wcw:1
     parentWidthChildrenHeight:                ww:1, hch:1


Bugfix in verification email content

Bugfix in verification email content

--- server/email.coffee
+++ server/email.coffee
@@ -3,7 +3,7 @@
 Accounts.emailTemplates.verifyEmail.subject = (user) ->
   "Email confirmation for Coauthor"
 Accounts.emailTemplates.verifyEmail.text = (user) -> """
-  Welcome to Coauthor, #{user.email}!
+  Welcome to Coauthor, #{user.emails[0].address}!

   You (or someone claiming to be you) just registered an account with username
   "#{user.username}" on the Coauthor collaboration website,


add normalizeDeck to set up model input

add normalizeDeck to set up model input

--- server/golem.coffee
+++ server/golem.coffee
@@ -87,6 +87,24 @@
   assert.ok(newfeats.chips >= 0)
   newfeats

+normalizeDeck = (feats) ->
+  # Takes a deckFeatures object and squishes together the card counts and
+  # a bunch of features into one object. The card counts will be normalized
+  # into cards per hand, and other quantities also reduced, to prepare for
+  # prediction by Vowpal.
+  nHands = Math.max(feats.n, 5) / 5
+  normalized = {actions: 0}
+  for card, count of feats.deck
+    normalized[card] = count / nHands
+    if card_info[card].isAction
+      # This was a bug in our training! Unfortunately, we should test the
+      # same way. We counted unique actions / 5 instead of total actions.
+      normalized.actions += 0.2
+  normalized.unique = feats.unique / 5
+  normalized.n = feats.n / 10
+  normalized.vp = feats.vp / 10
+  normalized
+
 chooseGain = (mydeck, oppdeck, supply, coins, buys, turnNum, responder) ->
   # Decide what to gain or buy.
   # Arguments:
@@ -113,14 +131,22 @@
   myfeatures = getDeckFeatures(mydeck)
   oppfeatures = getDeckFeatures(oppdeck)

-  vwInput = [
-    vowpal.featureString(
-      '+'.join(choice),
-      addToDeckFeatures(mydeck, myfeatures, choice),
-      oppfeatures
-    ) for choice in choices
-  ].join('\n')
-  vowpal.maximizePrediction("model"+turnNum+".vw", vwInput, responder)
+  vwLines = for choice in choices
+    newfeats = addToDeckFeatures(mydeck, myfeatures, choice)
+    vwStruct = {
+      cards: normalizeDeck(newfeats)
+      opponent: normalizeDeck(oppfeatures)
+      unique: newfeats.unique
+      vsunique: oppfeatures.unique
+    }
+    name = choice.join('+')
+    vowpal.featureString(name, vwStruct)
+
+  vowpal.maximizePrediction(
+    "model"+turnNum+".vw",
+    vwLines.join('\n'),
+    responder
+  )

 gainHandler = (request, responder, query) ->
   mydeck = JSON.parse(query.mydeck)   # mapping from card -> count


#2 more nexmo stuff

#2 more nexmo stuff

--- server/nexmo.coffee
+++ server/nexmo.coffee
@@ -1,3 +1,5 @@
+
+@NexmoMessages = new Mongo.Collection 'nexmo_messages'

 Meteor.methods
@@ -6,13 +8,17 @@

-Router.route '/receive_sms/:data', ->
+Router.route '/receive-sms-' + nexmo_callback_secret, ->
   # TODO: https://github.com/Nexmo/Quickstarts/blob/master/sms/receive/php/receive.php

   # NodeJS objects
   # console.log @request
-  console.log @response
-
-  @response.end 'file download content\n'
+  sms_data = @request.query
+  console.log sms_data
+  NexmoMessages.insert sms_data
+  Messages.insert
+    text: sms_data.text
+    dateime: new Date()
+  @response.end JSON.stringify sms_data
 ,
   where: 'server'


Make sure all folders exist up until /Champion

--- functions/main.coffee
+++ functions/main.coffee
@@ -142,9 +142,11 @@

 # Console log what install path were using after getInstallPath closes.
+# Also create the folders through if need.
 clInstallPath = (cb) ->
   cl 'Using League Installation in: ' +GLOBAL.lolInstallPath, 'green'
-  cb null
+  mkdirp GLOBAL.lolInstallPath, (err) ->
+    cb null

 # Get latest Riot Version


Fix the tests.

Fix the tests.

--- spec/github-file-spec.coffee
+++ spec/github-file-spec.coffee
@@ -65,16 +65,16 @@

         it "opens the GitHub.com blob URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.open()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blob/master/some-dir/some-file.md'

         describe "when text is selected", ->
           it "opens the GitHub.com blob URL for the file with the selection range in the hash", ->
             atom.config.set('open-on-github.includeLineNumbersInUrls', true)
             spyOn(githubFile, 'openUrlInBrowser')
-            githubFile.open([[0, 0], [1, 1]])
-            expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+            waitsForPromise -> githubFile.open([[0, 0], [1, 1]])
+            runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
               'https://github.com/some-user/some-repo/blob/master/some-dir/some-file.md#L1-L2'

         describe "when the file has a '#' in its name", ->
@@ -86,8 +86,8 @@
               editor = atom.workspace.getActiveTextEditor()
               githubFile = GitHubFile.fromPath(editor.getPath())
               spyOn(githubFile, 'openUrlInBrowser')
-              githubFile.open()
-              expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+              waitsForPromise -> githubFile.open()
+              runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
                 'https://github.com/some-user/some-repo/blob/master/a/b%23/test%23hash.md'

       describe "when the branch has a '/' in its name", ->
@@ -102,8 +102,8 @@

         it "opens the GitHub.com blob URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.open()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blob/foo/bar/some-dir/some-file.md'

       describe "when the branch has a '#' in its name", ->
@@ -118,8 +118,8 @@

         it "opens the GitHub.com blob URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.open()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blob/a%23b%23c/some-dir/some-file.md'

       describe "when the remote has a '/' in its name", ->
@@ -134,8 +134,8 @@

         it "opens the GitHub.com blob URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.open()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blob/baz/some-dir/some-file.md'

       describe "when the local branch is not tracked", ->
@@ -149,8 +149,8 @@
           teardownWorkingDirAndRestoreFixture(fixtureName)
         it "opens the GitHub.com blob URL for the file on the master branch", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.open()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blob/master/some-dir/some-file.md'

       describe "when there is no remote", ->
@@ -165,8 +165,8 @@

         it "logs an error", ->
           spyOn(atom.notifications, 'addWarning')
-          githubFile.open()
-          expect(atom.notifications.addWarning).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(atom.notifications.addWarning).toHaveBeenCalledWith \
             'No URL defined for remote: null'

       describe "when the root directory doesn't have a git repo", ->
@@ -177,9 +177,10 @@

         it "does nothing", ->
           spyOn(atom.notifications, 'addWarning')
-          githubFile.open()
-          expect(atom.notifications.addWarning).toHaveBeenCalled()
-          expect(atom.notifications.addWarning.mostRecentCall.args[0]).toContain("No repository found")
+          waitsForPromise -> githubFile.open()
+          runs ->
+            expect(atom.notifications.addWarning).toHaveBeenCalled()
+            expect(atom.notifications.addWarning.mostRecentCall.args[0]).toContain("No repository found")

       describe "when the remote repo is not hosted on github.com", ->
         fixtureName = 'github-enterprise-remote'
@@ -193,8 +194,8 @@

         it "opens a GitHub enterprise style blob URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.open()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.open()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://git.enterprize.me/some-user/some-repo/blob/master/some-dir/some-file.md'

     describe "openOnMaster", ->
@@ -209,8 +210,8 @@

       it "opens the GitHub.com blob URL for the file", ->
         spyOn(githubFile, 'openUrlInBrowser')
-        githubFile.openOnMaster()
-        expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+        waitsForPromise -> githubFile.openOnMaster()
+        runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
           'https://github.com/some-user/some-repo/blob/master/some-dir/some-file.md'

     describe "blame", ->
@@ -226,16 +227,16 @@

         it "opens the GitHub.com blame URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.blame()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.blame()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blame/master/some-dir/some-file.md'

         describe "when text is selected", ->
           it "opens the GitHub.com blame URL for the file with the selection range in the hash", ->
             atom.config.set('open-on-github.includeLineNumbersInUrls', true)
             spyOn(githubFile, 'openUrlInBrowser')
-            githubFile.blame([[0, 0], [1, 1]])
-            expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+            waitsForPromise -> githubFile.blame([[0, 0], [1, 1]])
+            runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
               'https://github.com/some-user/some-repo/blame/master/some-dir/some-file.md#L1-L2'

       describe "when the local branch is not tracked", ->
@@ -250,8 +251,8 @@

         it "opens the GitHub.com blame URL for the file on the master branch", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.blame()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.blame()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/blame/master/some-dir/some-file.md'

     describe "branchCompare", ->
@@ -267,8 +268,8 @@

         it "opens the GitHub.com branch compare URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.openBranchCompare()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.openBranchCompare()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/compare/master'

     describe "history", ->
@@ -284,8 +285,8 @@

         it "opens the GitHub.com history URL for the file", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.history()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.history()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/commits/master/some-dir/some-file.md'

       describe "when the local branch is not tracked", ->
@@ -300,8 +301,8 @@

         it "opens the GitHub.com history URL for the file on the master branch", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.history()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.history()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/commits/master/some-dir/some-file.md'

     describe "copyUrl", ->
@@ -317,13 +318,13 @@

       describe "when text is selected", ->
         it "copies the URL to the clipboard with the selection range in the hash", ->
-          githubFile.copyUrl([[0, 0], [1, 1]])
-          expect(atom.clipboard.read()).toBe 'https://github.com/some-user/some-repo/blob/80b7897ceb6bd7531708509b50afeab36a4b73fd/some-dir/some-file.md#L1-L2'
+          waitsForPromise -> githubFile.copyUrl([[0, 0], [1, 1]])
+          runs -> expect(atom.clipboard.read()).toBe 'https://github.com/some-user/some-repo/blob/80b7897ceb6bd7531708509b50afeab36a4b73fd/some-dir/some-file.md#L1-L2'

       describe "when no text is selected", ->
         it "copies the URL to the clipboard with the cursor location in the hash", ->
-          githubFile.copyUrl([[2, 1], [2, 1]])
-          expect(atom.clipboard.read()).toBe 'https://github.com/some-user/some-repo/blob/80b7897ceb6bd7531708509b50afeab36a4b73fd/some-dir/some-file.md#L3'
+          waitsForPromise -> githubFile.copyUrl([[2, 1], [2, 1]])
+          runs -> expect(atom.clipboard.read()).toBe 'https://github.com/some-user/some-repo/blob/80b7897ceb6bd7531708509b50afeab36a4b73fd/some-dir/some-file.md#L3'

     describe "openRepository", ->
       describe "when the file is openable on GitHub.com", ->
@@ -338,8 +339,8 @@

         it "opens the GitHub.com repository URL", ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.openRepository()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.openRepository()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo'

     describe "openIssues", ->
@@ -355,70 +356,87 @@

         it 'opens the GitHub.com issues URL', ->
           spyOn(githubFile, 'openUrlInBrowser')
-          githubFile.openIssues()
-          expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
+          waitsForPromise -> githubFile.openIssues()
+          runs -> expect(githubFile.openUrlInBrowser).toHaveBeenCalledWith \
             'https://github.com/some-user/some-repo/issues'

   describe "gitHubRepoUrl", ->
+    waitsForGitHubUrl = null
     beforeEach ->
       githubFile = new GitHubFile()

+      waitsForGitHubUrl = (expectedUrl) ->
+        gitHubRepoUrl = null
+        waitsForPromise -> githubFile.gitHubRepoUrl().then (url) -> gitHubRepoUrl = url
+        runs ->
+          if expectedUrl?
+            expect(gitHubRepoUrl).toBe expectedUrl
+          else
+            expect(gitHubRepoUrl).toBeUndefined()
+
     it "returns the GitHub.com URL for an HTTPS remote URL", ->
-      githubFile.gitUrl = -> "https://github.com/foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "https://github.com/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("https://github.com/foo/bar.git")
+      waitsForGitHubUrl("https://github.com/foo/bar")

     it "will only strip a single .git suffix", ->
-      githubFile.gitUrl = -> "https://github.com/foo/bar.git.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "https://github.com/foo/bar.git"
-
-      githubFile.gitUrl = -> "https://github.com/foo/bar.git.other.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "https://github.com/foo/bar.git.other"
+      githubFile.gitUrl = -> Promise.resolve("https://github.com/foo/bar.git.git")
+      waitsForGitHubUrl("https://github.com/foo/bar.git")
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("https://github.com/foo/bar.git.other.git")
+        waitsForGitHubUrl("https://github.com/foo/bar.git.other")

     it "returns the GitHub.com URL for an HTTP remote URL", ->
-      githubFile.gitUrl = -> "http://github.com/foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "http://github.com/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("http://github.com/foo/bar.git")
+      waitsForGitHubUrl("http://github.com/foo/bar")

     it "returns the GitHub.com URL for an SSH remote URL", ->
-      githubFile.gitUrl = -> "git@github.com:foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "http://github.com/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("git@github.com:foo/bar.git")
+      waitsForGitHubUrl("http://github.com/foo/bar")

     it "returns a GitHub enterprise URL for a non-Github.com remote URL", ->
-      githubFile.gitUrl = -> "https://git.enterprize.me/foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "https://git.enterprize.me/foo/bar"
-
-      githubFile.gitUrl = -> "git@git.enterprize.me:foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "http://git.enterprize.me/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("https://git.enterprize.me/foo/bar.git")
+      waitsForGitHubUrl("https://git.enterprize.me/foo/bar")
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("git@git.enterprize.me:foo/bar.git")
+        waitsForGitHubUrl("http://git.enterprize.me/foo/bar")

     it "returns the GitHub.com URL for a git:// URL", ->
-      githubFile.gitUrl = -> "git://github.com/foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "http://github.com/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("git://github.com/foo/bar.git")
+      waitsForGitHubUrl("http://github.com/foo/bar")

     it "returns the GitHub.com URL for a ssh:// URL", ->
-      githubFile.gitUrl = -> "ssh://git@github.com/foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "http://github.com/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("ssh://git@github.com/foo/bar.git")
+      waitsForGitHubUrl("http://github.com/foo/bar")

     it "returns undefined for Bitbucket URLs", ->
-      githubFile.gitUrl = -> "https://bitbucket.org/somebody/repo.git"
-      expect(githubFile.gitHubRepoUrl()).toBeUndefined()
-
-      githubFile.gitUrl = -> "https://bitbucket.org/somebody/repo"
-      expect(githubFile.gitHubRepoUrl()).toBeUndefined()
-
-      githubFile.gitUrl = -> "git@bitbucket.org:somebody/repo.git"
-      expect(githubFile.gitHubRepoUrl()).toBeUndefined()
-
-      githubFile.gitUrl = -> "git@bitbucket.org:somebody/repo"
-      expect(githubFile.gitHubRepoUrl()).toBeUndefined()
+      githubFile.gitUrl = -> Promise.resolve("https://bitbucket.org/somebody/repo.git")
+      waitsForGitHubUrl()
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("https://bitbucket.org/somebody/repo")
+        waitsForGitHubUrl()
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("git@bitbucket.org:somebody/repo.git")
+        waitsForGitHubUrl()
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("git@bitbucket.org:somebody/repo")
+        waitsForGitHubUrl()

     it "removes leading and trailing slashes", ->
-      githubFile.gitUrl = -> "https://github.com/foo/bar/"
-      expect(githubFile.gitHubRepoUrl()).toBe "https://github.com/foo/bar"
-
-      githubFile.gitUrl = -> "https://github.com/foo/bar//////"
-      expect(githubFile.gitHubRepoUrl()).toBe "https://github.com/foo/bar"
-
-      githubFile.gitUrl = -> "git@github.com:/foo/bar.git"
-      expect(githubFile.gitHubRepoUrl()).toBe "http://github.com/foo/bar"
+      githubFile.gitUrl = -> Promise.resolve("https://github.com/foo/bar/")
+      waitsForGitHubUrl("https://github.com/foo/bar")
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("https://github.com/foo/bar//////")
+        waitsForGitHubUrl("https://github.com/foo/bar")
+
+      runs ->
+        githubFile.gitUrl = -> Promise.resolve("git@github.com:/foo/bar.git")
+        waitsForGitHubUrl("http://github.com/foo/bar")

   it "activates when a command is triggered on the active editor", ->
     activationPromise = atom.packages.activatePackage('open-on-github')


check if ROOT_URL includes a trailing slash

--- routes/api/v2/docs.coffee
+++ routes/api/v2/docs.coffee
@@ -2,7 +2,9 @@
   if Meteor.isServer
     aglio = require('aglio')
     blueprintDoc = Assets.getText 'API.apib'
-    blueprintDoc = blueprintDoc.replace 'http://localhost:3000/', process.env.ROOT_URL
+    console.log 'wawiewa', process.env.ROOT_URL[..1]
+    url = "#{process.env.ROOT_URL}#{if process.env.ROOT_URL[process.env.ROOT_URL.length-1..-1] isnt '/' then '/' else ''}"
+    blueprintDoc = blueprintDoc.replace 'http://localhost:3000/', url
     rendered = ''

     options =
@@ -12,7 +14,7 @@
     aglio.render blueprintDoc, options, (err, html, warnings) ->
       rendered = html

-    WebApp.connectHandlers.use "/api/v2/docs", (req, res, next) ->
+    WebApp.connectHandlers.use "/docs/api/v2", (req, res, next) ->
       res.setHeader 'content-type', 'text/html; charset=utf-8'
       res.writeHead 200
       res.end rendered


html-md editor: fix bug involving color html appearing during file open

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -5225,10 +5225,11 @@
             elt   = button_bar.find(".sagews-output-editor-foreground-color-selector")
             button_bar_input = elt.find("input").colorpicker()
             sample = elt.find("i")
-            set = (hex) =>
+            set = (hex, init) =>
                 sample.css("color", hex)
                 button_bar_input.css("background-color", hex)
-                @command(@cm(), "color", hex)
+                if not init
+                    @command(@cm(), "color", hex)

             button_bar_input.change (ev) =>
                 hex = button_bar_input.val()
@@ -5241,7 +5242,7 @@
             sample.click (ev) =>
                 button_bar_input.colorpicker('show')

-            set("#000000")
+            set("#000000", true)

         init_color_control()
         # initialize the color control
@@ -5249,10 +5250,11 @@
             elt   = button_bar.find(".sagews-output-editor-background-color-selector")
             button_bar_input = elt.find("input").colorpicker()
             sample = elt.find("i")
-            set = (hex) =>
+            set = (hex, init) =>
                 button_bar_input.css("background-color", hex)
                 elt.find(".input-group-addon").css("background-color", hex)
-                @command(@cm(), "background-color", hex)
+                if not init
+                    @command(@cm(), "background-color", hex)

             button_bar_input.change (ev) =>
                 hex = button_bar_input.val()
@@ -5265,7 +5267,7 @@
             sample.click (ev) =>
                 button_bar_input.colorpicker('show')

-            set("#fff8bd")
+            set("#fff8bd", true)

         init_background_color_control()


Components: SidebarMachinesListItem: Set coordinates to "states" variable when the component is mounted.

--- client/app/lib/components/sidebarmachineslistitem/sidebarworkspaceslistitem.coffee
+++ client/app/lib/components/sidebarmachineslistitem/sidebarworkspaceslistitem.coffee
@@ -23,10 +23,16 @@
     activeWorkspace : EnvironmentFlux.getters.activeWorkspace

-  componentWillReceiveProps: ->
+  setCoordiantes: ->

     coordinates = getBoundingClientReact @refs.WorkspaceItem
     @setState { coordinates: coordinates }
+
+
+  componentWillReceiveProps: -> @setCoordiantes()
+
+
+  componentDidMount: -> @setCoordiantes()

   getWorkspaceLink: ->


Allow commas to separate arguments in RUNOFF

--- grammars/runoff.cson
+++ grammars/runoff.cson
@@ -51,6 +51,9 @@
 			name: "constant.numeric.runoff"
 			match: "[-+]?\\d+(?:\\.\\d+)?"
 		},{
+			name: "punctuation.separator.comma.runoff"
+			match: ","
+		},{
 			# Double-quoted string
 			name: "string.quoted.runoff"
 			begin: '"'
@@ -60,7 +63,7 @@
 		},{
 			# Unquoted string
 			name: "variable.parameter.runoff"
-			match: "(?!\\.)[^\\s;]+"
+			match: "(?!\\.)[^\\s;,]+"
 			captures:
 				0: name: "string.unquoted.runoff"
 		}]
@@ -124,7 +127,8 @@
 					# Allow punctuation/parameters in clustered command-chains to be highlighted:
 					# .LM0.RM80 is likely a compressed form of ".LM 0; .RM 80"
 					{match: "\\.", name: "punctuation.delimiter.period.full-stop.runoff"}
-					{match: "\\d+(?=$|\\n|\\.(?!\\d))", name: "constant.numeric.runoff"}
+					{match: "\\,", name: "punctuation.separator.comma.runoff"}
+					{match: "\\d+(?=$|\\n|,|\\.(?!\\d))", name: "constant.numeric.runoff"}
 				]
 			endCaptures:
 				1: name: "punctuation.terminator.statement.runoff"


kite-server: pass around the log level

kite-server: pass around the log level

--- src/kite-server/kite-server.coffee
+++ src/kite-server/kite-server.coffee
@@ -41,9 +41,10 @@
     throw new Error "Already registered!"  if @kontrol?
     kontrolUriP = Promise.cast kontrolUri
     kiteUriP = Promise.cast kiteUri
-    Promise.all [kontrolUriP, kiteUriP, @normalizeKiteKey(kiteKey)]
+    kiteKeyP = @normalizeKiteKey kiteKey
+    Promise.all [kontrolUriP, kiteUriP, kiteKeyP]
       .spread (kontrolUri, kiteUri, key) =>
-        { name, username, environment, version, region, hostname } = @options
+        { name, username, environment, version, region, hostname, logLevel } = @options

         throw new Error "No kite key!"  unless key?

@@ -58,14 +59,14 @@
           version     : version
           region      : region
           hostname    : hostname
+          logLevel    : logLevel
         .on 'connected', =>
           @emit 'notice', "Connected to Kontrol"

         kiteUrl = "ws://#{ kiteUri }:#{ @port }/#{ @options.name }"

-        @kontrol.register url: kiteUrl
-          .then =>
-            @emit 'info', "Registered to kontrol with URL: #{ kiteUrl }"
+        @kontrol.register(url: kiteUrl).then =>
+          @emit 'info', "Registered to Kontrol with URL: #{ kiteUrl }"

   normalizeKiteKey: Promise.method (src, enc = "utf-8") -> switch
     when 'string' is typeof src


streamline commands

streamline commands

--- src/layout/inspect_commands.coffee
+++ src/layout/inspect_commands.coffee
@@ -17,7 +17,7 @@
   language = sprite.get('language')
   words = Object.keys language
   words = words.filter (x) -> x[0] != '_' unless my.design
-  command_buttons = make.buttons(
+  make.buttons(
     'command',
     words,
     my.command,


Do not update user in forceSetUserList

Do not update user in forceSetUserList

--- server/models/user.coffee
+++ server/models/user.coffee
@@ -144,7 +144,7 @@
     logger.info "force-setting userList ", @_id, userList
     await @update({$set: {"userList": userList}})
     @userList = userList
-    User.updateUser(@_id)
+    #User.updateUser(@_id)
     return undefined

 usersSchema.methods.setDormant = (dormant) ->


added getteam helper function to testhelper

--- servers/testhelper.coffee
+++ servers/testhelper.coffee
@@ -168,6 +168,21 @@
     body = TeamHandlerHelper.generateCreateTeamRequestBody()

     params               = { url, body }
+    defaultRequestParams = generateDefaultParams params
+    requestParams        = deepObjectExtend defaultRequestParams, opts
+    # after deep extending object, encodes body param to a query string
+    requestParams.body   = querystring.stringify requestParams.body
+
+    return requestParams
+
+
+  @generateGetTeamRequestParams = (opts = {}) ->
+
+    { groupSlug } = opts
+    url  = generateUrl
+      route : "-/teams/#{groupSlug}"
+
+    params               = { url }
     defaultRequestParams = generateDefaultParams params
     requestParams        = deepObjectExtend defaultRequestParams, opts
     # after deep extending object, encodes body param to a query string


:lipstick:

:lipstick:

--- src/app/repository-status-task.coffee
+++ src/app/repository-status-task.coffee
@@ -3,7 +3,6 @@

 module.exports =
 class RepositoryStatusTask extends Task
-
   constructor: (@repo) ->
     super('repository-status-handler')


Reformat generateOID

Reformat generateOID

--- source/utils.coffee
+++ source/utils.coffee
@@ -1,7 +1,5 @@
-generateOID = __root__._?.generateID ? do ->
-  n = 0
-  -> ++n
-
+generateOID = __root__._?.generateID ? do -> n = 0; (-> ++n)
+
 getOID = (object) -> object.oid ?= generateOID()

 resolveCallback = (object, callback) ->


opening maps on click

opening maps on click

--- src/Attendee/Bundle/WebpageBundle/Resources/public/js/app/views/map_view.js.coffee
+++ src/Attendee/Bundle/WebpageBundle/Resources/public/js/app/views/map_view.js.coffee
@@ -1,6 +1,10 @@
 App.MapView = Ember.View.extend
   templateName:      'views/map'
   attributeBindings: ['lat', 'lng'],
+
+  click: ->
+    url = 'http://maps.google.com/?q=' + @get('coordinates')
+    window.open url,'_blank'

   coordinates: (->
     if (@get 'lat') != 0 and (@get 'lng') != 0


Avoid references for passed schemas; fix

--- spec/helpers.coffee
+++ spec/helpers.coffee
@@ -21,11 +21,7 @@
   validator = new ZSchema
     ignoreUnresolvableReferences: false

-  schemas = []
-  schemas.push(json) for id, json of refs
-
-  validator.validateSchema schemas
-  validator.setRemoteReference(id, json) for id, json of refs
+  validator.setRemoteReference(id, JSON.stringify(json)) for id, json of refs

   valid = validator.validate @actual, expected


Note for future work

Note for future work

--- specsrc/task.coffee
+++ specsrc/task.coffee
@@ -14,6 +14,7 @@
 exports.describeTaskPromise = describeTaskPromise

 describe 'backburner.Task', ->
+    # TODO: if anything public other than Task() returns a task, refactor as describeTask
     describeDeferred -> new Deferred

     it 'should require that the first argument to the constructor be a function', ->


SplitView: removePanel logic removed WIP

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -299,48 +299,14 @@
     askedPanel._setSize sizes[0]
     affectedPanel._setSize sizes[1]

-  removePanel:(index)->
     @panels[1]._setOffset offset
     @resizer._setOffset offset  if @resizer
     @_resizeDidStop()
     @beingResized = no

-    l = @panels.length
-    if l is 1
-      warn "this is the only panel left"
-      return no
-
-    panel = @panels[index]
-    panel.destroy()
-
-    if index is 0
-      # log "FIRST ONE"
-      r = @resizers.shift()
-      r.destroy()
-      if res = @resizers[0]
-        res.panel0 = @panels[0]
-        res.panel1 = @panels[1]
-      # nextPanel._setOffset nextPanel._getOffset() - panel._getSize()
-      # nextPanel._setSize   nextPanel._getSize() + panel._getSize()
-
-    else if index is l - 1
-      # log "LAST ONE"
-      r = @resizers.pop()
-      r.destroy()
-      if res = @resizers[l-2]
-        res.panel0 = @panels[l-2]
-        res.panel1 = @panels[l-1]
-
-      # prevPanel = @panels[length - 2]
-      # prevPanel._setSize prevPanel._getSize() + panel._getSize()
+
   splitPanel:(index, options = {})->

-    else
-      # log "ONE IN THE MIDDLE"
-      [r] = @resizers.splice index - 1, 1
-      r.destroy()
-      @resizers[index - 1].panel0 = @panels[index-1]
-      @resizers[index - 1].panel1 = @panels[index]
     view            = @panels[index].subViews.first
     @panels[index].subViews = []
     if view
@@ -353,11 +319,9 @@

     @setView split, index

-      # prevPanel = @panels[index - 1]
-      # prevPanel._setSize prevPanel._getSize() + panel._getSize()
-
-
-    return yes
+
+  removePanel:(index)->
+

   setView:(view,index)->
     if index > @panels.length or not view


Stub in the unsaved state mixin

Stub in the unsaved state mixin

--- src/components/task-plan/builder.cjsx
+++ src/components/task-plan/builder.cjsx
@@ -13,10 +13,11 @@
 {TaskPlanStore, TaskPlanActions} = require '../../flux/task-plan'
 {TutorInput, TutorDateInput, TutorDateFormat, TutorTextArea} = require '../tutor-input'
 {CourseStore}   = require '../../flux/course'
+{UnsavedStateMixin} = require '../unsaved-state'

 module.exports = React.createClass
   displayName: 'TaskPlanBuilder'
-  mixins: [PlanMixin, BindStoreMixin, Router.State]
+  mixins: [PlanMixin, BindStoreMixin, Router.State, UnsavedStateMixin]
   bindStore: CourseStore
   propTypes:
     id: React.PropTypes.string.isRequired
@@ -27,6 +28,11 @@

     showingPeriods: not isNewPlan
     currentLocale: TimeHelper.getCurrentLocales()
+
+  # Called by the UnsavedStateMixin to detect if anything needs to be persisted
+  hasUnsavedState: ->
+    true
+

   mapPeriods: (opensAt, dueAt) ->
     planId = @props.id


bits: Add uintToString()

bits: Add uintToString()

--- bits.coffee
+++ bits.coffee
@@ -366,6 +366,13 @@
     @byte_index = nullPos + 1
     return str

+  # Returns a string constructed by a number
+  @uintToString: (num, numBytes, encoding='utf8') ->
+    arr = []
+    for i in [numBytes..1]
+      arr.push (num * Math.pow(2, -(i-1)*8)) & 0xff
+    return new Buffer(arr).toString encoding
+
   @searchBytesInArray: (haystack, needle, from_pos=0) ->
     if buffertools?  # buffertools is available
       if haystack not instanceof Buffer


Improve markdown formatting in atom

Improve markdown formatting in atom

--- home/atom/config.cson
+++ home/atom/config.cson
@@ -56,7 +56,7 @@
   editor:
     autoIndentOnPaste: false
     fontFamily: "Source Code Pro"
-    fontSize: 12
+    fontSize: 11
     invisibles:
       cr: " "
       eol: " "
@@ -67,9 +67,12 @@
   emmet: {}
   "exception-reporting":
     userId: "e1f4ef15-b72f-d1f8-3af5-8d0a66844b85"
+  "fast-eslint": {}
   "git-log":
     fontScale: 0.6
-  "git-plus": {}
+  "git-plus":
+    general:
+      _analyticsUserId: "2621e659-146c-461b-8354-71161e66aae9"
   jsformat:
     eval_code: true
     indent_with_tabs: true
@@ -104,6 +107,11 @@
     breakOnSingleNewline: true
   "markdown-table-formatter":
     formatOnSave: false
+    limitLastColumnPadding: true
+    markdownGrammarScopes: [
+      "source.gfm"
+      "text.md"
+    ]
   "one-dark-ui": {}
   "one-light-ui": {}
   "preview-plus": {}
@@ -191,6 +199,9 @@
 ".md.source":
   whitespace:
     removeTrailingWhitespace: true
+".md.text":
+  editor:
+    softWrapAtPreferredLineLength: true
 ".pyjade.source":
   editor:
     autoIndent: true


Document methods added to Datasource

Document methods added to Datasource

--- src/client/datasource.coffee
+++ src/client/datasource.coffee
@@ -11,6 +11,10 @@

     links

+  # Parse query string from a path
+  #
+  # @param {String} a path with a query string
+  # @return {Object}
   parseQueryString = (path) ->
     path = path.split('?')[1] || path

@@ -99,24 +103,44 @@
           dfd.reject err
       dfd.promise()

+    # Is datasource paginable?
+    #
+    # @returns {Boolean}
     isPaginable: ->
       @paginationLinks?

+    # Is datasource on the first page?
+    #
+    # @returns {Boolean}
     isFirst: ->
       !@paginationLinks.first

+    # Is datasource on the last page?
+    #
+    # @returns {Boolean}
     isLast: ->
       !@paginationLinks.last

+    # Go to previous page.
+    #
+    # Change datasource path to the path of the previous page.
     previous: ->
       unless @isFirst()
         @def.path = @paginationLinks.prev

+    # Go to next page.
+    #
+    # Change datasource path to the path of the next page.
     next: ->
       unless @isLast()
         @def.path = @paginationLinks.next

+    # Sort the datasource by a given field in a given direction.
+    #
+    # @param {String} field the field to sort the datasource by.
+    # @param {String} direction default to `"ASC"`. Can be `"DESC"` or `"ASC"`
     sort: (field, direction = 'ASC') ->
+      # TODO This can be moved
       params = parseQueryString(@def.path)
       params.order_by = field + ' ' + direction
       params = _.map params, (v, k) ->


fixed syntax error construction for loaded modules

--- src/coffee-react-script.coffee
+++ src/coffee-react-script.coffee
@@ -33,7 +33,7 @@
       # As the filename and code of a dynamically loaded file will be different
       # from the original file compiled with CoffeeScript.run, add that
       # information to error so it can be pretty-printed later.
-      throw helpers.updateSyntaxError err, input, filename
+      throw helpers.updateSyntaxError err, stripped, filename

     answer


Workspace: Fixed height of CodeMirror editor in tabbed container after resize done.

--- client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -34,6 +34,13 @@
             @createEditorInstance file, null, data.sessionKey

     @workspaceRef.onDisconnect().remove()  if @workspace.amIHost()
+
+    @on "PaneResized", =>
+      @setHeight @parent.getHeight()
+      for pane in @tabView.panes
+        {codeMirrorEditor} = pane.subViews[0]
+        codeMirrorEditor.display.wrapper.style.height = "#{@parent.getHeight() - 22}px"
+        codeMirrorEditor.refresh()

   createEditorTabs: ->
     @tabHandleContainer = new ApplicationTabHandleHolder


make session 'default'... the default!

make session 'default'... the default!

--- src/smc-webapp/init_app.coffee
+++ src/smc-webapp/init_app.coffee
@@ -362,4 +362,9 @@
         redux.getActions('page').set_fullscreen('default')

 # configure the session
-redux.getActions('page').set_session(misc_page.get_query_param('session'))
+# This makes it so the default session is 'default' and there is no
+# way to NOT have a session.
+session = misc_page.get_query_param('session') ? 'default'
+# This would make it so there is no session if you don't explicitly set ?session= in the URL.
+#session = misc_page.get_query_param('session')
+redux.getActions('page').set_session()


[Readability counts](http://www.python.org/dev/peps/pep-0020/)

--- vendor/assets/javascripts/brainstem/loaders/collection-loader.coffee
+++ vendor/assets/javascripts/brainstem/loaders/collection-loader.coffee
@@ -36,7 +36,7 @@
     for underscoredModelName in keys
       @storageManager.storage(underscoredModelName).update _(resp[underscoredModelName]).values()

-    unless @loadOptions.cache == false || @loadOptions.only?
+    if @loadOptions.cache && !@loadOptions.only?
       @storageManager.getCollectionDetails(@loadOptions.name).cache[@loadOptions.cacheKey] = results

     if @loadOptions.only?


fix resize fail for message box

fix resize fail for message box

--- src/frontend/textChat.coffee
+++ src/frontend/textChat.coffee
@@ -15,6 +15,8 @@

 ###

+
+
 class   TextChat
     constructor     : (NetworkManager) ->
         @jqForm       = ($ 'form#chatForm')
@@ -27,10 +29,12 @@
         @jqForm.submit (event) =>
             do event.preventDefault
             message = do @jqInput.val
-            @jqInput.val ''
+            @jqInput.val 'Insert your message here'
             @sendMessage message

-
+        $(window).resize () =>
+            do @scrollPane.reinitialise
+
         EventManager.addEvent "sendTextMessage", (message) =>
             NetworkManager.sendToAll message
         EventManager.addEvent "receiveTextMessage", (message) =>


Fix missing quotes

Fix missing quotes

--- src/fs_utils/generate.coffee
+++ src/fs_utils/generate.coffee
@@ -124,7 +124,7 @@
   debug "Concatenating #{files.map((_) -> _.path).join(', ')} to #{path}"
   files.forEach (file) ->
     root.add file.node
-    root.add ';' if type is javascript
+    root.add ';' if type is 'javascript'
     #debug JSON.stringify(file.node)
     root.setSourceContent file.node.source, file.source


fix one column table separator

fix one column table separator


parseLocation: Support single hostnames as domain.


Add failing spec

Add failing spec

--- spec/autosave-spec.coffee
+++ spec/autosave-spec.coffee
@@ -148,3 +148,15 @@

       expect(initialActiveItem.save).toHaveBeenCalled()
       expect(otherItem1.save).toHaveBeenCalled()
+
+  it "saves via the item's Pane so that write errors are handled via notifications", ->
+    saveError = new Error('Save failed')
+    saveError.code = 'EACCES'
+    saveError.path = initialActiveItem.getPath()
+    initialActiveItem.save.andThrow(saveError)
+    initialActiveItem.insertText('a')
+
+    atom.config.set('autosave.enabled', true)
+    atom.workspace.destroyActivePaneItem()
+
+    expect(initialActiveItem.save).toHaveBeenCalled()


Switch resolvename functionality to new tinymce 4 method

--- src/textorum/tinymce/plugin.coffee
+++ src/textorum/tinymce/plugin.coffee
@@ -152,10 +152,11 @@
         editor.serializer.addAttributeFilter 'id', that.tree.attributeFilterCallback
       editor.onInit.add (editor) ->

-      if editor.theme.onResolveName
-        editor.theme.onResolveName.add (theme, path_object) ->
-          if path_object.node.getAttribute?('data-xmlel')
-            path_object.name = path_object.node.getAttribute('data-xmlel')
+      editor.on('ResolveName', (e) ->
+        path_object = e.target
+        if path_object.getAttribute?('data-xmlel')
+          e.name = path_object.getAttribute('data-xmlel')
+      )

     getInfo: ->
       {


Do not remove permanent .popover--notifications widget.

--- app/assets/javascripts/app/controllers/_application_controller.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.coffee
@@ -469,7 +469,9 @@
       @userTicketPopupsList.popover('destroy')

   anyPopoversDestroy: ->
-    $('.popover').remove()
+
+    # do not remove permanent .popover--notifications widget
+    $('.popover:not(.popover--notifications)').remove()

   recentView: (object, o_id) =>
     params =


fixing drag and drop

fixing drag and drop

Former-commit-id: 851ecdac471842097038e85d8b2fb71c021d1b40 [formerly 9ed2eee206087b91fe16b813826b3b8747ecb799] [formerly 379086bf94d94d8dfcfec69fd45ee78fe76ad2a9 [formerly 379086bf94d94d8dfcfec69fd45ee78fe76ad2a9 [formerly 938128626864458457915e7d413075bb289dab86]]]
Former-commit-id: ccf55aba1ab8979513d61313291f1165981ae15e [formerly 40f00c5a0418773abe918a8d68c07a7bd96cdd50]
Former-commit-id: a6c6b98f4b70cddf94954a20d9a3df9f72efbdec
Former-commit-id: bd95dd3ad50a7eee70823b0f74abc3d9ceb8f017

--- app/assets/javascripts/backbone/apps/franklin/point/point_view.coffee
+++ app/assets/javascripts/backbone/apps/franklin/point/point_view.coffee
@@ -48,8 +48,11 @@
       @trigger 'point:unhighlight_includers'

     disableDrag : ->
-      @$el.find('.m-point-wrap').draggable 'destroy'
-
+      try
+        @$el.find('.m-point-wrap').draggable 'destroy'
+      catch e
+        # get here when nav to results page before draggable created
+
     enableDrag : ->
       @$el.find('.m-point-wrap').draggable
         revert: "invalid"


eliminated unnecessary calls to Array::reverse()

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -116,10 +116,9 @@
           t1
         when 'expr'
           a = node[1...]
-          a.reverse()
-          h = for child in a then visit child
-          h.reverse()
-          a.reverse()
+          h = Array a.length
+          for i in [a.length - 1 .. 0]
+            h[i] = visit a[i]

           # Form vectors from sequences of data
           i = 0


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -11,7 +11,7 @@
   cooldown: (name, options={}) ->
     options.target ||= 0
     options.approachBy = 1 unless options.approachBy?
-
+    options

     unless I.cooldowns[name]
       I.cooldowns[name] = options


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -41,7 +41,7 @@

     player.update(1)

-    player.I.shootTimer # => 99
+    player.I.turboTimer # => 995

 @name Cooldown
 @module


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -13,7 +13,9 @@
     options.approachBy = 1 unless options.approachBy?

     unless I.cooldowns[name]
-      I.cooldowns[name] = {}
+      I.cooldowns[name] = {
+
+      }

       if options.value?


Fix deprecation; remove unnecessary Workspace reference

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -1,10 +1,7 @@
-{Workspace} = require 'atom'
-
 describe "VimMode", ->
   [editor, editorElement, workspaceElement] = []

   beforeEach ->
-    atom.workspace = new Workspace
     workspaceElement = atom.views.getView(atom.workspace)

     waitsForPromise ->


Support \small, \large, etc.  Fix #198

Support \small, \large, etc.  Fix #198


Don't try to clone packages without repo urls

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -41,7 +41,8 @@
             console.error(error.message) if error?
             logProgress()
             callback()
-        else
+
+        else if pack.respository?.url?
           command = "git clone --depth=1 \"#{pack.repository.url}\" \"#{clonePath}\""
           child_process.exec command, (error) ->
             console.error(error.message) if error?


Finishing up the documentation of existing code

--- lib/patient.coffee
+++ lib/patient.coffee
@@ -16,7 +16,15 @@
   @constructs
   ###
   constructor: (@code, @codeSystemName) ->
+
+  ###*
+  @returns {String} the code
+  ###
   code: -> @code
+
+  ###*
+  @returns {String} the code system name
+  ###
   codeSystemName: -> @codeSystemName

 ###*
@@ -84,9 +92,15 @@
   given: -> @json['first']
   family: -> @json['last']

+  ###*
+  @returns {Date} containing the patient's birthdate
+  ###
   birthtime: ->
     dateFromUtcSeconds @json['birthdate']

+  ###*
+  @returns {Array} A list of {@link Encounter} objects
+  ###
   encounters: ->
     for encounter in @json['encounters']
       new Encounter encounter


Fixed issue causing the server to lock up and the client to re-send requests. Better performance importing now.

--- src/EntityImporter.coffee
+++ src/EntityImporter.coffee
@@ -53,7 +53,8 @@
       return Q.reject('Either c3mls or fileId must be provided for importing.')
     c3mlsPromise.fail(df.reject)
     c3mlsPromise.then Meteor.bindEnvironment (c3mls) =>
-      df.resolve @fromC3mls(Setter.merge(args, {c3mls: c3mls}))
+      args.c3mls = c3mls
+      df.resolve @fromC3mls(args)
     df.promise

   fromC3mls: (args) ->
@@ -434,5 +435,4 @@

   Meteor.methods
     'entities/from/asset': (args) ->
-      @unblock()
       Promises.runSync -> EntityImporter.fromAsset(args)


retrieve after all files are ready

retrieve after all files are ready

--- client.iced
+++ client.iced
@@ -122,15 +122,19 @@
     stats.cookie = data.cookie
     stats.tasks = data.tasks
     for task in stats.tasks
+      task.finished = true
       for file in task.files
         file.status = 'warning'
         file.statusLabel = '未就绪'
         if file.url
           file.status = 'success'
           file.statusLabel = '就绪'
-          unless stats.retrieves.filter((r)-> r.task.id == task.id).length
-            stats.retrieves.push
-              task: task
+        else
+          task.finished = false
+      if task.finished
+        unless stats.retrieves.filter((r)-> r.task.id == task.id).length
+          stats.retrieves.push
+            task: task
     cb()
   deleteTask: (id, cb)->
     if stats.retrieving?.task.id == id


Fixed arrow-key scrolling.  Closes #21

Fixed arrow-key scrolling.  Closes #21

--- jquery.immybox.coffee
+++ jquery.immybox.coffee
@@ -158,14 +158,17 @@
     scroll: ->
       resultsHeight = @queryResultArea.height()
       resultsTop = @queryResultArea.scrollTop()
+      resultsBottom = resultsTop + resultsHeight
+
       highlightedChoice = @getHighlightedChoice()
-      highlightedChoiceTop = highlightedChoice.position().top
-      highlightedChoiceHeight = highlightedChoice.height()
-
-      if highlightedChoiceTop + highlightedChoiceHeight <= 0
-        @queryResultArea.scrollTop resultsTop - highlightedChoiceHeight
-      else if (highlightedChoiceTop + highlightedChoiceHeight) > resultsHeight
-        @queryResultArea.scrollTop highlightedChoiceTop + highlightedChoiceHeight - resultsHeight + resultsTop
+      highlightedChoiceHeight = highlightedChoice.outerHeight()
+      highlightedChoiceTop = highlightedChoice.position().top + resultsTop
+      highlightedChoiceBottom = highlightedChoiceTop + highlightedChoiceHeight
+
+      if highlightedChoiceTop < resultsTop
+        @queryResultArea.scrollTop highlightedChoiceTop
+      if highlightedChoiceBottom > resultsBottom
+        @queryResultArea.scrollTop highlightedChoiceBottom - resultsHeight

     positionResultsArea: ->


Swap order of "half-in" dialog buttons, fixes #62

--- src/scripts/controllers/Marker.coffee
+++ src/scripts/controllers/Marker.coffee
@@ -49,7 +49,7 @@
     askIfHalfVisible: =>
       dialog = new Dialog
         content: "Is at least half of that #{@annotation.value.species} visible in the image?"
-        buttons: [{'No': false}, {'Yes': true}]
+        buttons: [{'Yes': true}, {'No': false}]
         target: @picker.classifier.el.parent()
         className: 'classifier'
         done: (halfInValue) =>


fix #32

fix #32

--- lib/watcher.coffee
+++ lib/watcher.coffee
@@ -49,19 +49,21 @@

   deactivate: ->
     d 'deactivate'
+
+    if @ripper?
+      @ripper.dispose()
+      @ripper = null
+
     # Stop listening
     @cursorMoved = false

     @eventCursorMoved = off
     @eventBufferChanged = off
     clearTimeout @cursorMovedTimeoutId
-
-    @ripper?.dispose()

     # Remove references
     @cursorMovedTimeoutId = null
     @module = null
-    @ripper = null
     @renamingCursor = null
     @renamingMarkers = null


lower snapshot interval

lower snapshot interval

--- src/smc-util/syncstring_schema.coffee
+++ src/smc-util/syncstring_schema.coffee
@@ -84,7 +84,7 @@
                 string_id         : (obj, db) -> db.sha1(obj.project_id, obj.path)
                 users             : null
                 last_snapshot     : null
-                snapshot_interval : 400
+                snapshot_interval : 300
                 project_id        : null
                 path              : null
                 deleted           : null


Adding properties of servicebus message

Adding properties of servicebus message

--- src/sources/service.bus.source.coffee
+++ src/sources/service.bus.source.coffee
@@ -9,11 +9,12 @@

 module.exports =
-  newNotification: ({ context: { bindingData : { enqueuedTimeUtc, deliveryCount } }, message }) ->
+  newNotification: ({ context: { bindingData : { enqueuedTimeUtc, deliveryCount, properties } }, message }) ->
     message: _.omit message, "Sent"
     meta:
       insertionTime: enqueuedTimeUtc
       dequeueCount: deliveryCount
+      properties: properties
     type: "sb"

   delayObserver: create DelayObserver


Now that pusher is wrapped in a class, proxy the subscribe call

--- js/libs/pusher.coffee
+++ js/libs/pusher.coffee
@@ -9,7 +9,7 @@
     window.Pusher.channel_auth_endpoint = "/auth/pusher"

     @userSubscribePrivateChannel()
-    @pusherSetupBindings()
+    @setupBindings()

   userSubscribePrivateChannel: () =>
@@ -17,6 +17,11 @@
     @user_channel = @pusher.subscribe(channel_name)
     @user_channel.bind('pusher:subscription_error', (status) -> notifyError status)

-  pusherSetupBindings: () =>
+  subscribe: (args...) =>
+    @pusher.subscribe.apply @pusher, args
+
+
+
+  setupBindings: () =>
     @user_channel.bind "call", (data) =>
       this[data.fn].apply(this, data.args)


use defaultzoom level for zoombar

use defaultzoom level for zoombar

Former-commit-id: 5698255fa2743039a59a0f98edcbd52c16a97970 [formerly bc257d6f57f2a6f2bafc94632485d40addf21055]
Former-commit-id: fe3b252d5e6fccfe9a86ccf6d4f16bbc79beeb20

--- js/msa/zoombar.coffee
+++ js/msa/zoombar.coffee
@@ -7,7 +7,11 @@
       zoomSlider.type = "range"
       zoomSlider.name = "points"
       zoomSlider.min = 1
-      zoomSlider.max = 15
+      zoomSlider.max = 30
+      zoomSlider.style.width = "60%"
+
+      zoomSlider.value = @msa.zoomer.level
+
       zoomForm.appendChild zoomSlider
       @locked = false

@@ -29,7 +33,7 @@
     _reDraw: =>
       console.log @zoomSlider.value
       value = @zoomSlider.value
-      @msa.zoomer.setZoomLevel(value * 2)
+      @msa.zoomer.setZoomLevel(value)
       @msa.config.autofit = false
       @msa.redraw('stage')
       @msa.redraw('marker')


Updated atom.io configuration

Updated atom.io configuration

--- config.cson
+++ config.cson
@@ -25,6 +25,7 @@
       beautify_on_save: true
   "autocomplete-java":
     classpathFilePath: ".atom-classpath"
+    refreshClassOnSave: false
   core:
     closeDeletedFileTabs: true
     followSymlinks: false


update due to atom API change

update due to atom API change

--- keymaps/git-plus.cson
+++ keymaps/git-plus.cson
@@ -20,10 +20,10 @@
   'ctrl-shift-a s': 'git-plus:status'
   'ctrl-shift-a a': 'git-plus:add-all-and-commit'

-'.platform-darwin .editor':
+'.platform-darwin .atom-text-editor':
   'cmd-shift-a': 'git-plus:add'
   'cmd-shift-a c': 'git-plus:add-and-commit'

-'.platform-win32 .editor, .platform-linux .editor':
+'.platform-win32 .atom-text-editor, .platform-linux .atom-text-editor':
   'ctrl-shift-a': 'git-plus:add'
   'ctrl-shift-a c': 'git-plus:add-and-commit'


Centralize randomizing logic

Centralize randomizing logic

--- src/highfive.coffee
+++ src/highfive.coffee
@@ -130,6 +130,11 @@
         userFetcher from_user, to_user, (from_obj, to_obj) ->
             robot.logger.debug "from #{from_obj?.email} to #{to_obj?.email}"

+            double_rate = parseFloat(process.env.HUBOT_HIGHFIVE_DOUBLE_RATE || 0.1)
+            boomerang_rate = parseFloat(process.env.HUBOT_HIGHFIVE_BOOMERANG_RATE || 0.1)
+            do_double = Math.random() < double_rate
+            do_boomerang = Math.random() < boomerang_rate
+
             # Safety checks:
             # Don't target a nonexistent user or a robot
             if to_obj?.is_bot
@@ -156,8 +161,7 @@
             chatService.message roomid, from_obj, to_obj, reason

             # Daily Double! Randomly double the award amount
-            double_rate = parseFloat(process.env.HUBOT_HIGHFIVE_DOUBLE_RATE || 0.1)
-            if Math.random() < double_rate
+            if do_double
                 robot.logger.debug "DAILY DOUBLE #{amt} -> #{amt*2}"
                 amt *= 2
                 chatService.double roomid, doubleChooser
@@ -188,8 +192,7 @@
                     ]

                     # Randomly high-five back $10
-                    boomerang_rate = parseFloat(process.env.HUBOT_HIGHFIVE_BOOMERANG_RATE || 0.1)
-                    if Math.random() < boomerang_rate
+                    if do_boomerang
                         robot.logger.debug "BOOMERANG"
                         return tango(robot).order msg, from_obj, from_obj, 10, 'Boomerang'
                         , (order) ->


flag-based solution to #399 (17 new failures)

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1345,6 +1345,7 @@
   rootEvent = undefined
   waiters = []
   afters = []
+  processingAfters = false
   afterTransaction = (f) ->
     if rootEvent
       afters.push(f)
@@ -1382,9 +1383,14 @@
         flush()
       finally
         rootEvent = undefined
-        while (afters.length)
-          f = afters.shift()
-          f()
+        if not processingAfters
+          processingAfters = true
+          try
+            while (afters.length)
+              f = afters.shift()
+              f()
+          finally
+            processingAfters = false
         invalidateDeps()
       result


Bugfix: reset the marking surface selection for tools that can only appear once.

--- overrides.coffee
+++ overrides.coffee
@@ -62,6 +62,7 @@
       if tool.constructor.count > 1
         tool.deselect()
         tool.destroy()
+        @selection = null

       {label} = decisionTree.currentTask.getChoice() ? ''
       legend = tool.controls?.el.querySelector 'legend'


Update expression for matching Webpack configs (#616)

--- config.cson
+++ config.cson
@@ -4299,7 +4299,7 @@
 		icon: "webpack"
 		priority: 2
 		match: [
-			[/webpack(\.\w+)*\.conf(ig)?\./i, "medium-blue"]
+			[/webpack(\.[\w\-]+)*\.conf(ig)?\./i, "medium-blue"]
 			[/^webpackfile\.js$/i, "medium-blue"]
 		]


adding some configs

adding some configs

--- config.cson
+++ config.cson
@@ -16,6 +16,12 @@
   'disabledPackages': [
     'metrics'
   ]
-  'projectHome': '/Users/bradleywogsland/Projects'
-  'linter-phpcs': 'phpExecutablePath': '/usr/bin/php'
-  'linter-jshint': 'jshintExecutablePath': '/usr/local/bin/jshint'
+  'ignoredNames': [
+    '.git'
+    '.svn'
+    '.DS_Store'
+  ]
+'projectHome': '/Users/bradleywogsland/Projects'
+'linter-phpcs': 'phpExecutablePath': '/usr/bin/php'
+'linter-jshint': 'jshintExecutablePath': '/usr/local/bin/jshint'
+'autosave': {}