Fixes and improvements to Ukrainian translation (#4038)

* Added new and fixed existing translations for uk.coffee.

* More fixes to uk.coffee.

--- app/locale/uk.coffee
+++ app/locale/uk.coffee
@@ -14,7 +14,7 @@
     our_courses: "Наша програма була протестована, щоб бути ефективною в класі, навіть для вчителів з мінімальним досвідом програмування."
     top_screenshots_hint: "Учні пишуть код і бачать усі зміни наживо"
     designed_with: "Зважаючи на потреби вчителя"
-    real_code: "Кодування"
+    real_code: "Справжній код"
     from_the_first_level: "з першого рівня"
     getting_students: "Написання коду самостійно сприяє вивченню синтаксису та структури програми."
     educator_resources: "Путівник по курсах"
@@ -22,49 +22,49 @@
     teaching_computer_science: "Щоб викладати комп'ютерні науки не обов'язково мати науковий ступінь, адже ми підтримуємо вчителів з будь-яким досвідом."
     accessible_to: "Доступно"
     everyone: "кожному"
-    democratizing: "Навчання, доступне кожному — наша головна мета. Кожен повинен мати можливість навчитись програмувати."
+    democratizing: "Навчання, доступне кожному, — наша головна мета. Кожен повинен мати можливість навчитись програмувати."
     forgot_learning: "Таке враження, що, граючи, вони забули, що це навчання."
     wanted_to_do: " Програмування — те, чим я завжди хотів займатись, ніколи не думав, що буду вчити це в школі."
     why_games: "Чому навчання через гру важливе?"
     games_reward: "Гра — винагорода за зусилля."
     encourage: "Гра заохочує спілкуватись, відкривати щось нове і вчитись на помилках. Хороша гра змушує гравця весь час покращувати власні навички, що подібно до процесу роботи учнів під час навчання."
-#    excel: "Games excel at rewarding"
-#    struggle: "productive struggle"
-#    kind_of_struggle: "the kind of struggle that results in learning that’s engaging and"
-#    motivating: "motivating"
-#    not_tedious: "not tedious."
-#    gaming_is_good: "Studies suggest gaming is good for children’s brains. (it’s true!)"
-#    game_based: "When game-based learning systems are"
-#    compared: "compared"
-#    conventional: "against conventional assessment methods, the difference is clear: games are better at helping students retain knowledge, concentrate and"
-#    perform_at_higher_level: "perform at a higher level of achievement"
-#    feedback: "Games also provide real-time feedback that allows students to adjust their solution path and understand concepts more holistically, instead of being limited to just “correct” or “incorrect” answers."
-#    real_game: "A real game, played with real coding."
-#    great_game: "A great game is more than just badges and achievements - it’s about a player’s journey, well-designed puzzles, and the ability to tackle challenges with agency and confidence."
-#    agency: "CodeCombat is a game that gives players that agency and confidence with our robust typed code engine, which helps beginner and advanced students alike write proper, valid code."
-#    request_demo_title: "Get your students started today!"
-#    request_demo_subtitle: "Request a demo and get your students started in less than an hour."
-#    get_started_title: "Set up your class today"
-#    get_started_subtitle: "Set up a class, add your students, and monitor their progress as they learn computer science."
-#    request_demo: "Request a Demo"
-#    setup_a_class: "Set Up a Class"
-#    have_an_account: "Have an account?"
-#    logged_in_as: "You are currently logged in as"
-#    computer_science: "Computer science courses for all ages"
-#    show_me_lesson_time: "Show me lesson time estimates for:"
-#    curriculum: "Total curriculum hours:"
-#    ffa: "Free for all students"
-#    lesson_time: "Lesson time:"
-#    coming_soon: "More coming soon!"
-#    courses_available_in: "Courses are available in JavaScript and Python. Web Development courses utilize HTML, CSS, jQuery, and Bootstrap."
-#    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"
-#    view_progress: "View Progress"
-#    go_to_courses: "Go to My Courses"
-#    want_coco: "Want CodeCombat at your school?"
+    excel: "Ігри особливо ефективні при заохоченні до"
+    struggle: "прикладання зусиль"
+    kind_of_struggle: "- того різновиду зусиль що стимулює навчання, заохочує і"
+    motivating: "мотивує"
+    not_tedious: "а не видається нудним."
+    gaming_is_good: "Дослідження показуть, що ігри є корисними для дитячого розуму. (І це правда!)"
+    game_based: "Коли навчальні системи, зроблені на основі ігор,"
+    compared: "порівнюються"
+    conventional: "з традиційними методами оцінювання, різниця стає очевидною: ігри краще допомагають учням отримувати знання, зосереджуватись та"
+    perform_at_higher_level: "досягати кращих результатів."
+    feedback: "Ігри також забезпечують зворотній зв'язок в реальному часі, що дозволяє учням вдосконалювати способи вирішення задач і більш цілісно розуміти концепції, виходячи за рамки відповідей на зразок “вірно” чи “не вірно”."
+    real_game: "Справжня гра в яку граєш за допомогою справжнього програмування."
+    great_game: "Хороша гра - це більше ніж медалі та досягнення. Це пригода для гравця, якісно виконані головоломки, можливість впевнено долати перешкоди."
+    agency: "CodeCombat - це гра яка за допомогою надійної платформи типізованого коду допомагає як новачкам, так і досвідченим гравцям писати якісний та коректний програмний код."
+    request_demo_title: "Розпочніть вчити ваших учнів вже сьогодні!"
+    request_demo_subtitle: "Запросіть демо версію і ваші учні зможуть почати навчання менш ніж за годину."
+    get_started_title: "Налаштуйте ваш клас вже сьогодні"
+    get_started_subtitle: "Налаштуйте клас, додайте ваших учнів та спостерігайте за їхнім прогресом у вивченні програмування."
+    request_demo: "Запит на демо версію"
+    setup_a_class: "Налаштуйте клас"
+    have_an_account: "Вже є акаунт?"
+    logged_in_as: "Ви увійшли як"
+    computer_science: "Курси програмування для будь-якого віку"
+    show_me_lesson_time: "Покажіть мені оцінку часу уроку для:"
+    curriculum: "Загальна кількість годин навчального плану:"
+    ffa: "Безкоштовно для всіх учнів"
+    lesson_time: "Час уроку:"
+    coming_soon: "Очікуйте скоро!"
+    courses_available_in: "Курси доступні на JavaScript, Python і Java (очікуйте скоро!). Курси веб програмування використовують HTML, CSS, jQuery та Bootstrap."
+    boast: "Цікаві задачі, достатньо складні щоб зачарувати як гравців, так і програмістів."
+    winning: "Виграшна комбінація рольової гри та завдань з програмування дозволила зробити навчання одночасно захоплюючим, цікавим та підходящим для дітей."
+    run_class: "Все, що вам необхідно, щоб сьогодні ж розпочати клас інформатики у вашій школі. Знання інформатики не обов'язкові!"
+    goto_classes: "Перейти до моїх класів"
+    view_profile: "Переглянути мій профіль"
+    view_progress: "Переглянути прогрес"
+    go_to_courses: "Перейти до моїх курсів"
+    want_coco: "Хочете CodeCombat у вашій школі?"

   nav:
     play: "Грати" # The top nav bar entry where players choose which levels to play
@@ -78,7 +78,7 @@
     home: "На головну"
     contribute: "Співпраця"
     legal: "Юридична інформація"
-#    privacy: "Privacy"
+    privacy: "Конфіденційність"
     about: "Про нас"
     contact: "Контакти"
     twitter_follow: "Стежити"
@@ -87,19 +87,19 @@
     careers: "Робота"
     facebook: "Facebook"
     twitter: "Twitter"
-#    create_a_class: "Create a Class"
+    create_a_class: "Створити клас"
     other: "Інше"
-#    learn_to_code: "Learn to Code!"
-#    toggle_nav: "Toggle navigation"
-#    jobs: "Jobs"
+    learn_to_code: "Вчи приграмування!"
+    toggle_nav: "Переключення навігації"
+    jobs: "Вакансії"
     schools: "Школи"
-#    get_involved: "Get Involved"
-#    open_source: "Open source (GitHub)"
+    get_involved: "Взяти участь"
+    open_source: "Відкритий код (GitHub)"
     support: "Підтримка"
-#    faqs: "FAQs"
+    faqs: "Часті Питання"
     help_pref: "Потрібна допомога? Надішліть Email"
     help_suff: "і ми будемо на зв'язку!"
-#    resource_hub: "Resource Hub"
+    resource_hub: "Ресурси"

   modal:
     close: "Закрити"
@@ -140,7 +140,7 @@
     subscription_required: "Потрібен абонемет"
     anonymous: "Гравець-анонім"
     level_difficulty: "Складність: "
-#    play_classroom_version: "Play Classroom Version" # Choose a level in campaign version that you also can play in one of your courses
+    play_classroom_version: "Грати в версію для класу" # Choose a level in campaign version that you also can play in one of your courses
     campaign_beginner: "Кампанія для початківців"
     awaiting_levels_adventurer_prefix: "Ми щотижня додаємо нові рівні."
     awaiting_levels_adventurer: "Увійди як Шукач пригод"
@@ -209,7 +209,7 @@

   login:
     sign_up: "створення акаунту"
-#    email_or_username: "Email or username"
+    email_or_username: "Email або ім'я користувача"
     log_in: "Увійти"
     logging_in: "Триває процес входу"
     log_out: "Вийти"
@@ -225,7 +225,7 @@
     create_student_header: "Створити акаунт учня"
     create_teacher_header: "Створити акаунт вчителя"
     create_individual_header: "Створити індивідуальний акаунт"
-    email_announcements: "Отримувати анонси на email" # {change}
+    email_announcements: "Отримувати анонси про нові рівні та інші новинки CodeCombat!"
     creating: "Створення акаунту..."
     sign_up: "Реєстрація"
     log_in: "вхід з паролем"
@@ -233,59 +233,59 @@
     login_switch: "Уже маєте акаунт?"
     school_name: "Назва школи і місто"
     optional: "необов’язково"
-#    school_name_placeholder: "Example High School, Springfield, IL"
-#    connect_with: "Connect with:"
-#    connected_gplus_header: "You've successfully connected with Google+!"
-#    connected_gplus_p: "Finish signing up so you can log in with your Google+ account."
-#    gplus_exists: "You already have an account associated with Google+!"
-#    connected_facebook_header: "You've successfully connected with Facebook!"
-#    connected_facebook_p: "Finish signing up so you can log in with your Facebook account."
-#    facebook_exists: "You already have an account associated with Facebook!"
-#    hey_students: "Students, enter the class code from your teacher."
-#    birthday: "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"
-#    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"
-#    student_type_3: "Compete in arenas"
-#    student_type_4: "Choose your hero!"
-#    student_type_5: "Have your Class Code ready!"
-#    signup_as_student: "Sign up as a Student"
-#    individuals_or_parents: "Individuals & Parents"
-#    individual_type: "For players learning to code outside of a class. Parents should sign up for an account here."
-#    signup_as_individual: "Sign up as an Individual"
-#    enter_class_code: "Enter your Class Code"
-#    enter_birthdate: "Enter your birthdate:"
-#    parent_use_birthdate: "Parents, use your own birthdate."
-#    ask_teacher_1: "Ask your teacher for your Class Code."
-#    ask_teacher_2: "Not part of a class? Create an "
-#    ask_teacher_3: "Individual Account"
-#    ask_teacher_4: " instead."
-#    about_to_join: "You're about to join:"
-#    enter_parent_email: "Enter your parent’s email address:"
-#    parent_email_error: "Something went wrong when trying to send the email. Check the email address and try again."
-#    parent_email_sent: "We’ve sent an email with further instructions on how to create an account. Ask your parent to check their inbox."
-#    account_created: "Account Created!"
-#    confirm_student_blurb: "Write down your information so that you don't forget it. Your teacher can also help you reset your password at any time."
-#    confirm_individual_blurb: "Write down your login information in case you need it later. Verify your email so you can recover your account if you ever forget your password - check your inbox!"
-#    write_this_down: "Write this down:"
-#    start_playing: "Start Playing!"
-#    sso_connected: "Successfully connected with:"
-#    select_your_starting_hero: "Select Your Starting Hero:"
-#    you_can_always_change_your_hero_later: "You can always change your hero later."
+    school_name_placeholder: "Школа №1, м. Ялинківка, Київська обл."
+    connect_with: "Увійти за допомогою:"
+    connected_gplus_header: "Ви успішно увійшли за допомогою Google+!"
+    connected_gplus_p: "Завершіть авторизацію щоб увійти за допомогою акаунту Google+."
+    gplus_exists: "У вас вже є акаунт прив'язаний до Google+!"
+    connected_facebook_header: "Ви успішно увійшли за допомогою Facebook!"
+    connected_facebook_p: "Завершіть авторизацію щоб увійти за допомогою акаунту Facebook."
+    facebook_exists: "У вас вже є акаунт прив'язаний до Facebook!"
+    hey_students: "Учні, введіть код від класу вашого вчителя."
+    birthday: "День народження"
+    parent_email_blurb: "Ми знаємо, що ти з нетерпінням хочеш навчитись програмувати — ми також не можемо дочекатись! Твої батьки отримають листа з інструкціями як створити для тебе акаунт. Напиши на {{email_link}} якщо виникли запитання."
+    classroom_not_found: "Ми не змогли знайти клас з таким кодом. Перевірте правильність написання або попросіть вашого вчителя про допомогу."
+    checking: "Перевірка..."
+    account_exists: "Цей email вже використовується:"
+    sign_in: "Увійти"
+    email_good: "Email в порядку!"
+    name_taken: "Ім'я вже використовується! Спробуйте {{suggestedName}}?"
+    name_available: "Ім'я доступне!"
+    name_is_email: "Ім'я користувача не повинно бути email-адресою"
+    choose_type: "Виберіть тип акаунту:"
+    teacher_type_1: "Навчайте програмуванню за допомогою CodeCombat!"
+    teacher_type_2: "Налаштовуйте ваш клас"
+    teacher_type_3: "Отримайте доступ до навчальних матеріалів"
+    teacher_type_4: "Слідкуйте за прогресом учнів"
+    signup_as_teacher: "Зареєструйтесь як Вчитель"
+    student_type_1: "Вчіть програмування поки граєте в захоплюючу гру!"
+    student_type_2: "Грайте разом зі своїм класом"
+    student_type_3: "Змагайтесь на аренах"
+    student_type_4: "Оберіть свого героя!"
+    student_type_5: "Потрібно мати актуальний код класу!"
+    signup_as_student: "Зареєструйтесь як Учень"
+    individuals_or_parents: "Індивідуальні гравці та Батьки"
+    individual_type: "Для гравців що навчаються без класу. Батьки повинні зареєструватись тут."
+    signup_as_individual: "Зареєструйтесь як Індивідуальний гравець"
+    enter_class_code: "Введіть код класу"
+    enter_birthdate: "Введіть дату народження:"
+    parent_use_birthdate: "Батьки, використовуйте вашу особисту дату народження."
+    ask_teacher_1: "Запитайте вашого вчителя про код класу."
+    ask_teacher_2: "Не перебуваєте в класі? Створіть "
+    ask_teacher_3: "Особистий Акаунт"
+    ask_teacher_4: " замість цього."
+    about_to_join: "Ви приєднуєтесь до:"
+    enter_parent_email: "Введи email твоїх батьків:"
+    parent_email_error: "Щось пішло не так під час надсилання листа. Перевір email і спробуй знову."
+    parent_email_sent: "Ми надіслали листа з інструкціями про те, як створити акаунт. Попроси батьків перевірити вхідні повідомлення на email."
+    account_created: "Акаунт створено!"
+    confirm_student_blurb: "Запишіть ваші дані щоб не забути їх. Ваш вчитель зможе допомогти скинути пароль в будь-який час."
+    confirm_individual_blurb: "Занотуйте інформацію для входу в систему, вона може знадобитися пізніше. Перевірте email (перевірте вхідні повідомлення), щоб ви могли відновити доступ до вашого акаунту якщо забудете пароль!"
+    write_this_down: "Запишіть це:"
+    start_playing: "Розпочніть гру!"
+    sso_connected: "Ви успішно підключились за допомогою:"
+    select_your_starting_hero: "Виберіть вашого першого героя:"
+    you_can_always_change_your_hero_later: "Ви завжди зможете змінити героя пізніше."

   recover:
     recover_account_title: "Відновити акаунт"
@@ -302,17 +302,17 @@

   common:
     back: "Назад" # When used as an action verb, like "Navigate backward"
-#    coming_soon: "Coming soon!"
+    coming_soon: "Очікуйте скоро!"
     continue: "Вперед" # When used as an action verb, like "Continue forward"
-#    default_code: "Default Code"
+    default_code: "Код за замовчуванням"
     loading: "Завантаження..."
-#    overview: "Overview"
-#    solution: "Solution"
-#    intro: "Intro"
+    overview: "Огляд"
+    solution: "Рішення"
+    intro: "Вступ"
     saving: "Збереження..."
     sending: "Надсилання..."
-    send: "Надіслано"
-#    sent: "Sent"
+    send: "Надіслати"
+    sent: "Надіслано"
     cancel: "Скасувати"
     save: "Зберегти"
     publish: "Опублікувати"
@@ -328,7 +328,7 @@
     submit_patch: "Надіслати патч"
     submit_changes: "Надіслати зміни"
     save_changes: "Зберегти зміни"
-    required_field: "Обов'язкове поле" # {change}
+    required_field: "обов'язкове поле"

   general:
     and: "та"
@@ -382,7 +382,7 @@
     wizard: "Чаклун"
     first_name: "Ім'я"
     last_name: "Прізвище"
-#    last_initial: "Last Initial"
+    last_initial: "Ініціали прізвища"
     username: "Ім'я користувача"

   units:
@@ -408,9 +408,9 @@
     done: "Готово"
     next_level: "Наступний Рівень"
     next_game: "Наступна гра"
-#    language: "Language"
-#    languages: "Languages"
-#    programming_language: "Programming language"
+    language: "Мова"
+    languages: "Мови"
+    programming_language: "Мова програмування"
     show_menu: "Показати меню гри"
     home: "На головну" # Not used any more, will be removed soon.
     level: "Рівень" # Like "Level: Dungeons of Kithgard"
@@ -432,34 +432,34 @@
     victory: "Перемога"
     victory_title_prefix: ""
     victory_title_suffix: " завершено"
-    victory_sign_up: "Підписатись на оновлення"
-    victory_sign_up_poke: "Хочете отримувати останні новини на email? Створіть безкоштовний акаунт, і ми будемо тримати Вас у курсі!"
-    victory_rate_the_level: "Оцінити рівень: " # {change}
+    victory_sign_up: "Зареєструватись щоб зберегти прогрес"
+    victory_sign_up_poke: "Хочете зберегти ваш код? Створіть безкоштовний акаунт!"
+    victory_rate_the_level: "Наскільки цікавим був цей рівень?"
     victory_return_to_ladder: "Повернутися до таблиці рівнів"
     victory_saving_progress: "Триває збереження прогресу"
     victory_go_home: "На головну"
     victory_review: "Розкажіть нам більше!"
-    victory_review_placeholder: "Вам сподобався рівень?"
+    victory_review_placeholder: "Як вам цей рівень?"
     victory_hour_of_code_done: "Ви закінчили?"
-    victory_hour_of_code_done_yes: "Так, я закінчив Годину Коду!"
+    victory_hour_of_code_done_yes: "Так, я закінчив Годину Коду™!"
     victory_experience_gained: "Отримано досвіду"
     victory_gems_gained: "Отримано самоцвітів"
     victory_new_item: "Новий предмет"
-#    victory_new_hero: "New Hero"
+    victory_new_hero: "Новий герой"
     victory_viking_code_school: "Хай йому грець! Цей рівень був важким, але Ви справились! Якщо Ви ще не програміст, вам слід ним стати. Ви щойно екстерном пройшли процедуру прийняття у Viking Code School, де Ви матимете змогу значно покращити свої здібності та стати професійним веб-розробником за 14 тижнів."
     victory_become_a_viking: "Стати вікінгом"
-#    victory_no_progress_for_teachers: "Progress is not saved for teachers. But, you can add a student account to your classroom for yourself."
+    victory_no_progress_for_teachers: "Прогрес для вчителів не зберігається. Але ви можете створити для себе акаунт учня у вашій кімнаті."
     guide_title: "Посібник"
     tome_cast_button_run: "Виконати"
     tome_cast_button_running: "Виконання"
     tome_cast_button_ran: "Виконати"
     tome_submit_button: "Підтвердити"
-    tome_reload_method: "Відновити початковий код цього методу" # {change}
+    tome_reload_method: "Відновити оригінальний код щоб перезапустити рівень"
     tome_available_spells: "Доступні заклинання"
     tome_your_skills: "Ваші вміння"
     tome_current_method: "Поточний метод"
-#    hints: "Hints"
-#    hints_title: "Hint {{number}}"
+    hints: "Підказки"
+    hints_title: "Підказка {{number}}"
     code_saved: "Код збережено"
     skip_tutorial: "Пропустити (esc)"
     keyboard_shortcuts: "Клавіатурні скорочення"
@@ -532,7 +532,7 @@
 #    tip_life_and_polymorphism: "Open Source is like a totally polymorphic heterogeneous structure: All types are welcome."
     tip_mistakes_proof_of_trying: "Помилки у вашому коді є лише доказом того, що ви намагаєтеся."
 #    tip_adding_orgres: "Rounding up ogres."
-#    tip_sharpening_swords: "Sharpening the swords."
+    tip_sharpening_swords: "Нагострюємо мечі."
 #    tip_ratatouille: "You must not let anyone define your limits because of where you come from. Your only limit is your soul. - Gusteau, Ratatouille"
 #    tip_nemo: "When life gets you down, want to know what you've gotta do? Just keep swimming, just keep swimming. - Dory, Finding Nemo"
 #    tip_internet_weather: "Just move to the internet, it's great here. We get to live inside where the weather is always awesome. - John Green"
@@ -543,17 +543,17 @@
 #    tip_programming_not_about_computers: "Computer Science is no more about computers than astronomy is about telescopes. - Edsger Dijkstra"
 #    tip_mulan: "Believe you can, then you will. - Mulan"

-#  play_game_dev_level:
-#    created_by: "Created by {{name}}"
-#    how_to_play_title: "How to play:"
-#    how_to_play_1: "Use the mouse to control the hero!"
-#    how_to_play_2: "Click anywhere on the map to move to that location."
-#    how_to_play_3: "Click on the ogres to attack them."
-#    restart: "Restart Level"
-#    play: "Play Level"
-#    play_more_codecombat: "Play More CodeCombat"
-#    default_student_instructions: "Click to control your hero and win your game!"
-#    back_to_coding: "Back to Coding"
+  play_game_dev_level:
+    created_by: "Створив: {{name}}"
+    how_to_play_title: "Як грати:"
+    how_to_play_1: "Використовуйте мишку щоб керувати героєм!"
+    how_to_play_2: "Клікніть де-небудь на карті щоб рухатись до того місця."
+    how_to_play_3: "Клікніть на огрів щоб атакувати їх."
+    restart: "Перезапустити рівень"
+    play: "Грати рівень"
+    play_more_codecombat: "Грати більше CodeCombat"
+    default_student_instructions: "Клікніть щоб керувати вашим героєм і виграти гру!"
+    back_to_coding: "Повернутись до коду"

   game_menu:
     inventory_tab: "Інвентар"
@@ -612,9 +612,9 @@

   subscribe:
     comparison_blurb: "Відточіть свої навички завдяки підписці на CodeCombat!"
-    feature1: "Більше __levelsCount__ основних рівней на просторах __worldsCount__ світів" # {change}
-    feature2: "__heroesCount__ могутніх нових героїв з унікальними здібностями!" # {change}
-    feature3: "Більше __bonusLevelsCount__-ти бонусних рівнів" # {change}
+    feature1: "__levelsCount__+ основних рівнів на просторах __worldsCount__ світів"
+    feature2: "__heroesCount__ могутніх нових героїв з унікальними здібностями!"
+    feature3: "__bonusLevelsCount__+ бонусних рівнів"
     feature4: "{{gems}} бонусних самоцвітів кожного місяця!"
     feature6: "Екслюзивна підтримка по електронній пошті"
     feature7: "Приватні клани"
@@ -643,16 +643,16 @@
     parents_title: "Дорога мамо/батьку, ваша дитина вчиться програмувати. Чи допоможите ви їй продовжити цю спрову?"
     parents_blurb1: "Ваша дитина уже завершила __nLevels__ рівнів та вивчила базові речі. Допоможіть культивувати її інтерес та придбайте їй абонемент аби вона могла продовжити грати."
     parents_blurb1a: "Коп'ютерне програмування є необхідними вмінням, що ваша дитина беззаперечно використовуватиме у дорослому віці. До 2020 року 77% професій потребуватимуть базових навичок у програмному забезпечені, а програмісти надзвичайно потрібні у всьому світі. Чи знали ви, що Комп'ютерні Науки - це найбільш високооплачувана університетьська спеціальність?"
-    parents_blurb2: "За {{price}}$ на місяць, вона отримуватиме нові завдання щотижня та персональні листи підтримки від професійних програмістів." # {change}
+    parents_blurb2: "За {{price}}$ на місяць ваша дитина отримає нові завдання щотижня та персональні листи підтримки від професійних програмістів."
     parents_blurb3: "Жодного ризику: 100% гарантія повернення грошей, легке скасування абонементу одним кліком."
     payment_methods: "Платіжні методи"
     payment_methods_title: "Платіжні методи, що приймаються"
-    payment_methods_blurb1: "Наразі ми приймаємо кредитні картки та Alpiay." # {change}
+    payment_methods_blurb1: "Наразі ми приймаємо кредитні картки та Alpiay. Ви також можете оплатити {{three_month_price}}$ через PayPal до nick@codecombat.com з email-адресою вашого акаунту в коментарі щоб придбати підписку на три місяці та самоцвіти, або {{year_price}}$ на рік."
     payment_methods_blurb2: "Якщо Вам необхідно використати інший спосіб оплати, будь ласка, зв'яжіться з нами."
     sale_button: "Розпродаж!"
-    sale_button_title: "Збережіть ${{discount}} при покупці абонементу на 1 рік" # {change}
+    sale_button_title: "Збережіть {{discount}}$ при покупці абонементу на 1 рік"
     stripe_description: "Щомісячний абонемент"
-    stripe_description_year_sale: "1 рік підписки (${{discount}} знижка)" # {change}
+    stripe_description_year_sale: "1 рік підписки ({{discount}}$ знижка)"
     subscription_required_to_play: "Аби грати в цьому рівні потрібен абонемент."
     unlock_help_videos: "Підпишіться, щоб відкрити усі навчальні відео."
     personal_sub: "Особистий абонемент" # Accounts Subscription View below
@@ -729,8 +729,8 @@
     current_value: "Поточне значення"
     default_value: "Типове значення"
     parameters: "Параметри"
-#    required_parameters: "Required Parameters"
-#    optional_parameters: "Optional Parameters"
+    required_parameters: "Обов'язкові параметри"
+    optional_parameters: "Необов'якові параметри"
     returns: "Повертає"
     granted_by: "Забезпечує"

@@ -756,39 +756,39 @@
   about:
     main_title: "Якщо ви хочете навчитися програмувати, вам потрібно писати код (багато коду)."
     main_description: "Тут, на CodeCombat, наше завдання - переконатися, що ви це робитимете з усмішкою на обличчі."
-#    mission_link: "Mission"
+    mission_link: "Місія"
     team_link: "Команда"
-#    story_link: "Story"
-#    press_link: "Press"
-#    mission_title: "Our mission: make programming accessible to every student on Earth."
-#    mission_description_1: "Programming is magic. It's the ability to create things from pure imagination. We started CodeCombat to give learners the feeling of wizardly power at their fingertips by using typed code."
-#    mission_description_2: "As it turns out, that enables them to learn faster too. WAY faster. It's like having a conversation instead of reading a manual. We want to bring that conversation to every school and to every student, because everyone should have the chance to learn the magic of programming."
-#    team_title: "Meet the CodeCombat team"
-#    team_values: "We value open and respectful dialog, where the best idea wins. Our decisions are grounded in customer research and our process is focused on delivering tangible results for them. Everyone is hands-on, from our CEO to our GitHub contributors, because we value growth and learning in our team."
-    nick_title: "Співзасновник" # {change}
+    story_link: "Історія"
+    press_link: "Преса"
+    mission_title: "Наша місія: зробити програмування доступним для кожного учня на Землі."
+    mission_description_1: "Програмування - це магія. Це можливість створювати речі виключно з уяви. Ми створили CodeCombat щоб дозволити учням відчути магічні сили на кінчиках їхніх пальців використовуючи типізований код."
+    mission_description_2: "Виявляється, що це дозволяє їм також і навчатися скоріше. ЗНАЧНО скоріше. Це як спілкування замість читання підручників. Ми хочемо донести цю розмову до кожної школи і до кожного учня, тому що коден повинен мати шанс навчитися магії програмування."
+    team_title: "Зустрічайте команду CodeCombat"
+    team_values: "Ми цінуємо відкритий і ввічливий діалог в якому найкращі ідеї перемагають. Наші рішення засновані на дослідженнях наших клієнтів, а наш процес фокусується на забезпеченні клієнтів суттєвими результатами. Кожен голос враховується, від нашого CEO до помічників на GitHub, тому що ми цінуємо ріст та навчання в нашій команді."
+    nick_title: "Співзасновник, CEO"
     nick_blurb: "Ґуру мотивації"
-    matt_title: "Співзасновник" # {change}
-    cat_title: "Головний ремісник" # {change}
+    matt_title: "Співзасновник, CTO"
+    cat_title: "Головний ремісник"
     cat_blurb: "Маг повітря"
-    scott_title: "Співзасновник" # {change}
+    scott_title: "Співзасновник, Інженер-програміст"
     scott_blurb: "Розумник"
-#    maka_title: "Customer Advocate"
-#    maka_blurb: "Storyteller"
-#    rob_title: "Software Engineer"
-#    rob_blurb: "Codes things and stuff"
-#    josh_c_title: "Game Designer"
-#    josh_c_blurb: "Designs games"
+    maka_title: "Адвокат клієнтів"
+    maka_blurb: "Казкар"
+    rob_title: "Інженер-програміст"
+    rob_blurb: "Програмує все"
+    josh_c_title: "Дизайнер гри"
+    josh_c_blurb: "Робить дизайни ігор"
 #    robin_title: "UX Design & Research"
 #    robin_blurb: "Scaffolding"
     josh_title: "Дизайнер гри"
     josh_blurb: "Підлога - це лава"
-#    phoenix_title: "Software Engineer"
-#    nolan_title: "Territory Manager"
-#    elliot_title: "Partnership Manager"
-#    elliot_blurb: "Mindreader"
+    phoenix_title: "Інженер-програміст"
+    nolan_title: "Менеджер територій"
+    elliot_title: "Менеджер партнерства"
+    elliot_blurb: "Читач думок"
 #    lisa_title: "School Specialist"
 #    lisa_blurb: "A gritty one"
-#    sean_title: "Territory Manager"
+    sean_title: "Менеджер територій"
     retrostyle_title: "Ілюстрація"
     retrostyle_blurb: "RetroStyle Games"
     jose_title: "Музика"
@@ -801,9 +801,9 @@
 #    community_description_link: "contribute page"
 #    community_description_2: "for more info."
 #    number_contributors: "Over 450 contributors have lent their support and time to this project."
-#    story_title: "Our story so far"
+    story_title: "Наша історія"
 #    story_subtitle: "Since 2013, CodeCombat has grown from a mere set of sketches to a living, thriving game."
-#    story_statistic_1a: "5,000,000+"
+    story_statistic_1a: "5,000,000+"
 #    story_statistic_1b: "total players"
 #    story_statistic_1c: "have started their programming journey through CodeCombat"
 #    story_statistic_2a: "We’ve been translated into over 50 languages — our players hail from"
@@ -824,33 +824,33 @@
 #    jobs_benefit_9: "10-year option exercise window"
 #    jobs_benefit_10: "Maternity leave: 10 weeks paid, next 6 @ 55% salary"
 #    jobs_benefit_11: "Paternity leave: 10 weeks paid"
-#    learn_more: "Learn More"
-#    jobs_custom_title: "Create Your Own"
+    learn_more: "Дізнатись більше"
+    jobs_custom_title: "Створити власну"
 #    jobs_custom_description: "Are you passionate about CodeCombat but don't see a job listed that matches your qualifications? Write us and show how you think you can contribute to our team. We'd love to hear from you!"
 #    jobs_custom_contact_1: "Send us a note at"
 #    jobs_custom_contact_2: "introducing yourself and we might get in touch in the future!"
-#    contact_title: "Press & Contact"
-#    contact_subtitle: "Need more information? Get in touch with us at"
-#    screenshots_title: "Game Screenshots"
-#    screenshots_hint: "(click to view full size)"
+    contact_title: "Преса та контакти"
+    contact_subtitle: "Потрібно більше інформації? Зв'яжіться з нами:"
+    screenshots_title: "Скріншоти гри"
+    screenshots_hint: "(клікніть щоб побачити в повному розмірі)"
 #    downloads_title: "Download Assets & Information"
-#    about_codecombat: "About CodeCombat"
-#    logo: "Logo"
-#    screenshots: "Screenshots"
+    about_codecombat: "Про CodeCombat"
+    logo: "Лого"
+    screenshots: "Скріншоти"
 #    character_art: "Character Art"
-#    download_all: "Download All"
-#    previous: "Previous"
+    download_all: "Завантажити все"
+    previous: "Попереднє"
 #    location_title: "We're located in downtown SF:"

   teachers:
     who_for_title: "Для кого призначений CodeCombat?"
-    who_for_1: "Ми рекомендуємо CodeCombat для учнів старше 9 років. Попередній досвід програмування не потрібен." # {change}
-    who_for_2: "Ми розробили CodeCombat так, щоб він підходив і хлопчикам і дівчаткам." # {change}
+    who_for_1: "Ми рекомендуємо CodeCombat для учнів старше 9 років. Попередній досвід програмування не потрібен. Дизайн CodeCombat розроблено щоб зацікавити як хлопчиків, так і дівчаток."
+    who_for_2: "Наша система курсів дозволяє вчителям налаштовувати класи, слідкувати за прогресом та давати додаткові матеріали учням за допомогою спеціального інтерфейсу."
     more_info_title: "Де я можу знайти більше інформації?"
     more_info_1: "Наш"
     more_info_2: "вчительський форум"
     more_info_3: "є гарним місцем для спілкування із колегами-педагогами, котрі використовують CodeCombat."
-#    licenses_needed: "Licenses needed"
+    licenses_needed: "Потрібна ліцензія"

 #  teachers_quote:
 #    name: "Demo Form"
@@ -948,8 +948,8 @@
     manage_subscription: "Натисніть тут для керування підпискою."
     new_password: "Новий пароль"
     new_password_verify: "Підтвердження паролю"
-    type_in_email: "Введіть свій email, аби підтвердити вилучення екаунту." # {change}
-    type_in_email_progress: "Уведіть вашу email-адресу щоб підтвердити видалення поступу."
+    type_in_email: "Введіть ваш email або ім'я користувача щоб підтвердити видалення акаунту."
+    type_in_email_progress: "Введіть ваш email щоб підтвердити видалення прогресу."
     type_in_password: "Також, введіть свій пароль."
     email_subscriptions: "Email-підписки"
     email_subscriptions_none: "Жодних підписок."
@@ -1009,7 +1009,7 @@
     social_facebook: "Вподобайте CodeCombat на Facebook"
     social_twitter: "Слідкуйте за CodeCombat у Twitter"
     social_gplus: "Приєднайтесь до CodeCombat у Google+"
-#    social_slack: "Chat with us in the public CodeCombat Slack channel"
+    social_slack: "Переписуйтесь з нами в публічному каналі CodeCombat Slack"
     contribute_to_the_project: "Взяти участь у розробці"

   clans:
@@ -1021,7 +1021,7 @@
     subs_only: "лише для підписників"
     create_clan: "Створити новий клан"
     private_preview: "Прев'ю"
-#    private_clans: "Private Clans"
+    private_clans: "Приватні клани"
     public_clans: "Публічні клани"
     my_clans: "Мої клани"
     clan_name: "Назва клану"
@@ -1052,7 +1052,7 @@
     started_2: "Розпочато"
     not_started_2: "Не розпочато"
     view_solution: "Натисніть аби переглянути рішення."
-#    view_attempt: "Click to view attempt."
+    view_attempt: "Натисніть аби переглянути спробу."
     latest_achievement: "Останнє досягнення"
     playtime: "Тривалість гри"
     last_played: "Остання гра"
@@ -1105,8 +1105,8 @@
     invite_link_p_1: "Дайте це посилання студентам, яких ви б хотіли запросити в клас."
     invite_link_p_2: "Або ми самі можемо написати їм листа на пошту:"
     capacity_used: "Використано слотів курсу:"
-    enter_emails: "Введіть email-и студентів по одному на рядок, яких ви хочете запросити" # {change}
-    send_invites: "Відправити запрошення" # {change}
+    enter_emails: "Відокремлюйте кожен email перенесенням рядка або комою"
+    send_invites: "Запросити учнів"
     creating_class: "Триває створення класу ... "
     purchasing_course: "Триває придбання курсу ... "
     buy_course: "Купити курс"
@@ -1283,8 +1283,8 @@
 #    select_level: "Select level"
 #    play_level: "Play Level"
     concepts_covered: "Концептів покрито"
-    print_guide: "Надрукувати Настанову (PDF)"
-    view_guide_online: "Переглянути Настанову в Режимі Online (PDF)" # {change}
+    print_guide: "Роздрукувати інструкцію (PDF)"
+    view_guide_online: "Переглянути огляд та рішення"
 #    grants_lifetime_access: "Grants access to all Courses."
 #    enrollment_credits_available: "Licenses Available:"
     language_select: "Оберіть Мову" # ClassroomSettingsModal
@@ -1717,7 +1717,7 @@
     scribe_introduction_url_mozilla: "Mozilla Developer Network"
     scribe_introduction_suf: ". Якщо Ваше уявлення про веселощі промовляє через принципи програмування у формі Markdown, тоді цей клас для Вас."
     scribe_attribute_1: "Вміння писати - це, по суті, все, що Вам треба. Не тільки знати граматику і правопис, але й могти розкривати складні ідеї іншим."
-    contact_us_url: "Зв'яжіться з нами" # {change}
+    contact_us_url: "Зв'яжіться з нами"
     scribe_join_description: ", розкажіть нам трохи про себе, свій досвід програмування і про які речі Ви хотіли б писати. З цього ми почнемо!"
     scribe_subscribe_desc: "Отрумивати листи з анонсами щодо написання статтей."
     diplomat_introduction_pref: "Отож, якщо є одна річ, яку ми вивчили з часу "
@@ -1783,7 +1783,7 @@
     tutorial_skip: "Пропустити навчання"
     tutorial_not_sure: "Не впевнені, що відбувається?"
     tutorial_play_first: "Спочатку пройти навчання."
-    simple_ai: "Простий ШІ" # {change}
+    simple_ai: "Простий ШІ"
     warmup: "Розігрів"
     friends_playing: "Дружня гра"
     log_in_for_friends: "Увійдіть та грайте за своїх друзів!"
@@ -1806,7 +1806,7 @@
     winners: "Переможці"
     league: "Ліга"
     red_ai: "Червоний ШІ" # "Red AI Wins", at end of multiplayer match playback
-    blue_ai: "Синій ШІ" # {change}
+    blue_ai: "Синій ШІ"
     wins: "переміг" # At end of multiplayer match playback
     humans: "Червоний" # Ladder page display team name
     ogres: "Синій"
@@ -1860,15 +1860,15 @@
     card: "Картка"
     status_unsubscribed_active: "У Вас немає передплати і рахунок Вам не прийде, але Ваш акаунт і далі дійсний."
     status_unsubscribed: "Отримайте доступ до новних рівнів, героїв та бонусів з абонементом CodeCombat!"
-#    not_yet_verified: "Not yet verified."
-#    resend_email: "Resend email"
-#    email_sent: "Email sent! Check your inbox."
-#    verifying_email: "Verifying your email address..."
-#    successfully_verified: "You've successfully verified your email address!"
+    not_yet_verified: "Ще не підтверджено."
+    resend_email: "Надіслати email знову"
+    email_sent: "Email надіслано! Перевірте вашу поштову скриньку."
+    verifying_email: "Перевірка email-адреси..."
+    successfully_verified: "Ви успішно підтвердили ваш email!"
 #    back_to_student_page: "Go back to student things"
-#    back_to_teacher_page: "Go to My Classes"
-#    back_to_game: "Go play some more levels!"
-#    verify_error: "Something went wrong when verifying your email :("
+    back_to_teacher_page: "Перейти до моїх класів"
+    back_to_game: "Грати більше рівнів!"
+    verify_error: "Щось пішло не так під час перевірки вашої email-адреси :("

   account_invoices:
     amount: "Сума у доларах США"
@@ -1890,44 +1890,44 @@
     users: "Користувачі"
     months: "Місяці"
     purchase_total: "Загалом"
-#    purchase_button: "Submit Purchase"
+    purchase_button: "Підтвердити придбання"
     your_codes: "Ваші коди"
 #    redeem_codes: "Redeem a Subscription Code"
-#    prepaid_code: "Prepaid Code"
-#    lookup_code: "Lookup prepaid code"
+    prepaid_code: "Передплачений код"
+    lookup_code: "Lookup prepaid code"
     apply_account: "Застосувати до свого екаунту"
     copy_link: "Ви можете скопіювати посилання на код та надіслати будь-кому."
     quantity: "Кількіть"
 #    redeemed: "Redeemed"
     no_codes: "Коди відсутні!"
-#    you_can1: "You can"
-#    you_can2: "purchase a prepaid code"
-#    you_can3: "that can be applied to your own account or given to others."
-
-#  coppa_deny:
-#    text1: "Can’t wait to learn programming?"
-#    text2: "Your parents will need to create an account for you to use! Email team@codecombat.com if you have any questions."
-#    close: "Close Window"
+    you_can1: "Ви можете"
+    you_can2: "купити передплачений код"
+    you_can3: "який може бути застосовано до вашого акаунту або переданий іншим."
+
+  coppa_deny:
+    text1: "Не можеш дочекатися щоб вчити програмування?"
+    text2: "Твоїм батькам необхідно створити для тебе акаунт! Напиши листа на team@codecombat.com якщо виникли запитання."
+    close: "Закрити вікно"

   loading_error:
     could_not_load: "Помилка завантаження з сервера"
     connection_failure: "Помилка з'єднання."
-#    connection_failure_desc: "It doesn’t look like you’re connected to the internet! Check your network connection and then reload this page."
-#    login_required: "Login Required"
-#    login_required_desc: "You need to be logged in to access this page."
+    connection_failure_desc: "Не схоже, що ви підключені до інтернету! Перевірте ваше з'єднання та превантажте цю сторінку."
+    login_required: "Необхідно увійти"
+    login_required_desc: "Вам потрібно увійти щоб отримати доступ до цієї сторінки."
     unauthorized: "Вам потрібно увійти. Ви вимкнули куки?"
     forbidden: "Недостатньо прав."
-#    forbidden_desc: "Oh no, there’s nothing we can show you here! Make sure you’re logged into the correct account, or visit one of the links below to get back to programming!"
+    forbidden_desc: "О ні, тут немає нічого що б ми могли вам показати! Перевірте, що ви зайшли під правильним акаунтом, або відвідайте одне з наступних посилань щоб повернутись до програмування!"
     not_found: "Не знайдено."
-#    not_found_desc: "Hm, there’s nothing here. Visit one of the following links to get back to programming!"
+    not_found_desc: "Хм, тут нічого немає. Відвідайте одне з наступних посилань щоб повернутись до програмування!"
     not_allowed: "Недозволений метод."
-    timeout: "Час очікування сервера минув." # {change}
+    timeout: "Час очікування сервера минув."
     conflict: "Конфлікт ресурсів."
-    bad_input: "Помилкове уведення."
+    bad_input: "Помилкове введення."
     server_error: "Помилка сервера."
-    unknown: "Невідома помилка." # {change}
+    unknown: "Невідома помилка"
     error: "ПОМИЛКА"
-#    general_desc: "Something went wrong, and it’s probably our fault. Try waiting a bit and then refreshing the page, or visit one of the following links to get back to programming!"
+    general_desc: "Щось пішло не так і це, скоріш за все, наша провина. Спробуйте трохи зачекати і після цього перевантажити сторінку, або відвідайте одне з наступних посилань щоб повернутись до програмування!"

   resources:
     level: "Рівень"
@@ -1938,7 +1938,7 @@
     component: "Компонент"
     components: "Компоненти"
     hero: "Герой"
-    campaigns: "Кампаніі"
+    campaigns: "Кампанії"

   concepts:
 #    advanced_css_rules: "Advanced CSS Rules"
@@ -2026,10 +2026,10 @@
 #    trees: "Data Structures - Trees"
     variables: "Змінні"
     vectors: "Вектори"
-#    while_condition_loops: "While Loops with Conditionals"
-#    while_loops_simple: "While Loops"
-#    while_loops_nested: "Nested While Loops"
-#    xy_coordinates: "Cartesian Coordinates"
+    while_condition_loops: "Цикли While з умовами"
+    while_loops_simple: "Цикли While"
+    while_loops_nested: "Вкладені цикли While"
+    xy_coordinates: "Декартові координати"
     advanced_strings: "Продвинуті строки" # Rest of concepts are deprecated
     algorithms: "Алгоритми"
     boolean_logic: "Булева логіка"
@@ -2043,7 +2043,7 @@
 #    advanced_css: "Advanced CSS"
 #    advanced_web_scripting: "Advanced Web Scripting"
     input_handling: "Обробка введення"
-    while_loops: "Цикли" # {change}
+    while_loops: "Цикли While"

   delta:
     added: "Додано"
@@ -2074,7 +2074,7 @@
     email_settings_url: "налаштування Вашого email"
     email_description_suffix: "або через посилання в повідомленнях, котрі ми присилаємо, Ви можете змінити свої уподобання і легко відмовитись від підписки в будь-який час."
     cost_title: "Вартість"
-#    cost_description: "CodeCombat is free to play for all of its core levels, with a ${{price}} USD/mo subscription for access to extra level branches and {{gems}} bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee."
+    cost_description: "CodeCombat є безкоштовною для всіх на базових рівнях, з підпискою за {{price}}$/місяць для доступу до додаткових рівнів та {{gems}} бонусних самоцвітів в місяць. В можете скасувати підписку одним кліком, і ми гарантуємо 100% повернення грошей."
     copyrights_title: "Авторські права та ліцензії"
     contributor_title: "Авторська ліцензійна згода"
     contributor_description_prefix: "Усі права, як на сайті так і у нашому сховищі GitHub, є у відповідності з нашими"
@@ -2108,8 +2108,8 @@
     nutshell_title: "Коротко"
     nutshell_description: "Будь-які ресурси, котрі ми надаємо в редакторі рівнів, є безкоштовними для використання за Вашим бажанням для створення рівнів. Але ми залишаємо за собою право обмежувати розповсюдження самих рівнів (котрі були створені на codecombat.com), тому в майбутньому за них може стягуватися плата, якщо в кінцевому результаті таке станеться."
     canonical: "Англомовна версія цього документа є остаточною та канонічною версією. Якщо є будь-які невідповідності в перекладі, англійська версія документа є пріоритетною."
-#    third_party_title: "Third Party Services"
-#    third_party_description: "CodeCombat uses the following third party services (among others):"
+    third_party_title: "Сервіси сторонніх розробників"
+    third_party_description: "CodeCombat використовує наступні сервіси сторонніх розробників (серед інших):"

   ladder_prizes:
     title: "Нагороди за турнір" # This section was for an old tournament and doesn't need new translations now.
@@ -2131,19 +2131,19 @@
     license: "ліцензія"
     oreilly: "електронна книга на ваш вибір"

-#  calendar:
-#    year: "Year"
-#    day: "Day"
-#    month: "Month"
-#    january: "January"
-#    february: "February"
-#    march: "March"
-#    april: "April"
-#    may: "May"
-#    june: "June"
-#    july: "July"
-#    august: "August"
-#    september: "September"
-#    october: "October"
-#    november: "November"
-#    december: "December"
+ calendar:
+   year: "Рік"
+   day: "День"
+   month: "Місяць"
+   january: "Січень"
+   february: "Лютий"
+   march: "Березень"
+   april: "Квітень"
+   may: "Травень"
+   june: "Червень"
+   july: "Липень"
+   august: "Серпень"
+   september: "Вересень"
+   october: "Жовтень"
+   november: "Листопад"
+   december: "Грудень"


Fix title search for islolup

Fix title search for islolup

--- scripts/islolup.coffee
+++ scripts/islolup.coffee
@@ -6,7 +6,7 @@
         titlebar: "1"
       })
       .get() (err, res, body) ->
-        title = /<title>([^<]+)<\/title>/.exec(body)
+        title = /document\.title="([^\"]+)"/.exec(body)
         if title
           msg.send title[1]
         else


Coffeescript truthiness rules are coffeescript

--- scripts/pudding.coffee
+++ scripts/pudding.coffee
@@ -180,7 +180,8 @@
     response = '```\n'

     instances.map (inst) ->
-      inst.ip ?= '_'
+      if not inst.ip or inst.ip is ''
+        inst.ip = '_'
       response += format_instance(inst)

     msg.send response + '```'


go away spammy mcspam

go away spammy mcspam

--- scripts/storage.coffee
+++ scripts/storage.coffee
@@ -1,25 +0,0 @@
-# Description:
-#   Inspect the data in redis easily
-#
-# Commands:
-#   hubot show users - Display all users that hubot knows about
-#   hubot show storage - Display the contents that are persisted in the brain
-
-
-Util = require "util"
-
-module.exports = (robot) ->
-  robot.respond /show storage$/i, (msg) ->
-    output = Util.inspect(robot.brain.data, false, 4)
-    msg.send output
-
-  robot.respond /show users$/i, (msg) ->
-    response = ""
-
-    for own key, user of robot.brain.data.users
-      response += "#{user.id} #{user.name}"
-      response += " <#{user.email_address}>" if user.email_address
-      response += "\n"
-
-    msg.send response
-


Add correct url for cabin facts

Add correct url for cabin facts

--- coffee/controller/cabin.coffee
+++ coffee/controller/cabin.coffee
@@ -22,7 +22,7 @@
     links = [
       {
         title: 'Fakta'
-        url: '#beskrivelse'
+        url: '#fakta'
         glyph: 'list-alt'
       }
       {


implemented newMirror function for creating mirrors

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- coffee/controllers/main.coffee
+++ coffee/controllers/main.coffee
@@ -124,11 +124,14 @@
           # append the mirror to the list of mirrors
           $scope.mirrors.push.apply( $scope.mirrors, data )

-    $scope.addMirror = (mirror)->
-      Restangular.one('mirrors').post('',mirror).
-        then (n) ->
-          $scope.mirrors.push( n )
-        , fetchError('add mirror')
+    $scope.newMirror = ()->
+      nu = {
+          id: undefined,
+          name: undefined
+          saved: false
+        }
+      $scope.mirrors.unshift(nu)
+      nu

     $scope.fetchNode = (id)->
       Restangular.one('node', id).get().


Sim: Fix /ip command.

Sim: Fix /ip command.

--- server/commands.coffee
+++ server/commands.coffee
@@ -235,17 +235,17 @@
   if !nameOrIp
     user.error(errors.COMMAND_ERROR, "Usage: /ip username")
     return next()
-  user = @users.get(nameOrIp)
-  if user
-    ips = user.sparks.map((spark) -> spark.address.ip)
+  checkedUser = @users.get(nameOrIp)
+  if checkedUser
+    ips = checkedUser.sparks.map((spark) -> spark.address.ip)
     ips = _.chain(ips).compact().unique().value()
     user.announce('success', "#{nameOrIp}'s IP addresses: #{ips.join(', ')}")
   else
     users = []
-    for user in @users.getUsers()
-      for spark in user.sparks
+    for checkedUser in @users.getUsers()
+      for spark in checkedUser.sparks
         if spark.address.ip == nameOrIp
-          users.push(user.name)
+          users.push(checkedUser.name)
           break
     user.announce('success', "Users with IP #{nameOrIp}: #{users.join(', ')}")
   next()


FormView no longer prevents @events property from being overridden

--- app/assets/javascripts/luca/components/form_view.coffee
+++ app/assets/javascripts/luca/components/form_view.coffee
@@ -92,7 +92,11 @@

 formView.privateConfiguration
   tagName: 'form'
-  events:
+
+  # These events will get registered on the component
+  # but still leave the @events property open to extend
+  # for any component which inherits from us.
+  _events:
     "click .submit-button" : "submitHandler"
     "click .reset-button" : "resetHandler"

@@ -113,6 +117,11 @@
     @components ||= @fields

     _.bindAll @, "submitHandler", "resetHandler", "renderToolbars"
+
+    # have our events be internal to the view, and not
+    # part of the normal @events chain, so they can be inherited
+    for eventId, handler of @_events
+      @registerEvent(eventId, handler)

     if @trackDirtyState is true
       @on "after:components", ()->


EDSC-488: Fix timeline animation when dragging in Chrome.  EDSC-490: Fix timeline click behavior in Firefox.

--- app/assets/javascripts/modules/timeline/timeline.coffee
+++ app/assets/javascripts/modules/timeline/timeline.coffee
@@ -338,10 +338,14 @@
       null

     _forceRedraw: ->
-      rect = @_buildRect(stroke: 'none')
+      rect = @_buildRect(stroke: 'none', fill: 'none')
       svg = @svg
       svg.appendChild(rect)
-      setTimeout -> svg.removeChild(rect)
+      callback =  -> svg.removeChild(rect)
+      if window.requestAnimationFrame
+        window.requestAnimationFrame(callback)
+      else
+        setTimeout(callback)

     refresh: ->
       @datasets(@_datasets)
@@ -646,6 +650,7 @@

       draggables = @root.find([@scope('.draggable'), @scope('.selection'), @scope('.display-top'), @scope('.focus')].join(', '))
       draggables.attr('transform', "translate(#{-@originPx + OFFSET_X},0)")
+      @_forceRedraw()

       @_finishPan() if commit


refs #5 Twitter Bootstrap alerts per JavaScript.

--- app/assets/javascripts/voluntary/ember_js/app.js.coffee
+++ app/assets/javascripts/voluntary/ember_js/app.js.coffee
@@ -12,4 +12,15 @@

 Ember.TextField.reopen attributeBindings: [
   'data-autocomplete', 'data-append-autocomplete', 'data-append-to'
-]+]
+
+Volontariat.alert = (type, message) ->
+  $('#alert_message').text message
+  $('#alert').addClass "alert-#{type}"
+  $("#alert").slideDown()
+
+  setTimeout (->
+    $("#alert").slideUp(400, ->
+      $('#alert').removeClass "alert-#{type}"
+    )
+  ), 5000


loader: set `line-height` of loader

loader: set `line-height` of loader

--- src/components/loader/loaderview.coffee
+++ src/components/loader/loaderview.coffee
@@ -34,6 +34,9 @@
     for own option, value of loaderOptions
       @canvas["set#{option.capitalize()}"] value

+    {height} = @getOption 'size'
+    @setCss 'line-height', if height then height + 'px' else 'initial'
+
     @show()  if showLoader

   show:->


Cakefile: fixed the builders sprite generator/race condition on styl saves

--- Builder.coffee
+++ Builder.coffee
@@ -49,6 +49,7 @@
 module.exports = class Builder

   spritePath   = './website/sprites'
+  spriteHelper = null

   buildClient: (options) ->

@@ -58,14 +59,21 @@
       path     : spritePath
       watch    : yes

-    try fs.mkdirSync ".build"
-
-    if @config.client.runtimeOptions.precompiledApi
-      exec 'coffee ./bongo-api-builder/build.coffee -o .build/api.js', =>
+    sprite.stylus
+      path     : spritePath
+      httpPath : '/sprites'
+      retina   : '@2x'
+    , (err, helper)=>
+      spriteHelper = helper
+
+      try fs.mkdirSync ".build"
+
+      if @config.client.runtimeOptions.precompiledApi
+        exec 'coffee ./bongo-api-builder/build.coffee -o .build/api.js', =>
+          @buildAndWatchClient options
+      else
+        fs.writeFileSync '.build/api.js', '', 'utf-8'
         @buildAndWatchClient options
-    else
-      fs.writeFileSync '.build/api.js', '', 'utf-8'
-      @buildAndWatchClient options

   buildAndWatchClient: (options) ->

@@ -324,20 +332,14 @@

         rootPath = path.dirname(file.sourcePath)

-        sprite.stylus
-          path     : spritePath
-          httpPath : '/sprites'
-          retina   : '@2x'
-        , (err, helper)->
-          stylus = Stylus(source)
-            .set('compress',true)
-            .set('paths', [rootPath])
-            .define('sprite', helper.fn )
-            .use(nib())
-            .render (err, css)=> # callback is synchronous
-              console.log ">>>>>>>>>>>>>>>>>>>>>>>>> GOKMEN FIX ME!!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
-              log.error "error with styl file at #{file.includePath}:\n #{err}"  if err
-              file.content = css
+        stylus = Stylus(source)
+          .set('compress',true)
+          .set('paths', [rootPath])
+          .define('sprite', spriteHelper.fn )
+          .use(nib())
+          .render (err, css)=> # callback is synchronous
+            log.error "error with styl file at #{file.includePath}:\n #{err}"  if err
+            file.content = css

       when ".css"
         file.content = source


stopped test checking stringified date

stopped test checking stringified date

--- test/generators/flat/page.coffee
+++ test/generators/flat/page.coffee
@@ -102,7 +102,7 @@
           assert.ok(!err, "No errors: #{err}")
           assert.equal(data.total_rows, 1, 'has the correct total_rows')
           assert.equal(data.rows.length, 1, 'has the correct row.length')
-          assert.deepEqual(expected = JSON.stringify(model.toJSON()), actual = JSON.stringify(data.rows[0]), "\nExpected: #{util.inspect(expected)}\nActual: #{util.inspect(actual)}")
+          assert.deepEqual(expected = model.toJSON().id, actual = data.rows[0].id, "\nExpected: #{util.inspect(expected)}\nActual: #{util.inspect(actual)}")
           done()

 # TODO: explain required set up


Handle language parameter more liberally.

--- server-src/legacy-redirector.coffee
+++ server-src/legacy-redirector.coffee
@@ -13,8 +13,9 @@
     language = 'fi'
     path = null
     language = 'fi'
-    attributeName = if isEmbed then 'language' else 'lang'
-    value = req.query[attributeName]
+    value = req.query.lang
+    if not value?
+        value = req.query.language
     if value
         switch value
             when 'se'


DataTable

DataTable

- fixedCols: 0 if no rowMove set, forgot this
  in my last checkin.

--- src/elements/DataTable/DataTable.coffee
+++ src/elements/DataTable/DataTable.coffee
@@ -184,7 +184,7 @@
 			fixedRows: if @_no_header then 0 else 1
 			footer_left: footer
 			footer_right: @_footer_right
-			fixedCols: 1 #if @_rowMove then 1 else 0
+			fixedCols: if @_rowMove then 1 else 0
 			colResize: if @_no_header then false else true
 			colClasses: colClasses
 			rowMove: @_rowMove


prevent crash when deleting app fast

prevent crash when deleting app fast

--- server/initializers/realtime.coffee
+++ server/initializers/realtime.coffee
@@ -24,16 +24,15 @@
     # also create a notification when an app install is complete
     realtime.on 'application.update', (event, id) ->
         Application.find id, (err, app) ->
-            return console.log err.stack if err # no notification, no big deal
-            switch app.state
-                when 'broken'
-                    messageKey = 'installation message failure'
-                    options = appName: app.displayName
-                    message = localizationManager.t messageKey, options
-                    notifhelper.createTemporary
-                        text: message
-                        resource: app: 'home'
-                else return
+            if err # no notification, no big deal
+                console.log err.stack
+            else if app?.state is 'broken'
+                messageKey = 'installation message failure'
+                options = appName: app.displayName
+                message = localizationManager.t messageKey, options
+                notifhelper.createTemporary
+                    text: message
+                    resource: app: 'home'

     realtime.on 'usage.application', (event, name) ->
         if name isnt 'home' and name isnt 'proxy'


criação de salas sem upperCase

criação de salas sem upperCase

--- server/methods/createChannel.coffee
+++ server/methods/createChannel.coffee
@@ -23,7 +23,7 @@
 			usernames: members
 			ts: now
 			t: 'c'
-			name: name.toUpperCase()
+			name: name
 			msgs: 0
 			u:
 				_id: Meteor.userId()
@@ -42,7 +42,7 @@
 			sub =
 				rid: rid
 				ts: now
-				name: name.toUpperCase()
+				name: name
 				t: 'c'
 				unread: 0
 				u:


More decorations! (for dungeon preset)

More decorations! (for dungeon preset)

--- app/views/editor/level/modals/TerrainRandomizeModal.coffee
+++ app/views/editor/level/modals/TerrainRandomizeModal.coffee
@@ -24,7 +24,7 @@
     'margin': 1
     }
   'wood': {
-    'thangs': ['Firewood 1', 'Firewood 2', 'Firewood 3', 'Barrel']
+    'thangs': ['Firewood 1', 'Firewood 2', 'Firewood 3']
     'margin': 1
     }
   'farm': {
@@ -39,6 +39,18 @@
     'thangs': ['Gargoyle', 'Rock Cluster 1', 'Rock Cluster 2', 'Rock Cluster 3']
     'margin': 1
     }
+  'torch': {
+    'thangs': ['Torch']
+    'margin': 0
+  }
+  'chains': {
+    'thangs': ['Chains']
+    'margin': 0
+  }
+  'barrel': {
+    'thangs': ['Barrel']
+    'margin': 1
+  }
   'doors': {
     'thangs': ['Dungeon Door']
     'margin': -1
@@ -75,6 +87,7 @@
     'borders':'dungeon_wall'
     'borderNoise':0
     'borderSize':4
+    'borderThickness':1
     'floors':'dungeon_floor'
     'decorations': {
       'Room': {
@@ -84,6 +97,13 @@
         'thickness': [2,2]
         'cluster': 'dungeon_wall'
       }
+      'Barrels': {
+        'num': [1,1]
+        'width': [8, 12]
+        'height': [8, 12]
+        'numBarrels': [4,6]
+        'cluster': 'barrel'
+      }
       'cave': {
         'num':[1,1]
         'width': 10
@@ -100,6 +120,7 @@
     'borders':'indoor_wall'
     'borderNoise':0
     'borderSize':4
+    'borderThickness':1
     'floors':'indoor_floor'
     'decorations': {
       'Room': {
@@ -124,6 +145,7 @@
     'borders':'trees'
     'borderNoise':1
     'borderSize':0
+    'borderThickness':3
     'floors':'grass_floor'
     'decorations': {
       'house': {
@@ -156,10 +178,12 @@
   'small': {
     'x':80
     'y':68
+    'sizeFactor':1
   }
   'large': {
     'x':160
     'y':136
+    'sizeFactor':2
   }
 }

@@ -171,7 +195,6 @@
   'borderSize': {
     'x':4
     'y':4
-    'thickness':3
   }
 }

@@ -221,7 +244,8 @@

   randomizeBorder: (preset, presetSize, noiseFactor=1) ->
     for i in _.range(0, presetSize.x, thangSizes.borderSize.x)
-      for j in _.range(thangSizes.borderSize.thickness)
+      for j in _.range(preset.borderThickness)
+        # Bottom wall
         while not @addThang {
           'id': @getRandomThang(clusters[preset.borders].thangs)
           'pos': {
@@ -231,6 +255,8 @@
           'margin': clusters[preset.borders].margin
         }
           continue
+
+        # Top wall
         while not @addThang {
           'id': @getRandomThang(clusters[preset.borders].thangs)
           'pos': {
@@ -241,8 +267,38 @@
         }
           continue

+        # Double wall on top
+        if preset.type is 'dungeon'
+          @addThang {
+            'id': @getRandomThang(clusters[preset.borders].thangs)
+            'pos': {
+              'x': i + preset.borderSize/2
+              'y': presetSize.y - 3 * preset.borderSize/2
+            }
+            'margin': clusters[preset.borders].margin
+          }
+          if ( i / preset.borderSize ) % 2 and i isnt presetSize.x - thangSizes.borderSize.x
+            @addThang {
+              'id': @getRandomThang(clusters['torch'].thangs)
+              'pos': {
+                'x': i + preset.borderSize
+                'y': presetSize.y - preset.borderSize
+              }
+              'margin': clusters['torch'].margin
+            }
+          else if ( i / preset.borderSize ) % 2 is 0 and i and _.random(100) < 30
+            @addThang {
+              'id': @getRandomThang(clusters['chains'].thangs)
+              'pos': {
+                'x': i + preset.borderSize
+                'y': presetSize.y - preset.borderSize
+              }
+              'margin': clusters['chains'].margin
+            }
+
     for i in _.range(0, presetSize.y, thangSizes.borderSize.y)
-      for j in _.range(3)
+      for j in _.range(preset.borderThickness)
+        # Left wall
         while not @addThang {
           'id': @getRandomThang(clusters[preset.borders].thangs)
           'pos': {
@@ -252,6 +308,8 @@
           'margin': clusters[preset.borders].margin
         }
           continue
+
+        # Right wall
         while not @addThang {
           'id': @getRandomThang(clusters[preset.borders].thangs)
           'pos': {
@@ -263,9 +321,8 @@
           continue

   randomizeDecorations: (preset, presetSize)->
-    if presetSize is presetSizes['small'] then sizeFactor = 1 else sizeFactor = 2
     for name, decoration of preset.decorations
-      for num in _.range(sizeFactor * _.random(decoration.num[0], decoration.num[1]))
+      for num in _.range(presetSize.sizeFactor * _.random(decoration.num[0], decoration.num[1]))
         if @['build'+name] isnt undefined
           @['build'+name](preset, presetSize, decoration)
           continue
@@ -291,15 +348,14 @@
               continue

   buildRoom: (preset, presetSize, room) ->
-    if presetSize is presetSizes['small'] then sizeFactor = 1 else sizeFactor = 2
     while true
       rect = {
-        'width':sizeFactor * (room.width[0] + preset.borderSize * _.random(0, (room.width[1] - room.width[0])/preset.borderSize))
-        'height':sizeFactor * (room.height[0] + preset.borderSize * _.random(0, (room.height[1] - room.height[0])/preset.borderSize))
+        'width':presetSize.sizeFactor * (room.width[0] + preset.borderSize * _.random(0, (room.width[1] - room.width[0])/preset.borderSize))
+        'height':presetSize.sizeFactor * (room.height[0] + preset.borderSize * _.random(0, (room.height[1] - room.height[0])/preset.borderSize))
       }
       roomThickness = _.random(room.thickness[0], room.thickness[1])
       rect.x = _.random(rect.width/2 + preset.borderSize * (roomThickness+1.5), presetSize.x - rect.width/2 - preset.borderSize * (roomThickness+1.5))
-      rect.y = _.random(rect.height/2 + preset.borderSize * (roomThickness+2.5), presetSize.y - rect.height/2 - preset.borderSize * (roomThickness+2.5))
+      rect.y = _.random(rect.height/2 + preset.borderSize * (roomThickness+2.5), presetSize.y - rect.height/2 - preset.borderSize * (roomThickness+3.5))
       # Snap room walls to the wall grid.
       rect.x = Math.round((rect.x - preset.borderSize / 2) / preset.borderSize) * preset.borderSize + preset.borderSize / 2
       rect.y = Math.round((rect.y - preset.borderSize / 2) / preset.borderSize) * preset.borderSize + preset.borderSize / 2
@@ -318,6 +374,7 @@

     for t in _.range(0, roomThickness+1)
       for i in _.range(rect.x - rect.width/2 - (t-1) * preset.borderSize, rect.x + rect.width/2 + t * preset.borderSize, preset.borderSize)
+        # Bottom wall
         thang = {
           'id': @getRandomThang(clusters[room.cluster].thangs)
           'pos': {
@@ -331,6 +388,18 @@
           thang.pos.y -= preset.borderSize/3
         @addThang thang unless i is bottomDoorX and t isnt roomThickness and bottomDoor

+        if t is roomThickness and i isnt rect.x - rect.width/2 - (t-1) * preset.borderSize and preset.type is 'dungeon'
+          if ( i isnt bottomDoorX and i isnt bottomDoorX + preset.borderSize ) or not bottomDoor
+            @addThang {
+              'id': @getRandomThang(clusters['torch'].thangs)
+              'pos': {
+                'x': thang.pos.x - preset.borderSize / 2
+                'y': thang.pos.y + preset.borderSize / 2
+              }
+              'margin': clusters['torch'].margin
+            }
+
+        # Top wall
         thang = {
           'id': @getRandomThang(clusters[room.cluster].thangs)
           'pos': {
@@ -346,6 +415,7 @@

     for t in _.range(0, roomThickness)
       for i in _.range(rect.y - rect.height/2 - t * preset.borderSize, rect.y + rect.height/2 + (t+1) * preset.borderSize, preset.borderSize)
+        # Left wall
         @addThang {
           'id': @getRandomThang(clusters[room.cluster].thangs)
           'pos': {
@@ -355,6 +425,7 @@
           'margin': clusters[room.cluster].margin
         }

+        # Right wall
         @addThang {
           'id': @getRandomThang(clusters[room.cluster].thangs)
           'pos': {
@@ -363,6 +434,35 @@
           }
           'margin': clusters[room.cluster].margin
         }
+
+  buildBarrels: (preset, presetSize, decoration) ->
+    rect = {
+      'width':presetSize.sizeFactor * ( _.random( decoration.width[0], decoration.width[1] ) )
+      'height':presetSize.sizeFactor * ( _.random( decoration.height[0], decoration.height[1] ) )
+    }
+    x = [ rect.width/2 + preset.borderSize , presetSize.x - rect.width/2 - preset.borderSize ]
+    y = [ rect.height/2 + preset.borderSize , presetSize.y - rect.height/2 - 2 * preset.borderSize ]
+
+    for i in x
+      for j in y
+        if _.random(100) < 40
+          rect = {
+            'x': i
+            'y': j
+            'width': rect.width
+            'height': rect.height
+          }
+          if @addRect rect
+            for num in _.range( _.random( decoration.numBarrels[0], decoration.numBarrels[1] ) )
+              while not @addThang {
+                'id': @getRandomThang(clusters[decoration.cluster].thangs)
+                'pos': {
+                  'x': _.random(rect.x - rect.width/2, rect.x + rect.width/2)
+                  'y': _.random(rect.y - rect.height/2, rect.y + rect.height/2)
+                }
+                'margin': clusters[decoration.cluster].margin
+              }
+                continue

   addThang: (thang) ->
     if @falseCount > 100


Make promise libraries list long stack traces in development

--- server/app.coffee
+++ server/app.coffee
@@ -10,8 +10,13 @@
 i18n = require('i18n')
 flash = require('connect-flash')

+
 exports.createApp = ->
   app = express()
+
+  if app.get('env') isnt 'production'
+    require('bluebird').longStackTraces()
+    require('q').longStackSupport = true

   require('./initializers/logging')(app)


:wat:

:wat:

--- app/assets/javascripts/admin/moi_tree.js.coffee
+++ app/assets/javascripts/admin/moi_tree.js.coffee
@@ -81,7 +81,6 @@
     @setChildren(@rootNeuron)

     @neurons = @tree.nodes(@rootNeuron)
-    @shownNeurons = @neurons
     @update(@rootNeuron)
     @drawWithoutParent()

@@ -134,17 +133,12 @@
       @showChildren(child)
     null

-  filterShownNeurons: ->
-    @shownNeurons = @neurons.filter (neuron) ->
-      !neuron.hidden
-
   toggleNode: (node) ->
     if node.children
       @hideChildren(node)
       node.hidden = false
     else
       @showChildren(node)
-    @filterShownNeurons()
     @update(node)

   draw: ->


ensures newly created items are bound if 'homogeneous_values' is selected

--- app/assets/javascripts/backend/budget.js.coffee
+++ app/assets/javascripts/backend/budget.js.coffee
@@ -85,6 +85,8 @@
     $("a[data-association='item']").each ->
       $(this).click()
       new_item = $(this).closest('td').prev()
+      if new_item.prev().hasClass('homogeneous')
+        new_item.addClass('homogeneous')
       new_item.attr('data-support-destroy', support_destroy_id)
     # adds total calculation cells
     $("[data-appendable]").each ->


Fix bug where clicking on the radio buttons labels on edition (not creation) didn't checked the associated input

--- app/assets/javascripts/cocoon/iceberg.js.coffee
+++ app/assets/javascripts/cocoon/iceberg.js.coffee
@@ -29,7 +29,7 @@
         @display.addClass('hidden')

         clone = @oldForm().clone()
-        clone.insertAfter(@oldForm())
+        clone.insertBefore(@oldForm())
         clone.trigger('cocoon:after-insert')
         clone.removeClass('hidden')
         @_bindButtons(@newForm())


don't show answer when editing

don't show answer when editing

--- app/assets/javascripts/components/question.cjsx
+++ app/assets/javascripts/components/question.cjsx
@@ -69,11 +69,7 @@
     if (@state.hidden and @props.answered) or @props.mode == "show"
       @renderAppropriateAnswer()
     else
-      [
-        @renderTooltip(),
-        @renderAppropriateAnswer(),
-        @renderAppropriateQuestion()
-      ]
+      [@renderTooltip(), @renderAppropriateQuestion()]

   renderTooltip: =>
     return null unless @props.data.tooltip


Connor revisions 2

Connor revisions 2

Connor rev 2

--- app/assets/javascripts/inspector_address.coffee
+++ app/assets/javascripts/inspector_address.coffee
@@ -300,7 +300,7 @@
       }
       {
         element: "#map-highlight"
-        intro: "A house divided cannot stand? Abe Lincoln should have spent more time in New York. While this proved to be an apt metaphor for our nation torn asunder, large structures were actually frequently divided on the inside and were given more than one address (without collapsing as a result). Give us both numbers.
 They could be on the same street..."
+        intro: "A house divided cannot stand? Abe Lincoln should have spent more time in New York. While this proved to be an apt metaphor for our nation torn asunder, large structures were actually frequently divided on the inside and were given more than one address (without collapsing as a result). Give us both numbers.
 They could be on the same street, like this one..."
         position: "right"
         polygon_index: 5
       }
@@ -395,8 +395,14 @@
         polygon_index: 12
       }
       {
-        element: "#map-highlight"
-        intro: "Space is always at a premium in NYC and some properties may have more than one building on it - one right on the street and another in the backyard. Both of them share the same address. This backlot structure is a part of 17 Morton St."
+        element: ".leaflet-control-zoom.leaflet-bar.leaflet-control"
+        intro: "Space is always at a premium in NYC and some properties may have more than one building on it - one right on the street and another in the backyard. Both of them share the same address. Use the zoom function to see who owns this building. "
+        position: "right"
+        polygon_index: 13
+      }
+      {
+        element: "#map-highlight"
+        intro: "This backlot structure is a part of 17 Morton St."
         position: "right"
         polygon_index: 13
       }


validating product form after checkbox is clicked

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -246,7 +246,8 @@

 if gon.page is 'products_new' or gon.page is 'products_create' or gon.page is 'products_edit' or gon.page is 'products_update'
     $ ->
-        ($ '.mobile-form')
+        $mobile_form = ($ '.mobile-form')
+        $mobile_form
             .on 'click', '.add-button', (e) ->
                 # tap submits the form on iOS
                 e.preventDefault()
@@ -270,8 +271,9 @@
                 ($ '#product_quantity')
                     .prop('disabled', '')
                     .val(val)
-
-        ($ '.mobile-form')
+            $mobile_form.trigger('submit')
+
+        $mobile_form
             .isHappy(product_form_validation_rules)
             .submit((e) ->
                 if ($ @).find('.unhappy').length


Ignored models in collections which are shared across projects in toJson() and fromJson().

--- src/ProjectUtils.coffee
+++ src/ProjectUtils.coffee
@@ -13,13 +13,13 @@
   # @returns {Object} JSON serialization of the given project and its models. IDs are unaltered.
   toJson: (id) ->
     project = Projects.findOne(id)
-    unless project
-      throw new Error('Project with ID ' + id + ' not found')
+    unless project then throw new Error('Project with ID ' + id + ' not found')
     result = {}
     result[Collections.getName(Projects)] = [project]
     collections = _.without(CollectionUtils.getAll(), Projects)
     _.each Collections.getMap(collections), (collection, name) ->
-      result[name] = collection.findByProject(id).fetch()
+      # TODO(aramk) This will ignore collectionType references which don't have "projectId" fields.
+      result[name] = collection.findByProject?(id).fetch()
     result

   # Constructs new models from the given JSON serialization. IDs are used to retain references
@@ -45,9 +45,13 @@
     _.each collectionMap, (collection, name) ->
       idMap = idMaps[name] = {}
       _.each json[name], (model) ->
+        oldModelId = model._id
+        # Ignore documents in the JSON which don't have a projectId and exist in the collection,
+        # indicating they should be shared between projects.
+        projectId = SchemaUtils.getParameterValue(model, SchemaUtils.projectIdProperty)
+        return if !projectId? && collection.findOne(oldModelId)?
         createDf = Q.defer()
         createDfs.push(createDf.promise)
-        oldModelId = model._id
         delete model._id
         # TODO(aramk) Disabling validation is dangerous - only done here to avoid validation
         # errors which don't have messages at the moment. Improve collection2 to provide the


test(cell): don't test the framework

test(cell): don't test the framework

--- test/spec/directives/cell.coffee
+++ test/spec/directives/cell.coffee
@@ -7,18 +7,14 @@

   beforeEach inject ($rootScope, $compile) ->
     scope   = $rootScope.$new()
-    element = $compile('<bp-cell>{{ label }}</bp-cell>') scope
+    element = $compile('<bp-cell>Yo</bp-cell>') scope
     scope.$apply()

   describe 'element', ->
-    it 'should have correct label', ->
-      scope.label = 'foo'
-      scope.$apply()
-      expect(element.text()).toBe scope.label
-
-      scope.label = 'bar'
-      scope.$apply()
-      expect(element.text()).toBe scope.label
-
     it 'should have ARIA role', ->
       expect(element.attr 'role' ).toBe 'listitem'
+
+  describe 'label', ->
+    it 'should be wrapped in span', ->
+      expect(element.find('span').length).toBe 1
+


Récupération de l'événement sur le bouton sauvegarde. Attention la l'orthographe et à la casse des termes utilisés. Le copier/collé est sans doute la meilleur façon d'éviter les problèmes.

--- source/javascripts/appExtJS/controller/Regions.js.coffee
+++ source/javascripts/appExtJS/controller/Regions.js.coffee
@@ -9,13 +9,17 @@
       # render: @onPanelRendered
       'listeRegions':
         itemdblclick: @editRegion
+      'editRegion button[action=save]':
+        click: @updateRegion
     )
     null
   #onPanelRendered: ->
   #  console.log('le Panel a été affiché')
   #  null
-  editRegion: (grid, record)->
+  editRegion: (grid, record) ->
     console.log('Double click sur '+record.get('NCCENR'))
     view = Ext.widget('editRegion')
     view.down('form').loadRecord(record)
+  updateRegion: (button) ->
+    console.log('click sur le bouton Sauvegarder')
 )


Wrap the attempt to rebuild in a try/finally to gracefully handle errors

--- source/server/infrastructure/projection-rebuilder.coffee
+++ source/server/infrastructure/projection-rebuilder.coffee
@@ -31,25 +31,26 @@
       projection.enterRebuildMode()
       queue.push projection

-    # Loop through all events and hand them individually to all projections
-    for event in @commitStore.getAllEvents()
-      projection.on(event, true) for projection in queue
+    try
+      # Loop through all events and hand them individually to all projections
+      for event in @commitStore.getAllEvents()
+        projection.on(event, true) for projection in queue

-    # Update the real collection data with the in-memory versions
-    # for the specified projections only.
-    for collectionId, realCollection of realCollectionsBackups
-      inMemoryCollection = @injector.get(collectionId)
-      inMemoryData = inMemoryCollection.find().fetch()
-      realCollection.remove {}
-      if inMemoryData.length
-        realCollection.batchInsert inMemoryData
-      else
-        @log.info(@_logMsg("No data to insert after replaying events for #{collectionId}"))
-      # Restore original collections
-      @injector.override(collectionId).to realCollection
-
-    for projection in queue
-      projection.exitRebuildMode()
+      # Update the real collection data with the in-memory versions
+      # for the specified projections only.
+      for collectionId, realCollection of realCollectionsBackups
+        inMemoryCollection = @injector.get(collectionId)
+        inMemoryData = inMemoryCollection.find().fetch()
+        realCollection.remove {}
+        if inMemoryData.length
+          realCollection.batchInsert inMemoryData
+        else
+          @log.info(@_logMsg("No data to insert after replaying events for #{collectionId}"))
+        # Restore original collections
+        @injector.override(collectionId).to realCollection
+    finally
+      for projection in queue
+        projection.exitRebuildMode()

   _getCollectionIdsOfProjection: (projection) ->
     collectionIds = []


Tests: Added beginTest helper.

Tests: Added beginTest helper.

--- tests/src/helpers/helpers.coffee
+++ tests/src/helpers/helpers.coffee
@@ -3,6 +3,16 @@

 module.exports =
+
+  beginTest: (browser) ->
+    url  = @getUrl()
+    user = utils.getUser()
+
+    browser.url(url)
+    browser.maximizeWindow()
+
+    @doLogin(browser, user)
+

   doLogin: (browser, user) ->


fixes #1099: instead of nothing, compile to false

"if a in []"

--- nodes.coffee
+++ nodes.coffee
@@ -1340,6 +1340,7 @@
     [cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
     tests = for item, i in @array.base.objects
       (if i then ref else sub) + cmp + item.compile o, LEVEL_OP
+    return 'false' if tests.length is 0
     tests = tests.join cnj
     if o.level < LEVEL_OP then tests else "(#{tests})"


Fixes #3132 - Improve rendering of block-comments

--- nodes.coffee
+++ nodes.coffee
@@ -556,7 +556,8 @@
   makeReturn:      THIS

   compileNode: (o, level) ->
-    code = "/*#{multident @comment, @tab}#{if '\n' in @comment then "\n#{@tab}" else ''}*/"
+    comment = @comment.replace /^(\s*)#/gm, "$1 *"
+    code = "/*#{multident comment, @tab}#{if '\n' in comment then "\n#{@tab}" else ''} */"
     code = o.indent + code if (level or o.level) is LEVEL_TOP
     [@makeCode("\n"), @makeCode(code)]


video: enable icon on control views

video: enable icon on control views

--- client/app/lib/videocollaboration/views/controlview.coffee
+++ client/app/lib/videocollaboration/views/controlview.coffee
@@ -5,6 +5,7 @@
   constructor: (options = {}, data) ->

     options.cssClass = kd.utils.curry 'solid', options.cssClass
+    options.icon = yes
     super options, data

     @setActiveState options.active


Fixing tests

Fixing tests

--- test/reactions_test.coffee
+++ test/reactions_test.coffee
@@ -65,8 +65,8 @@
   it 'registers a sad responder', ->
     expect(@robot.respond).to.have.been.calledWith(/react (sad|cry)/i)

-  it 'registers a dissapoint responder', ->
-    expect(@robot.respond).to.have.been.calledWith(/react (dissapoint|dissapointed|upset)/i)
+  it 'registers a disappoint responder', ->
+    expect(@robot.respond).to.have.been.calledWith(/react (disappoint|disappointed|upset)/i)

   it 'registers a impatient responder', ->
     expect(@robot.respond).to.have.been.calledWith(/react (impatient|taking too long|speed it up|faster)/i)


Code formatting

Code formatting

--- client/ide/lib/idehelpers.coffee
+++ client/ide/lib/idehelpers.coffee
@@ -1,10 +1,11 @@
-remote = require('app/remote').getInstance()
-showError = require 'app/util/showError'
-kd = require 'kd'
-globals = require 'globals'
-FSHelper = require 'app/util/fs/fshelper'
-FilePermissionsModal = require './views/modals/filepermissionsmodal'
-dataProvider = require 'app/userenvironmentdataprovider'
+kd                    = require 'kd'
+remote                = require('app/remote').getInstance()
+globals               = require 'globals'
+FSHelper              = require 'app/util/fs/fshelper'
+showError             = require 'app/util/showError'
+dataProvider          = require 'app/userenvironmentdataprovider'
+FilePermissionsModal  = require './views/modals/filepermissionsmodal'
+

 WORKSPACE_WELCOME_TXT = """
   # Awesome, you've just made a new workspace!


Revert "adjust local varable name"

Revert "adjust local varable name"

This reverts commit abaa0b630429a88b9910c9ec8e8eff27e594cd8c.

--- client/ide/lib/idehelpers.coffee
+++ client/ide/lib/idehelpers.coffee
@@ -53,11 +53,11 @@
     workspaces = o.workspaces for o in globals?.userEnvironmentData?.own when o.machine.uid is machineUId
     workspace = w for w in workspaces when w.rootPath is rootPath

-    handleRout_ = (machine, workspace) ->
+    handleRout = (machine, workspace) ->
       href = "/IDE/#{machine.slug or machine.label}/#{workspace.slug}"
       router.handleRoute href

-    return handleRout_(machine, workspace) if workspace
+    return handleRout(machine, workspace) if workspace

     remote.api.JWorkspace.create data, (err, workspace) =>
       return helpers.handleWorkspaceCreateError_ eventObj, err  if err
@@ -79,7 +79,7 @@

           eventObj.emit 'WorkspaceCreated', workspace

-          handleRout_(machine, workspace)
+          handleRout(machine, workspace)

   handleWorkspaceCreateError_: (eventObj, error) ->


test for this

test for this

--- test/spec/view_spec.coffee
+++ test/spec/view_spec.coffee
@@ -364,6 +364,7 @@
       e = new E
       window.clickOnElement e.el
       expect(e.handler).was.calledOnce()
+      expect(e.handler).was.calledOn(e)

     it 'should add and return subviews', ->
       expect(view.subview).to.be.a 'function'


DOM

DOM

- isInDOM: allow no node to pe passed, return null.

--- src/base/DOM/DOM.coffee
+++ src/base/DOM/DOM.coffee
@@ -877,6 +877,9 @@
 		parents

 	@isInDOM: (docElem) ->
+		if not docElem
+			return null
+
 		assert(docElem instanceof HTMLElement, "CUI.DOM.isInDOM", "docElem needs to be instanceof HTMLElement.", docElem: docElem)
 		if @closestUntil(docElem, null, document.documentElement)
 			true


Mark deploys as deploys (when successful)

--- js/inner.coffee
+++ js/inner.coffee
@@ -123,6 +123,8 @@
           "warning"
         when "fixed"
           "success"
+        when "deploy"
+          "success"
         when "success"
           "success"
         when "running"


Updated index.coffee (_calculateUpperLimits)

--- index.coffee
+++ index.coffee
@@ -14,8 +14,13 @@
       @totalFileSizeInBytes = stats.size
       return cbfn()

+  _calculateUpperLimits: ->
+    @bytesPerHundredMilisecond = Math.floor (@bytesPerSecond / 10)
+    @carryBytes = @bytesPerSecond - (10 * @bytesPerHundredMilisecond)
+
   copy: ->
     @_gatherInputFileStats =>
+      @_calculateUpperLimits()

 @Slowcopy = Slowcopy


Proper namespacing for config key

Proper namespacing for config key

--- index.coffee
+++ index.coffee
@@ -20,7 +20,7 @@
       return fn.call(editor.languageMode, bufferRow, options) unless editor.getGrammar().scopeName == "source.js.jsx"

       scopeDescriptor = @editor.scopeDescriptorForBufferPosition([bufferRow, 0])
-      decreaseNextLineIndentRegex = @getRegexForProperty(scopeDescriptor, 'decreaseIndentForNextLinePattern')
+      decreaseNextLineIndentRegex = @getRegexForProperty(scopeDescriptor, 'react.decreaseIndentForNextLinePattern')
       decreaseIndentRegex = @decreaseIndentRegexForScopeDescriptor(scopeDescriptor)
       increaseIndentRegex = @increaseIndentRegexForScopeDescriptor(scopeDescriptor)

@@ -49,10 +49,10 @@
       return indent unless editor.getGrammar().scopeName == "source.js.jsx" and bufferRow > 1

       scopeDescriptor = @editor.scopeDescriptorForBufferPosition([bufferRow, 0])
-      decreaseNextLineIndentRegex = @getRegexForProperty(scopeDescriptor, 'decreaseIndentForNextLinePattern')
+      decreaseNextLineIndentRegex = @getRegexForProperty(scopeDescriptor, 'react.decreaseIndentForNextLinePattern')
       increaseIndentRegex = @increaseIndentRegexForScopeDescriptor(scopeDescriptor)
-      tagStartRegex = @getRegexForProperty(scopeDescriptor, 'jsxTagStartPattern')
-      complexAttributeRegex = @getRegexForProperty(scopeDescriptor, 'jsxComplexAttributePattern')
+      tagStartRegex = @getRegexForProperty(scopeDescriptor, 'react.jsxTagStartPattern')
+      complexAttributeRegex = @getRegexForProperty(scopeDescriptor, 'react.jsxComplexAttributePattern')

       precedingRow = @buffer.previousNonBlankRow(bufferRow)

@@ -80,9 +80,9 @@
     jsxComplexAttributePattern = '(?x)\\{ [^}"\']* $|\\( [^)"\']* $'
     decreaseIndentForNextLinePattern = '/>\\s*,?\\s*$'

-    atom.config.set("jsxTagStartPattern", jsxTagStartPattern)
-    atom.config.set("jsxComplexAttributePattern", jsxComplexAttributePattern)
-    atom.config.set("decreaseIndentForNextLinePattern", decreaseIndentForNextLinePattern)
+    atom.config.set("react.jsxTagStartPattern", jsxTagStartPattern)
+    atom.config.set("react.jsxComplexAttributePattern", jsxComplexAttributePattern)
+    atom.config.set("react.decreaseIndentForNextLinePattern", decreaseIndentForNextLinePattern)

     # Patch edtiors language mode to get proper indention
     @patchEditorLangMode(editor) for editor in atom.workspace.getTextEditors()


tighten security

tighten security

--- index.coffee
+++ index.coffee
@@ -77,6 +77,8 @@
   app.put '/tastes/:filename', (req, res, next) ->
     if req.params.filename.length > config.maxFilenameLength
       return res.status(414).send()
+    if "/#{req.params.filename}" isnt path.resolve '/', req.params.filename
+      return res.status(400).send()
     relPath = "tastes/#{req.params.filename}"
     fs.exists relPath, (exists) ->
       return res.status(409).send()  if exists


Add entry point to styles

Add entry point to styles

Perhaps this should go into webpack's config as a separate entry point?

--- index.coffee
+++ index.coffee
@@ -1,3 +1,8 @@
+# Require styles.
+# This is the entry point for webpack to recognize and load the styles
+# They're split out from the build into a separate css file
+require './resources/styles/tutor.less'
+
 require 'jquery'

 api = require './src/api'


[Status] Simple status endpoint

[Status] Simple status endpoint

--- index.coffee
+++ index.coffee
@@ -97,56 +97,8 @@
         userMobileNumber
         numberOfDigits
       }
-      res.send JSON.stringify {
+      res.send {
         success: true
-        userMobileNumber
-      }
-    catch err
-      logError err
-      res.status 500
-      res.send JSON.stringify {
-        success: false
-        err: "Server Error: #{ util.format err }"
-        userMobileNumber
-      }
-  .catch (err) ->
-    logError err
-    res.status 500
-    res.send JSON.stringify {
-      success: false
-      err: "Server Error: #{ util.format err }"
-    }
-
-app.all '/checkCode', (req, res) ->
-  co ->
-    res.type 'application/json'
-
-    {
-      userMobileNumber
-      developerEmail
-      code
-    } = req.query
-
-    ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
-
-    badRequest = _badRequestFactory res
-
-    if _basicValidateRequest badRequest, req
-      return
-
-    unless code?
-      return badRequest "`code` must be provided"
-
-    try
-      validCode = yield checkCodeAsync {
-        developerEmail
-        code
-        userMobileNumber
-      }
-      validCode = !!validCode
-      res.send JSON.stringify {
-        success: true
-        validCode
         userMobileNumber
       }
     catch err
@@ -160,11 +112,63 @@
   .catch (err) ->
     logError err
     res.status 500
-    res.send JSON.stringify {
+    res.send {
       success: false
       err: "Server Error: #{ util.format err }"
     }

+app.get '/status', (req, res) ->
+  res.type 'application/json'
+  res.send status: 'ok'
+
+app.all '/checkCode', (req, res) ->
+  co ->
+    res.type 'application/json'
+
+    {
+      userMobileNumber
+      developerEmail
+      code
+    } = req.query
+
+    ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
+
+    badRequest = _badRequestFactory res
+
+    if _basicValidateRequest badRequest, req
+      return
+
+    unless code?
+      return badRequest "`code` must be provided"
+
+    try
+      validCode = yield checkCodeAsync {
+        developerEmail
+        code
+        userMobileNumber
+      }
+      validCode = !!validCode
+      res.send {
+        success: true
+        validCode
+        userMobileNumber
+      }
+    catch err
+      logError err
+      res.status 500
+      res.send {
+        success: false
+        err: "Server Error: #{ util.format err }"
+        userMobileNumber
+      }
+  .catch (err) ->
+    logError err
+    res.status 500
+    res.send {
+      success: false
+      err: "Server Error: #{ util.format err }"
+    }
+

 _badRequestFactory = (res) ->
   """Returns a function that sends bad request responses"""
@@ -172,7 +176,7 @@
   badRequest = (message) ->
     console.log "Bad Request: #{ message }"
     res.status 400
-    res.send JSON.stringify {
+    res.send {
       success: false
       err: "Bad Request: #{ message }"
     }


`ready` signals in `view` style fixed

`ready` signals in `view` style fixed

--- index.coffee
+++ index.coffee
@@ -112,7 +112,7 @@
 			elem.unshift "'this'"
 		else if id is 'this'
 			elem[0] = "'this'"
-		else if id is 'view' or ids.hasOwnProperty(id)
+		else if (id is 'view' or ids.hasOwnProperty(id)) and (i is 0 or binding[i-1][binding[i-1].length - 1] isnt '.')
 			continue
 		else
 			binding[i] = elem.join '.'
@@ -310,7 +310,7 @@
 	code += stringObjectChildren obj
 	code += stringAttributes obj
 	code += stringFunctions obj
-	code += "});"
+	code += "});\n"
 	code

 module.exports = (file, filename) ->
@@ -326,6 +326,14 @@

 	idsKeys = Object.keys ids

+	if filename is 'view'
+		code += "setImmediate(function(){\n"
+	code += "for (var _id in ids){\n"
+	code += "	ids[_id].ready(); ids[_id].onReady.disconnectAll();\n"
+	code += "}\n"
+	if filename is 'view'
+		code += "});\n"
+
 	code += "var mainItem;\n"
 	for elem in elems
 		if elem.type is OBJECT


Reference the acutal array to ensure the array is mutated

--- index.coffee
+++ index.coffee
@@ -57,8 +57,8 @@
 			if @handlers[event][key]?
 				remove @handlers[event][key], (cbs) -> cbs.original == callback
 		else
-			for arr in @handlers[event]
-				remove arr, (cbs) -> cbs.original == callback
+			for key of @handlers[event]
+				remove @handlers[event][key], (cbs) -> cbs.original == callback

 	###
 	Create an event container for the window event type and


Minor cleanup

Minor cleanup

--- index.coffee
+++ index.coffee
@@ -1,5 +1,3 @@
-
-fuel = require 'voxel-fuel'

 # plugins (loaded by voxel-plugins; listed here for browserify)
 require 'voxel-engine'
@@ -31,23 +29,21 @@
 require 'voxel-drop'
 require 'voxel-start'
 require 'voxel-zen'
-
 require 'voxel-debug'
 require 'voxel-plugins-ui'
 require 'kb-bindings-ui'

-createArtpacks = require 'artpacks'
+artpacks = require 'artpacks'
+fuel = require 'voxel-fuel'

 main = () ->
   console.log 'voxpopuli starting'

-  isClient = process.browser # TODO: game
-
-  if isClient and window.performance && window.performance.timing
+  if window? and window.performance && window.performance.timing
     loadingTime = Date.now() - window.performance.timing.navigationStart
     console.log "User-perceived page loading time: #{loadingTime / 1000}s"

-  pluginOpts =
+  fuel {require:require, pluginOpts:
     'voxel-engine':
       appendDocument: true
       exposeGlobal: true  # for debugging
@@ -65,7 +61,7 @@
       chunkDistance: 2
       materials: []  # added dynamically later
       texturePath: 'ArtPacks/ProgrammerArt/textures/blocks/' # subproject with textures
-      artPacks: if isClient then createArtpacks ['ProgrammerArt-v2.1-ResourcePack-MC17.zip'] else []
+      artPacks: artpacks ['ProgrammerArt-v2.1-ResourcePack-MC17.zip']
       worldOrigin: [0, 0, 0]
       controls:
         discreteFire: false
@@ -149,9 +145,6 @@
     'voxel-debug': {}
     'voxel-plugins-ui': {}
     'kb-bindings-ui': {}
-
-  fuel
-    pluginOpts: pluginOpts
-    require: require
+  }

 main()


Set global.InventoryWindow_defaultGetTexture for inventory-window

Refactoring to make item texturing codepaths cleaner, less intertwined.

Requires all these commits:
deathcap/inventory-window@0c9f608123dfd6596891282a0a53249e42af2645
deathcap/voxel-workbench@d6a6ca1ed6d913072c99ce32b1b01cdc9f603469
deathcap/voxel-inventory-hotbar@ce194714d7288c9cb2eaf8f097d7049583c6841e
deathcap/voxel-inventory-dialog@f44eed1af728181deecd4d9550a7bbbfb5e503be

--- index.coffee
+++ index.coffee
@@ -108,6 +108,7 @@

   game.materials.load registry.getBlockPropsAll 'texture'
+  global.InventoryWindow_defaultGetTexture = (itemPile) => registry.getItemPileTexture(itemPile)

   plugins.load 'land', {
     populateTrees: true
@@ -163,11 +164,11 @@
   #playerInventory.give new ItemPile('workbench', 1)
   #toolbar = createToolbar {el: '#tools'}
   #inventoryToolbar = plugins.load 'inventory-toolbar', {toolbar:toolbar, inventory:playerInventory, inventorySize:10, registry:registry}
-  inventoryHotbar = plugins.load 'inventory-hotbar', {inventory:playerInventory, inventorySize:10, registry:registry}
-
-  inventoryDialog = plugins.load 'inventory-dialog', {playerInventory:playerInventory, registry:registry}
-
-  workbenchDialog = plugins.load 'workbench', {playerInventory:playerInventory, registry:registry}
+  inventoryHotbar = plugins.load 'inventory-hotbar', {inventory:playerInventory, inventorySize:10}
+
+  inventoryDialog = plugins.load 'inventory-dialog', {playerInventory:playerInventory}
+
+  workbenchDialog = plugins.load 'workbench', {playerInventory:playerInventory}

   REACH_DISTANCE = 8
   reach = game.plugins.load 'reach', { reachDistance: REACH_DISTANCE }


return cursor to correct position

return cursor to correct position

--- index.coffee
+++ index.coffee
@@ -107,9 +107,11 @@
     editor.transact =>
       for selection in selections
         try
+          bufStart = selection.getBufferRange().serialize()[0]
           jsxformat.setOptions({});
           result = jsxformat.format(selection.getText())
           selection.insertText(result, {autoIndent: true});
+          editor.setCursorBufferPosition(bufStart)
         catch err
           # Parsing/formatting the selection failed lets try to parse the whole file but format the selection only
           range = selection.getBufferRange().serialize()


Pass NoDefaultExcludes option to nuget

Pass NoDefaultExcludes option to nuget

Banning dot files by default is unexpected.

Refs atom/symbols-view#63

--- index.coffee
+++ index.coffee
@@ -57,6 +57,7 @@
       appDirectory
       '-OutputDirectory'
       nugetOutput
+      '-NoDefaultExcludes'
     ]

     exec {cmd, args}, (error) ->


fixing shake for transform chain

fixing shake for transform chain

--- src/camera_shake.coffee
+++ src/camera_shake.coffee
@@ -8,8 +8,8 @@

   self.transformFilterChain (transform) ->
     if I.shakeCooldown > 0
-      I.transform.tx += signedRand(I.shakeIntensity)
-      I.transform.ty += signedRand(I.shakeIntensity)
+      transform.tx += signedRand(I.shakeIntensity)
+      transform.ty += signedRand(I.shakeIntensity)

     return transform


Server: remove dash usage

Server: remove dash usage

--- servers/lib/server/customers.coffee
+++ servers/lib/server/customers.coffee
@@ -2,7 +2,7 @@
   '../../../workers/social/lib/social/models/socialapi/requests.coffee'
 )

-{ dash } = require 'bongo'
+async = require 'async'

 module.exports = (req, res) ->
   koding              = require './bongo'
@@ -40,15 +40,15 @@
         return res.status(400).send err  if err

         usernames.forEach (username) ->
-          queue.push -> JMachine.fetchByUsername username, (err, machines) ->
+          queue.push (fin) -> JMachine.fetchByUsername username, (err, machines) ->
             if err
-              queue.fin()
+              fin()
             else
               slugs = []
               machines.forEach (machine) ->
                 slugs.push  machine.data.slug  if machine.data.meta.alwaysOn

               response.push { 'username' : username, 'vms' : slugs }
-              queue.fin()
+              fin()

-      dash queue, -> res.status(200).send response
+      async.parallel queue, -> res.status(200).send response