Update uk.coffee

Update uk.coffee

--- app/locale/uk.coffee
+++ app/locale/uk.coffee
@@ -171,7 +171,7 @@
     accepted: "Прийнято"
     rejected: "Відхилено"
     withdrawn: "Відкликано"
-#    submitter: "Submitter"
+    submitter: "Відправник"
     submitted: "Відправлено"
     commit_msg: "Доручити повідомлення"
     review: "Огляд"
@@ -337,7 +337,7 @@
     tip_adding_evil: "Додамо дрібку зла."
     tip_hate_computers: "Ось правда про людей, які вважають, що ненавидять комп'ютери. Що вони насправді ненавидять, це паршивий програмерів. - Ларрі Нівен"
     tip_open_source_contribute: "Ви можете допомогти CodeCombat покращитися!"
-    tip_recurse: "Ітерація - від люини, рекурсія - від бога. - Л. Пітер Дойч"
+    tip_recurse: "Ітерація - від людини, рекурсія - від бога. - Л. Пітер Дойч"

   game_menu:
     inventory_tab: "Інвентар"
@@ -431,29 +431,29 @@
     stripe_description: "Щомісячний абонемент"
     subscription_required_to_play: "Аби грати в цьому рівні потрібен абонемент."
     unlock_help_videos: "Підпишіться, щоб відкрити усі навчальні відео."
-#    personal_sub: "Personal Subscription" # Accounts Subscription View below
-#    loading_info: "Loading subscription information..."
-#    managed_by: "Managed by"
-#    will_be_cancelled: "Will be cancelled on"
-#    currently_free: "You currently have a free subscription"
-#    currently_free_until: "You currently have a free subscription until"
-#    was_free_until: "You had a free subscription until"
-#    managed_subs: "Managed Subscriptions"
-#    managed_subs_desc: "Add subscriptions for other players (students, children, etc.)"
-#    group_discounts: "Group discounts"
-#    group_discounts_1st: "1st subscription (includes yours)"
-#    group_discounts_full: "Full price"
-#    group_discounts_2nd: "Subscriptions 2-11"
-#    group_discounts_20: "20% off"
-#    group_discounts_12th: "Subscriptions 12+"
-#    group_discounts_40: "40% off"
-#    subscribing: "Subscribing..."
-#    recipient_emails_placeholder: "Enter email address to subscribe, one per line."
-#    subscribe_users: "Subscribe Users"
-#    users_subscribed: "Users subscribed:"
-#    no_users_subscribed: "No users subscribed, please double check your email addresses."
-#    current_recipients: "Current Recipients"
-#    unsubscribing: "Unsubscribing..."
+    personal_sub: "Особистий абонемент" # Accounts Subscription View below
+    loading_info: "Завантажується інформація про абонемент..."
+    managed_by: "Керівник - "
+    will_be_cancelled: "Буде скасовано"
+    currently_free: "Зараз у Вас безкоштовний абонемент"
+    currently_free_until: "Зараз у Вас безкоштовний абонемент до "
+    was_free_until: "У Вас був безкоштовний абонемент до "
+    managed_subs: "Керовані абонементи"
+    managed_subs_desc: "Додати абонементи для інших гравців (учнів, дітей тощо)"
+    group_discounts: "Групові знижки"
+    group_discounts_1st: "1-ий абонемент (включає Ваш)"
+    group_discounts_full: "Повна ціна"
+    group_discounts_2nd: "2-11 абонементи"
+    group_discounts_20: "Знижка 20%"
+    group_discounts_12th: "Абонементи від 12-го"
+    group_discounts_40: "Знижка 40%"
+    subscribing: "Передплата..."
+    recipient_emails_placeholder: "Введіть ел. адреси для передплати, по одній в рядку."
+    subscribe_users: "Підписати користувачів"
+    users_subscribed: "Підписані користувачі:"
+    no_users_subscribed: "Користувачі не підписані, будь ласка, перевірте Ваші ел. адреси."
+    current_recipients: "Поточні отримувачі"
+    unsubscribing: "Скасування передплати..."

   choose_hero:
     choose_hero: "Оберіть героя"
@@ -468,7 +468,7 @@
     lua_blurb: "Мова ігрових сценаріїв."
     io_blurb: "Проста, але дивна."
     status: "Статус"
-#    hero_type: "Type"
+    hero_type: "Тип"
     weapons: "Зброя"
     weapons_warrior: "Мечі – ближній бій, жодної магії"
     weapons_ranger: "Стрілецька зброя – дистанційна атака, жодної магії"
@@ -487,8 +487,8 @@
 #    health_1: "Gains"
 #    health_2: "of listed"
 #    health_3: "armor health."
-#    speed_1: "Moves at"
-#    speed_2: "meters per second."
+    speed_1: "Рухається "
+    speed_2: " метрів на секунду."
     available_for_purchase: "Можна придбати" # Shows up when you have unlocked, but not purchased, a hero in the hero store
     level_to_unlock: "Розблокується на рівні:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
     restricted_to_certain_heroes: "Тільки певні герої можуть грати в цьому рівні."
@@ -564,58 +564,58 @@
     michael_blurb: "Сисадмін"
     matt_title: "Програміст"
     matt_blurb: "Велосипедист"
-#    cat_title: "Chief Artisan"
-#    cat_blurb: "Airbender"
-#    josh_title: "Game Designer"
+    cat_title: "Головний ремісник"
+    cat_blurb: "Маг повітря"
+    josh_title: "Дизайнер гри"
 #    josh_blurb: "Floor Is Lava"
-#    jose_title: "Music"
+    jose_title: "Музика"
 #    jose_blurb: "Taking Off"
-#    retrostyle_title: "Illustration"
-#    retrostyle_blurb: "RetroStyle Games"
-
-#  teachers:
-#    title: "CodeCombat for Teachers"
-#    preparation_title: "Preparation"
-#    preparation_1: "CodeCombat is free to play for the core level progression and does not require students to sign up. We encourage teachers to"
-#    preparation_play_campaign: "play through the campaign"
-#    preparation_2: "to try it out, but the only thing you absolutely need to do to be ready is ensure students have access to a computer."
-#    preparation_3: "It is not necessary for teachers to be comfortable with computer science concepts for students to have fun learning with CodeCombat."
-#    violent_title: "Is it violent?"
-#    violent_1: "We get this from teachers a lot due to our name. Although CodeCombat does contain cartoon violence, there is nothing graphic in either the visuals or language."
-#    violent_2: "If you are comfortable having your students play Angry Birds, you will be comfortable with CodeCombat."
-#    for_girls_title: "Is it for girls?"
-#    for_girls_1: "There are three game modes in CodeCombat: building, puzzles, and combat. We have intentionally designed each to appeal to both boys and girls and think that the building and puzzle levels especially differentiate the game from violent triple A titles that repel female players."
-#    age_recommended_title: "What age is it recommended at?"
-#    age_recommended_1: "The recommended minimum age is 9, however children as young as 7 can play the first stages with the assistance of an adult."
-#    what_cover_title: "What do we cover?"
-#    what_cover_1: "There are 27 levels in the first stage of CodeCombat that teach and reinforce 6 specific computer science concepts:"
-#    what_cover_notation_1: "Formal notation"
-#    what_cover_notation_2: "- builds an understanding of the importance of syntax in programming."
-#    what_cover_methods_1: "Calling methods"
-#    what_cover_methods_2: "- familiarizes students with the syntax of object-oriented method calls."
-#    what_cover_parameters_1: "Parameters"
-#    what_cover_parameters_2: "- trains how to pass parameters to functions."
-#    what_cover_strings_1: "Strings"
+    retrostyle_title: "Ілюстрація"
+    retrostyle_blurb: "Ігри в стилі ретро"
+
+  teachers:
+    title: "CodeCombat для вчителів"
+    preparation_title: "Підготовка"
+    preparation_1: "CodeCombat безкоштовний для проходження основних рівнів і студентам не обов'язково реєструватися. Ми заохочуємо вчителів "
+    preparation_play_campaign: "пройти кампанію, "
+    preparation_2: "щоб спробувати її, але єдине, що Вам точно треба зробити для підготовки, це переконатися, що в учнів є доступ до комп'ютера. "
+    preparation_3: "Учителям необов'язково знатися на концептах комп'ютерної науки, щоб учні отримували задоволення від навчання у CodeCombat. "
+    violent_title: "Це жорстоко?"
+    violent_1: "Вчителі запитують нас це переважно через нашу назву. Хоча CodeCombat містить жорстокі малюнки, нічого такого немає ні в візуалізаціях, ні в мові."
+    violent_2: "Якщо для Вас нормально, що учні грають в Angry Birds, то CodeCombat так само нормальний."
+    for_girls_title: "Дівчатам підходить?"
+#    for_girls_1: "У CodeCombat є три режими гри: будування, пазли і битви. Ми розробляли кожен так, щоб підходило і хлопцям, і дівчатам і думаємо, що будування і пазлиhink that the building and puzzle levels especially differentiate the game from violent triple A titles that repel female players."
+    age_recommended_title: "Для якого віку це рекомендуєте?"
+    age_recommended_1: "Мінімальний рекомендований вік — 9 років, однак перші етапи з допомогою дорослих можуть проходити й діти 7-ми років."
+    what_cover_title: "Що ми отримуємо?"
+    what_cover_1: "На першому етапі CodeCombat є 27 рівнів, що навчають і закріплюють 6 визначних принципів комп'ютерної науки:"
+    what_cover_notation_1: "Формальний запис"
+    what_cover_notation_2: "- розвиває розуміння важливості синтаксису у програмуванні."
+    what_cover_methods_1: "Методи виклику"
+    what_cover_methods_2: "- знайомить учнів з синтаксисом об'єктно-орієнтованих викликів."
+    what_cover_parameters_1: "Параметри"
+    what_cover_parameters_2: "- вчить, як передавати параметри у функції."
+    what_cover_strings_1: "Рядки"
 #    what_cover_strings_2: "- teaches students about string notation and passing strings as parameters."
-#    what_cover_loops_1: "Loops"
-#    what_cover_loops_2: "- develops the abstraction of designing short programs with loops."
-#    what_cover_variables_1: "Variables"
-#    what_cover_variables_2: "- adds the skill of referencing values that change over time."
-#    what_cover_2: "Students may continue past level 20, depending on their speed and interest, to learn two additional concepts in later levels:"
-#    what_cover_logic_1: "Conditional logic"
-#    what_cover_logic_2: "- when and how to use if/else to control in-game outcomes."
-#    what_cover_input_1: "Handling player input"
-#    what_cover_input_2: "- responding to input events to create a user interface."
-#    what_cover_array_1: "Arrays"
-#    what_cover_array_2: "- allows the player to create and loop through arrays."
-#    sys_requirements_title: "System Requirements"
-#    sys_requirements_1: "Because CodeCombat is a game, it is more intensive for computers to run smoothly than video or written tutorials. We have optimized it to run quickly on all modern browsers and on older machines so that everyone can play. That said, here are our suggestions for getting the most out of the CodeCombat experience:"
-#    sys_requirements_2: "Use newer versions of Chrome or Firefox."
-#    sys_requirements_3: "Although CodeCombat will work on browsers as old as Internet Explorer 9, the performance is not as good. Chrome is best."
-#    sys_requirements_4: "Use newer computers."
-#    sys_requirements_5: "Older computers, Chromebooks, and netbooks tend to have very few system resources, which makes for a less enjoyable experience. At least 2GB of RAM is required."
-#    sys_requirements_6: "Allow players to wear headphones/earbuds to hear the audio."
-#    sys_requirements_7: "We help players learn through voiceover and sound effects, which will make classrooms noisy and distracting."
+    what_cover_loops_1: "Цикли"
+    what_cover_loops_2: "- розвиває уявлення про створення коротких програм з допомогою циклів."
+    what_cover_variables_1: "Змінні"
+    what_cover_variables_2: "- додає навичку посилання на значення, що змінюються з часом."
+    what_cover_2: "Учні можуть продовжити далі 20-го рівня, залежно від їхньої швидкості та зацікавленості, й оволодіти двома додатковими принципами на пізніших рівнях:"
+    what_cover_logic_1: "Умовна логіка"
+    what_cover_logic_2: "- коли і як використовувати if/else щоб керувати результатами в грі."
+    what_cover_input_1: "Відгук на дії гравця"
+    what_cover_input_2: "- відповіді на події введення, щоб створити користувацький інтерфейс."
+    what_cover_array_1: "Масиви"
+    what_cover_array_2: "- дозволяє гравцеві створювати і виконувати дії з масивами."
+    sys_requirements_title: "Системні вимоги"
+    sys_requirements_1: "Оскільки CodeCombat — це гра, для нормальної роботи він вимагає у комп'ютерів більше, ніж відео чи текстові посібники. Ми оптимізували його для швидкої роботи в усіх сучасних браузерах і на старіших машинах, щоб кожен міг грати. І ось наші підказки, як отримати від CodeCombat якнайбільше:"
+    sys_requirements_2: "Використовуйте новіші версії Chrome або Firefox."
+    sys_requirements_3: "Хоча CodeCombat працюватиме у браузерах на зразок Internet Explorer 9, але не так добре. Chrome найкращий."
+    sys_requirements_4: "Використовуйте новіші комп'ютери."
+    sys_requirements_5: "Старіші комп'ютери, Chromebook'и і нетбуки можуть мати замало системних ресурсів, через що гра стає не така приємна. Необхідно щонайменше 2GB оперативної пам'яті."
+    sys_requirements_6: "Дозвольте гравцям одягнути навушники, щоб чути аудіо."
+    sys_requirements_7: "Ми допомагаємо гравцям навчатися через закадровий голос і звукові ефекти, через що класна кімната може стати шумною відволікаючою."

   versions:
     save_version_title: "Зберегти нову версію"
@@ -650,17 +650,17 @@
     autosave: "Зміни зберігаються автоматично"
     me_tab: "Я"
     picture_tab: "Аватар"
-#    delete_account_tab: "Delete Your Account"
-#    wrong_email: "Wrong Email"
+    delete_account_tab: "Вилучити свій акаунт"
+    wrong_email: "Неправильний email"
     upload_picture: "Відвантажити зображення"
-#    delete_this_account: "Delete this account permanently"
+    delete_this_account: "Вилучити цей акаунт назовсім"
     god_mode: "Режим Бога"
     password_tab: "Пароль"
     emails_tab: "Email-адреси"
     admin: "Aдмін"
     new_password: "Новий пароль"
     new_password_verify: "Підтвердження паролю"
-#    type_in_email: "Type in your email to confirm the deletion"
+    type_in_email: "Введіть свій email, щоб підтвердити вилучення"
     email_subscriptions: "Email-підписки"
     email_subscriptions_none: "Жодних підписок."
     email_announcements: "Оголошення"
@@ -728,22 +728,22 @@
   classes:
     archmage_title: "Архімаг"
     archmage_title_description: "(Програміст)"
-    archmage_summary: "Якщо ви зацікавлені в програмуванні навчальних ігор, станьте архімагом аби допомогти нам створювати CodeCombat!"
+    archmage_summary: "Якщо Ви зацікавлені в програмуванні навчальних ігор, станьте архімагом аби допомогти нам створювати CodeCombat!"
     artisan_title: "Ремісник"
     artisan_title_description: "(Створювач рівнів)"
-#    artisan_summary: "Build and share levels for you and your friends to play. Become an Artisan to learn the art of teaching others to program."
+    artisan_summary: "Створюйте і поширюйте рівні для себе і своїх друзів. Стань ремісником, щоб опанувати мистецтво навчання інших програмуванню."
     adventurer_title: "Шукач пригод"
     adventurer_title_description: "(Тестувальник рівнів)"
-#    adventurer_summary: "Get our new levels (even our subscriber content) for free one week early and help us work out bugs before our public release."
+    adventurer_summary: "Отримуйте наші нові рівні (навіть вміст передплат) безкоштовно на тиждень раніше і допомагайте нам шукати баґи до публічного випуску."
     scribe_title: "Писар"
     scribe_title_description: "(Редактор статей)"
     scribe_summary: "Хороший код потребує хорошої документації. Пишіть, редагуйте, та покращуйте документацію, яку побачать мільйони гравців зі всього світу."
     diplomat_title: "Дипломат"
     diplomat_title_description: "(Перекладач)"
     diplomat_summary: "За допомогою наших дипломатів CodeCombat локалізовано більш ніж 45 мовами. Ви також можете допомогти з перекладом."
-    ambassador_title: "Посланець"
+    ambassador_title: "Посол"
     ambassador_title_description: "(Підтримка)"
-#    ambassador_summary: "Tame our forum users and provide direction for those with questions. Our ambassadors represent CodeCombat to the world."
+    ambassador_summary: "Вгамовуйте користувачів нашого форуму і скеровуйте тих, хто має запитання. Наші посли — представники CodeCombat у світі."

   editor:
     main_title: "Редактори CodeCombat"
@@ -751,7 +751,7 @@
     thang_title: "Редактор об'єктів"
     level_title: "Редактор рівнів"
     achievement_title: "Редактор досягнень"
-#    poll_title: "Poll Editor"
+    poll_title: "Редактор опитувань"
     back: "Назад"
     revert: "Повернутись"
     revert_models: "Моделі повернення"
@@ -812,13 +812,13 @@
     new_level_title_login: "Увійдіть, щоб створити новий рівень"
     new_achievement_title: "Створити нове досягнення"
     new_achievement_title_login: "Увійдіть, щоб створити нове досягнення"
-#    new_poll_title: "Create a New Poll"
-#    new_poll_title_login: "Log In to Create a New Poll"
+    new_poll_title: "Створити нове опитування"
+    new_poll_title_login: "Увійдіть, щоб створити нове опитування"
     article_search_title: "Шукати статті тут"
     thang_search_title: "Шукати типи об'єктів тут"
     level_search_title: "Шукати рівні тут"
     achievement_search_title: "Шукати досягнення"
-#    poll_search_title: "Search Polls"
+    poll_search_title: "Шукати опитування"
     read_only_warning2: "Примітка: Ви не можете зберегти ніякі зміни, оскільки Ви не зареєструвались."
     no_achievements: "Для цього рівня ще не додано жодних досягнень."
     achievement_query_misc: "Ключове досягнення випадає з різного"
@@ -826,14 +826,14 @@
     level_completion: "Рівень завершено"
     pop_i18n: "Додати I18N"
     tasks: "Завдання"
-#    clear_storage: "Clear your local changes"
+    clear_storage: "Очистити свої локальні зміни"

   article:
     edit_btn_preview: "Перегляд"
     edit_article_title: "Редагувати статтю"

-#  polls:
-#    priority: "Priority"
+  polls:
+    priority: "Пріоритет"

   contribute:
     page_title: "Співпраця"
@@ -1013,15 +1013,15 @@
     status_unsubscribed_active: "У Вас немає передплати і рахунок Вам не прийде, але Ваш акаунт і далі дійсний."
     status_unsubscribed: "Отримайте доступ до новних рівнів, героїв та бонусів з абонементом CodeCombat!"

-#  account_invoices:
-#    amount: "Amount in US dollars"
-#    declined: "Your card was declined"
-#    invalid_amount: "Please enter a US dollar amount."
-#    not_logged_in: "Log in or create an account to access invoices."
-#    pay: "Pay Invoice"
-#    purchasing: "Purchasing..."
-#    retrying: "Server error, retrying."
-#    success: "Successfully paid. Thanks!"
+  account_invoices:
+    amount: "Сума у доларах США"
+    declined: "Вашу картку відхилено"
+    invalid_amount: "Будь ласка, введіть суму в доларах США."
+    not_logged_in: "Увійдіть або створіть акаунт, щоб мати доступ до інвойсів."
+    pay: "Оплатити інвойс"
+    purchasing: "Оплата..."
+    retrying: "Помилка сервера, повторна спроба."
+    success: "Успішно оплачено. Дякуємо!"

   loading_error:
     could_not_load: "Помилка завантаження з сервера"
@@ -1083,8 +1083,8 @@
     feedback: "Відгук"
     payment_info: "Інформація про платіж"
     campaigns: "Кампаніі"
-#    poll: "Poll"
-#    user_polls_record: "Poll Voting History"
+    poll: "Опитування"
+    user_polls_record: "Історія голосування в опитуваннях"

   delta:
     added: "Додано"
@@ -1223,44 +1223,44 @@
     basics_job_title: "Бажана позиція у роботі"
     basics_job_title_help: "Яку роль Ви шукаєте?"
     basics_city: "Місто"
-    basics_city_help: "Місто в якому б Ви хотіли працювати (або зараз проживаєте)."
+    basics_city_help: "Місто, в якому б Ви хотіли працювати (або зараз проживаєте)."
     basics_country: "Країна"
-#    basics_country_help: "Country you want to work in (or live in now)."
-#    basics_visa: "US Work Status"
-#    basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)"
-#    basics_looking_for: "Looking For"
-#    basics_looking_for_full_time: "Full-time"
-#    basics_looking_for_part_time: "Part-time"
-#    basics_looking_for_remote: "Remote"
-#    basics_looking_for_contracting: "Contracting"
-#    basics_looking_for_internship: "Internship"
-#    basics_looking_for_help: "What kind of developer position do you want?"
-#    name_header: "Fill in your name"
+    basics_country_help: "Країна, в якій Ви хотіли б працювати (або зараз живете)."
+    basics_visa: "Робочий статус у США"
+    basics_visa_help: "У Вас є право на роботу у США чи Вам потрібна допомога з отриманням візи? (Якщо проживаєте у Канаді чи Австралії, вкажіть, як перше.)"
+    basics_looking_for: "Шукаю"
+    basics_looking_for_full_time: "Повна"
+    basics_looking_for_part_time: "Часткова"
+    basics_looking_for_remote: "Віддалена"
+    basics_looking_for_contracting: "За контрактом"
+    basics_looking_for_internship: "Стажування"
+    basics_looking_for_help: "Яку посаду розробника Ви б хотіли?"
+    name_header: "Введіть своє ім'я"
     name_anonymous: "Анонімний розробник"
-#    name_help: "Name you want employers to see, like 'Nick Winter'."
-#    short_description_header: "Write a short description of yourself"
-#    short_description_blurb: "Add a tagline to help an employer quickly learn more about you."
-#    short_description: "Tagline"
-#    short_description_help: "Who are you, and what are you looking for? 140 characters max."
+    name_help: "Ім'я, яке Ви хочете повідомити своїм роботодавців, напр., 'Nick Winter'."
+    short_description_header: "Коротко розкажіть про себе"
+    short_description_blurb: "Додайте слоган, за яким роботодавець швидко складе уявлення про Вас."
+    short_description: "Слоган"
+    short_description_help: "Хто Ви і чого шукаєте? Макс. 140 символів."
     skills_header: "Навички"
-#    skills_help: "Tag relevant developer skills in order of proficiency."
-#    long_description_header: "Describe your desired position"
-#    long_description_blurb: "Tell employers how awesome you are and what role you want."
-#    long_description: "Self Description"
-#    long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max."
+    skills_help: "Вкажіть відповідні розробницькі навички за досвідченістю."
+    long_description_header: "Опишіть бажану посаду"
+    long_description_blurb: "Розкажіть роботодавцям, які Ви чудові і яку роль для себе хочете."
+    long_description: "Представлення"
+    long_description_help: "Опишіть себе потенційним роботодавцям. Коротко і по суті. Рекомендуємо підкреслити позицію, яка найбільше Вас цікавить. Допустима доречна розмітка, макс. 600 символів."
     work_experience: "Досвід роботи"
-#    work_header: "Chronicle your work history"
-#    work_years: "Years of Experience"
-#    work_years_help: "How many years of professional experience (getting paid) developing software do you have?"
-#    work_blurb: "List your relevant work experience, most recent first."
-#    work_employer: "Employer"
-#    work_employer_help: "Name of your employer."
-#    work_role: "Job Title"
-#    work_role_help: "What was your job title or role?"
-#    work_duration: "Duration"
+    work_header: "Вкажіть хроніку своєї історії роботи"
+    work_years: "Роки досвіду"
+    work_years_help: "Скільки у Вас років досвіду професійної розробки програмного забезпечення (з отриманням платні)?"
+    work_blurb: "Список відповідного досвіду роботи, від най свіжішого."
+    work_employer: "Роботодавець"
+    work_employer_help: "Ім'я Вашого роботодавця."
+    work_role: "Назва посади"
+    work_role_help: "Як називалась Ваша посада чи роль?"
+    work_duration: "Тривалість"
 #    work_duration_help: "When did you hold this gig?"
-#    work_description: "Description"
-#    work_description_help: "What did you do there? (140 chars; optional)"
+    work_description: "Опис"
+    work_description_help: "Що Ви там робили? (140 символів; необов'язково)"
     education: "Освіта"
 #    education_header: "Recount your academic ordeals"
 #    education_blurb: "List your academic ordeals."
@@ -1273,7 +1273,7 @@
 #    education_description: "Description"
 #    education_description_help: "Highlight anything about this educational experience. (140 chars; optional)"
     our_notes: "Наші примітки"
-#    remarks: "Remarks"
+    remarks: "Примітки"
     projects: "Роботи"
 #    projects_header: "Add 3 projects"
 #    projects_header_2: "Projects (Top 3)"


No initial view yet

No initial view yet

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/supersonic.git/'


Add the script to listen and decorate for messaging to Slack

error: pathspec 'src/esa-slack.coffee' did not match any file(s) known to git

--- src/esa-slack.coffee
+++ src/esa-slack.coffee
@@ -0,0 +1,116 @@
+# Descriptipn
+#   Hubot script decorated message from webhook of https://esa.io
+#
+# Dependencies:
+#   hubot-esa
+#   hubot-slack
+#
+# Commands:
+#   None
+#
+# Configuration:
+#   HUBOT_ESA_SLACK_DECORATOR
+#
+# Author:
+#   hmsk <k.hamasaki@gmail.com>
+#
+
+module.exports = (robot) ->
+  options =
+    enabled: process.env.HUBOT_ESA_SLACK_DECORATOR == 'true'
+
+  if options.enabled
+    # https://api.slack.com/docs/attachments
+    buildContent = (message) ->
+      content =
+        color: '#13958D' # Theme color from esa icon
+        fields: []
+        pretext: message
+        fallback: ''
+        thumb_url: 'https://img.esa.io/uploads/production/pictures/105/6161/image/425c3b1e777d356c34973e818543420e.gif'
+
+    emitSlackAttachment = (content, channel) ->
+      channel ?= '#notification'
+      att =
+        channel: channel
+        content: content
+      robot.emit 'slack.attachment', att
+
+    robot.on 'esa.webhook', (kind, data) ->
+      putUserAndPostToContent = (content, user, post) ->
+        content.author_name = user.screen_name
+        content.author_icon = user.icon.url
+        content.title = post.name
+        content.title_link = post.url
+
+      message_by_kind =
+        'post_create': 'New post created'
+        'post_update': 'The post updated'
+        'post_archive': 'The post archived'
+        'comment_create': 'The comment posted'
+        'member_join': 'New member joined'
+
+      content = buildContent message_by_kind[kind]
+      putUserAndPostToContent content, data.user, data.post unless kind is 'member_join'
+
+      switch kind
+        when 'post_create', 'post_update'
+          content.text = data.post.message
+        when 'comment_create'
+          content.text = data.comment.body_md
+        when 'member_join'
+          content.text = data.user.screen_name
+
+      emitSlackAttachment(content)
+
+    robot.on 'esa.hear.stats', (res, stats) ->
+      content = buildContent 'The stats of esa'
+      content.fields = [
+        {
+          title: 'Posts'
+          value: stats.posts
+          short: true
+        },
+        {
+          title: 'Comments'
+          value: stats.comments
+          short: true
+        },
+        {
+          title: 'Stars'
+          value: stats.stars
+          short: true
+        },
+        {
+          title: 'Daily Active Users'
+          value: stats.daily_active_users
+          short: true
+        },
+        {
+          title: 'Weekly Active Users'
+          value: stats.weekly_active_users
+          short: true
+        },
+        {
+          title: 'Monthly Active Users'
+          value: stats.monthly_active_users
+          short: true
+        }
+      ]
+      emitSlackAttachment(content, res.envelope.room)
+
+    robot.on 'esa.hear.post', (res, post) ->
+      content = buildContent ''
+      content.title = post.full_name
+      content.title_link = post.url
+      content.text = post.body_md
+      emitSlackAttachment(content, res.envelope.room)
+
+    robot.on 'esa.hear.comment', (res, comment, post) ->
+      content = buildContent ''
+      content.title = 'Comment for ' + post.full_name
+      content.title_link = comment.url
+      content.text = comment.body_md
+      content.author_name = comment.created_by.screen_name
+      content.author_icon = comment.created_by.icon
+      emitSlackAttachment(content, res.envelope.room)


Add spec for required ports

Add spec for required ports

error: pathspec 'spec/Component.coffee' did not match any file(s) known to git

--- spec/Component.coffee
+++ spec/Component.coffee
@@ -0,0 +1,38 @@
+if typeof process isnt 'undefined' and process.execPath and process.execPath.indexOf('node') isnt -1
+  chai = require 'chai' unless chai
+  component = require '../src/lib/Component.coffee'
+  port = require '../src/lib/Port.coffee'
+  socket = require '../src/lib/InternalSocket.coffee'
+else
+  component = require 'noflo/src/lib/Component.js'
+  port = require 'noflo/src/lib/Port.js'
+  socket = require 'noflo/src/lib/InternalSocket.js'
+
+describe 'Component', ->
+  describe 'with required ports', (done) ->
+    it 'should throw an error upon receiving packet with an unattached required port', (done) ->
+      s1 = new socket
+      s2 = new socket
+      c = new component
+        inPorts:
+          requiredPort: new inport
+            required: true
+          optionalPort: new inport
+      c.inPorts.optionalPort.attach s2
+      run = ->
+        s2.send 'some-data'
+      chai.expect(run).to.throw()
+
+    it 'should be cool with an attached port', (done) ->
+      s1 = new socket
+      s2 = new socket
+      c = new component
+        inPorts:
+          requiredPort: new inport
+            required: true
+          optionalPort: new inport
+      c.inPorts.requiredPort.attach s1
+      c.inPorts.optionalPort.attach s2
+      run = ->
+        s2.send 'some-data'
+      chai.expect(run).to.not.throw()


Whoops.

Whoops.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/philbooth/GitHubReminder.git/'


Fix extra line breaks in error messages

Fix extra line breaks in error messages

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/nathan/lemma.git/'


Add explicit obj braces for consistency and per lint config

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -31,7 +31,8 @@
     unbind

 # eventTransformer - defaults to returning the first argument to handler
-Bacon.$ = asEventStream: (eventName, selector, eventTransformer) ->
+Bacon.$ = {}
+Bacon.$.asEventStream = (eventName, selector, eventTransformer) ->
   [eventTransformer, selector] = [selector, undefined] if isFunction(selector)
   withDescription(@selector or this, "asEventStream", eventName, Bacon.fromBinder (handler) =>
     @on(eventName, selector, handler)
@@ -276,7 +277,7 @@

   withDescription(Bacon, "retry", options, source().flatMapError (e) ->
     if isRetryable(e) and retries > 0
-      retry(error: e, retriesDone: maxRetries - retries)
+      retry({error: e, retriesDone: maxRetries - retries})
     else
       Bacon.once(new Error(e)))

@@ -1331,7 +1332,7 @@
   inspect: -> "Some(" + @value + ")"
   toString: -> @inspect()

-None =
+None = {
   getOrElse: (value) -> value
   filter: -> None
   map: -> None
@@ -1340,6 +1341,7 @@
   toArray: -> []
   inspect: -> "None"
   toString: -> @inspect()
+}

 DepCache = (->
   flatDeps = {}
@@ -1612,11 +1614,12 @@

 Bacon._ = _

-Bacon.scheduler =
+Bacon.scheduler = {
   setTimeout: (f,d) -> setTimeout(f,d)
   setInterval: (f, i) -> setInterval(f, i)
   clearInterval: (id) -> clearInterval(id)
   now: -> new Date().getTime()
+}

 if define? and define.amd?
   define [], -> Bacon


Display error modal when connection refused

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andresmatasuarez/bookofblackmetal.git/'


simple template registry

simple template registry

error: pathspec 'app/assets/javascripts/mtl/templates.coffee' did not match any file(s) known to git

--- app/assets/javascripts/mtl/templates.coffee
+++ app/assets/javascripts/mtl/templates.coffee
@@ -0,0 +1,4 @@
+@MTL.templates = {}
+@MTL.renderTemplate = (template, data) ->
+  MTL.templates[template] = _.template(MTL.templates[template]) unless _.isFunction(MTL.templates[template])
+  MTL.templates[template](data)


feat(core): taiga-front-next component loader directive

error: pathspec 'app/coffee/modules/base/load-element.coffee' did not match any file(s) known to git

--- app/coffee/modules/base/load-element.coffee
+++ app/coffee/modules/base/load-element.coffee
@@ -0,0 +1,43 @@
+###
+# Copyright (C) 2014-2018 Taiga Agile LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# File: modules/base/navurls.coffee
+###
+
+taiga = @.taiga
+
+# https://stackoverflow.com/questions/12371159/how-to-get-evaluated-attributes-inside-a-custom-directive
+# https://stackoverflow.com/questions/18637803/how-can-i-parse-an-attribute-directive-which-has-multiple-values
+# https://github.com/angular/angular.js/blob/master/src/ng/directive/ngClass.js#L146
+
+module = angular.module("taigaBase")
+
+LoadElementDirective = ($parse) ->
+    link = ($scope, $el, $attrs) ->
+        $scope.$watch $parse($attrs.tgLoadElement), (val) ->
+            if val
+                legacyObj =  $parse($attrs.tgLoadElement)($scope)
+
+                $el[0].component = legacyObj.component
+
+                if legacyObj.params
+                    $el[0].params = legacyObj.params
+
+    return {
+        link: link
+    }
+
+module.directive("tgLoadElement", ['$parse', LoadElementDirective])


can be closed, remove data-state

can be closed, remove data-state

--- components/email/client/editorial_signup.coffee
+++ components/email/client/editorial_signup.coffee
@@ -53,7 +53,6 @@
       @showEditorialCTA 'modal'

   setupAEArticlePage: ->
-    @outcome = 'modal'
     @ctaBarView = new CTABarView
       mode: 'editorial-signup'
       name: 'editorial-signup-dismissed'
@@ -94,18 +93,14 @@
       image: sd.EDITORIAL_CTA_BANNER_IMG
     @$('#articles-show, .articles-articles-page').waypoint (direction) =>
       if direction is 'down'
-        setTimeout((=> @$('.articles-es-cta--banner').attr('data-state', 'in').css('opacity', 1)), 2000)
+        setTimeout((=> @$('.articles-es-cta--banner').css('opacity', 1)), 2000)
     analyticsHooks.trigger('view:editorial-signup', type: 'modal' )

   hideEditorialCTA: (e) ->
     e?.preventDefault()
     cta = @$(e.target).closest('.articles-es-cta--banner')
-    @onDismiss()
-    cta.css('opacity', 0)
-    setTimeout((=>
-      cta.attr('data-state', 'out')
-      cta.hide()
-      ), 2000)
+    @onDismiss(e)
+    cta.fadeOut()

   setupCTAWaypoints: =>
     @$el.append @ctaBarView.render().$el
@@ -139,7 +134,7 @@
           @$('.articles-es-cta__social').fadeIn()
         @ctaBarView.close()
         # CTA Banner
-        @$('.articles-es-cta--banner').css('opacity', 0).attr('data-state', 'out')
+        @$('.articles-es-cta--banner').css('opacity', 0)
         analyticsHooks.trigger('submit:editorial-signup', type: @getSubmissionType(e), email: @email)

   getSubmissionType: (e)->
@@ -151,6 +146,6 @@
     else
       'article_popup'

-  onDismiss: ->
+  onDismiss: (e)->
     @ctaBarView.logDimissal()
-    analyticsHooks.trigger('dismiss:editorial-signup')
+    analyticsHooks.trigger('dismiss:editorial-signup', type: @getSubmissionType(e))


Removed unnecessary argument of run() method

...in simulator.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/mmotorny/motornyy-mk61.git/'


add useApplyAsync

add useApplyAsync

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -341,6 +341,8 @@
     $httpProvider.defaults.headers.get = {
         "X-Session-Id": taiga.sessionId
     }
+
+    $httpProvider.useApplyAsync(true)

     $tgEventsProvider.setSessionId(taiga.sessionId)


Add spec for token iterator regression

Add spec for token iterator regression

error: pathspec 'spec/token-iterator-spec.coffee' did not match any file(s) known to git

--- spec/token-iterator-spec.coffee
+++ spec/token-iterator-spec.coffee
@@ -0,0 +1,35 @@
+TextBuffer = require 'text-buffer'
+TokenizedBuffer = require '../src/tokenized-buffer'
+
+describe "TokenIterator", ->
+  it "correctly terminates scopes at the beginning of the line (regression)", ->
+    grammar = atom.grammars.createGrammar('test', {
+      'scopeName': 'text.broken'
+      'name': 'Broken grammar'
+      'patterns': [
+        {
+          'begin': 'start'
+          'end': '(?=end)'
+          'name': 'blue.broken'
+        }
+        {
+          'match': '.'
+          'name': 'yellow.broken'
+        }
+      ]
+    })
+
+    buffer = new TextBuffer(text: """
+      start x
+      end x
+      x
+    """)
+    tokenizedBuffer = new TokenizedBuffer({buffer})
+    tokenizedBuffer.setGrammar(grammar)
+
+    tokenIterator = tokenizedBuffer.tokenizedLineForRow(1).getTokenIterator()
+    tokenIterator.next()
+
+    expect(tokenIterator.getBufferStart()).toBe 0
+    expect(tokenIterator.getScopeEnds()).toEqual []
+    expect(tokenIterator.getScopeStarts()).toEqual ['text.broken', 'yellow.broken']


lint & bugfix

lint & bugfix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/clochix/cozy-emails.git/'


[gen] Trim useless lines

[gen] Trim useless lines

Prevents lines with just ";" occuring

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/demhydraz/cafe.git/'


Assume that if callback exists it is a function

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/stinsonga/atom.git/'


Archived projects should not be visible under potential tab

[#105025912]

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/netguru/people.git/'


Don't function call on null

Don't function call on null

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bmedx/modulestore.git/'


do not use 'since' GET parameter

do not use 'since' GET parameter

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/rremizov/github-notifier.git/'


NotificationCollection: debug print, include setPasswordAuthHeader


add ability to add global Palettes on Umbriel instantiation

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/aligerum/umbriel.git/'


feat(logging): don't log if env is test

feat(logging): don't log if env is test

--- src/middleware.coffee
+++ src/middleware.coffee
@@ -8,7 +8,7 @@
 app = baritone.app()
 distPath = app.get 'dist_path'

-app.use morgan(if app.get('env') is 'production' then 'combined' else 'dev')
+app.use morgan(if app.get('env') is 'production' then 'combined' else 'dev') if app.get('env') isnt 'test'
 app.use compression()
 app.use bodyParser.json()
 app.use bodyParser.urlencoded extended: true


fixed opportunity detail date

fixed opportunity detail date

--- src/coffeescripts/Detail.coffee
+++ src/coffeescripts/Detail.coffee
@@ -114,8 +114,9 @@
   refreshOpptyHeader: (json) ->
     opptyDetails = ""
     if json.CloseDate
-      date = new Date()
-      date.setTime Date.parse(json.CloseDate)
+      dates = json.CloseDate.split "-"
+      # month is 0 indexed (-1)
+      date = new Date(dates[0], dates[1] - 1, dates[2])
       opptyDetails += L.get("close_detail_label", date.toLocaleDateString())
     if json.CloseDate and json.StageName
       opptyDetails += " - "


See #213. Disable `Beautify Directory` command from menus

--- menus/atom-beautify.cson
+++ menus/atom-beautify.cson
@@ -7,9 +7,9 @@
   '.tree-view .file > .name': [
     {label: 'Beautify File', command: 'atom-beautify:beautify-file'}
   ]
-  '.tree-view .directory > .header > .name': [
-    {label: 'Beautify Directory', command: 'atom-beautify:beautify-directory'}
-  ]
+  # '.tree-view .directory > .header > .name': [
+  #   {label: 'Beautify Directory', command: 'atom-beautify:beautify-directory'}
+  # ]

 'menu': [
   {


comments

comments

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/lavelle/coursework.git/'


test_one_parameter

error: pathspec 'test/8.test.coffee' did not match any file(s) known to git

--- test/8.test.coffee
+++ test/8.test.coffee
@@ -0,0 +1,28 @@
+fairy = require('..').connect()
+queue = fairy.queue('TEST8')
+uuid  = require('node-uuid')
+should = require 'should'
+
+describe ["Enqueuer should receive progress notification"], ->
+
+  new_id = uuid.v4()
+  it "Should Clear the Queue First", ->
+    queue.regist (group, callback, progress) ->
+      group.should.equal(new_id)
+      progress('progress')
+      progress('progress')
+      callback(null, 'result')
+
+  it 'Should Enqueue Successfully', (done) ->
+    progressed = 0
+    enqueued = off
+    queue.enqueue new_id, (->
+      enqueued.should.equal(off)
+      enqueued = on
+    ), ((result) ->
+      result.should.equal('result')
+      progressed.should.equal(2)
+      done()
+    ), (progress) ->
+      progressed++
+      progress.should.equal('progress')


Array typecheck

Array typecheck

error: pathspec 'src/typechecks.coffee' did not match any file(s) known to git

--- src/typechecks.coffee
+++ src/typechecks.coffee
@@ -0,0 +1,2 @@
+exports.isArray = (val) ->
+  Object.prototype.toString.call(val) is '[object Array]'


Fixed notification content bug

Fixed notification content bug

--- src/modules/notification.coffee
+++ src/modules/notification.coffee
@@ -67,7 +67,7 @@
     notification_window.removeClass "show"
     setTimeout _hide, 500

-  _iconHtml = (icon, type, title, content) ->
+  _iconHtml = (icon, title, content) ->
     html = """#{icon}

                 #{title}


format string to number on paste event

format string to number on paste event

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/VoiSmart/Rocket.Chat.git/'


disable invite button before close tabbar


Reduce down the HUD's match count string code

--- pages/hud.coffee
+++ pages/hud.coffee
@@ -29,9 +29,8 @@
     countElement = document.getElementById "hud-match-count"
     return unless countElement? # Don't do anything if we're not in find mode.

-    plural = if matchCount == 1 then "" else "es"
     countText = if matchCount > 0
-      " (" + matchCount + " Match#{plural})"
+      " (#{matchCount} Match#{if matchCount == 1 then "" else "es"})"
     else
       " (No matches)"
     countElement.textContent = if showMatchText then countText else ""


More clean-up

More clean-up

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/TheVikingOwain/arcadecache.git/'


Update names.coffee

Update names.coffee

--- app/lib/world/names.coffee
+++ app/lib/world/names.coffee
@@ -126,6 +126,7 @@
     "Iyert"
     "Palt"
     "Snortt"
+    "Kog"
   ]
   "Ogre": [
     "Krogg"


Modify behavior or classes properties setters to "add" class.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/JoshuaToenyes/stout-client.git/'


working around discord bugs

working around discord bugs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Mikuia/Mikuia.git/'


loging

loging

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/stormpack-core/stormpack-client-js.git/'


Allow items to be deleted from cart

Allow items to be deleted from cart

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/MicrosmartSA/ubiquity-ui.git/'


having div here is invalid `li` can only be in a list

--- src/packages/tree-view/src/tree-view.coffee
+++ src/packages/tree-view/src/tree-view.coffee
@@ -27,7 +27,7 @@
     @instance.serialize()

   @content: (rootView) ->
-    @div class: 'tree-view tool-panel', tabindex: -1
+    @ol class: 'tree-view tool-panel', tabindex: -1

   @deserialize: (state, rootView) ->
     treeView = new TreeView(rootView)


Add joinmessages handler

Add joinmessages handler

Currently only !quote and !setquote are supported.

error: pathspec 'handlers/joinmessages.coffee' did not match any file(s) known to git

--- handlers/joinmessages.coffee
+++ handlers/joinmessages.coffee
@@ -0,0 +1,111 @@
+###
+# Join messages for Tims Chat 3
+#
+# This handler provides simple join messages.
+#
+# Chatbot for Tim's Chat 3
+# Copyright (C) 2011 - 2014 Tim Düsterhus
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+###
+
+common = require '../common'
+config = require '../config'
+handlers = require '../handlers'
+api = require '../api'
+async = require 'async'
+winston = require 'winston'
+debug = (require 'debug')('Chatbot:handlers:joinMessages')
+db = require '../db'
+{ __, __n } = require '../i18n'
+
+getQuery = setQuery = delQuery = null
+
+onLoad = (callback) ->
+	db.serialize ->
+		db.run "CREATE TABLE IF NOT EXISTS joinMessages (
+			userID INT(10),
+			value MEDIUMTEXT,
+			PRIMARY KEY(userID)
+		);"
+
+		getQuery = db.prepare "SELECT joinMessages.value, users.* FROM users LEFT JOIN joinMessages ON users.userID = joinMessages.userID WHERE users.lastUsername = ?"
+		setQuery = db.prepare "INSERT OR REPLACE INTO joinMessages (userID, value) VALUES (?, ?)"
+		delQuery = db.prepare "DELETE FROM joinMessages WHERE userID = ?"
+
+handleMessage = (message, callback) ->
+	# Don't match our own messages
+	if message.sender is config.userID
+		callback?()
+		return
+
+	if message.message[0] is '!'
+		text = message.message[1..].split /\s/
+		[ command, parameters ] = [ text.shift(), text.join ' ' ]
+
+		switch command
+			when 'quote'
+				[ username ] = parameters.split /,/
+				username = message.username unless username? and username
+
+				getQuery.get username, (err, row) ->
+					if err? or not row?.value?
+						debug "Error: #{err}"
+
+						if username is message.username
+							api.replyTo message, __("You currently don't have a quote set."), no, callback
+						else
+							api.replyTo message, __("User “%1$s” has no quote set.", username), no, callback
+					else
+						api.replyTo message, __("[%1$s] %2$s", username, row.value), yes, callback
+			when 'setquote'
+				setQuery.run message.sender, parameters, (err) ->
+					if err?
+						debug "Error"
+						api.replyTo message, __("User %1$s has no quote set.", row.users.lastUsername), no, callback
+					else
+						api.replyTo message, __("Your quote has been set to: “%1$s”", parameters), yes, callback
+			when 'delquote'
+				callback?()
+			when 'forcequote'
+				callback?()
+			when 'wipequote'
+				callback?()
+			else
+				callback?()
+	else
+		# Not our business
+		callback?()
+
+unload = (callback) ->
+	async.parallel [
+		(callback) -> getQuery.finalize -> getQuery = null; callback?()
+		(callback) -> setQuery.finalize -> setQuery = null; callback?()
+		(callback) -> delQuery.finalize -> delQuery = null; callback?()
+	], callback
+
+purge = (callback) ->
+	db.runHuppedQuery "DROP TABLE joinMessages;", (err) ->
+		if err?
+			debug err
+			callback? err
+		else
+			callback?()
+
+module.exports =
+	handleMessage: handleMessage
+	handleUser: (user, callback) -> callback?()
+	unload: unload
+	onLoad: onLoad
+	purge: purge


replace the file that magically went missing....

error: pathspec 'lineman/app/services/user_auth_service.coffee' did not match any file(s) known to git

--- lineman/app/services/user_auth_service.coffee
+++ lineman/app/services/user_auth_service.coffee
@@ -0,0 +1,5 @@
+angular.module('loomioApp').factory 'UserAuthService', (Records, $http) ->
+  new class UserAuthService
+    constructor: ->
+      # this line is really inital app setup but I'm not actually sure where that is.
+      @currentUser = undefined


correctly reset context for each image

correctly reset context for each image

--- static/coffee/renderers/glyph/image_rgba.coffee
+++ static/coffee/renderers/glyph/image_rgba.coffee
@@ -64,10 +64,12 @@
       y_offset = @sy[i]+@sh[i]/2

       ctx.translate(0, y_offset)
-      ctx.scale(1,-1)
+      ctx.scale(1, -1)
       ctx.translate(0, -y_offset)
       ctx.drawImage(@image_data[i], @sx[i]|0, @sy[i]|0, @sw[i], @sh[i])
-      ctx.scale(1,1)
+      ctx.translate(0, y_offset)
+      ctx.scale(1, -1)
+      ctx.translate(0, -y_offset)

     ctx.setImageSmoothingEnabled(old_smoothing)
     ctx.restore()


Fucking encodings, man

Fucking encodings, man

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -437,7 +437,7 @@
     resultWrapper = (err, res) ->
       return callback(err, res) if err

-      res = res.map (line) ->
+      res = res.toString('utf8').map (line) ->
         [_, who, what] = line.match(/^([^\s]+): (.+)$/)
         [filterName(who), filterText(what)]
       callback(err, res)


Comma and tweets

Comma and tweets

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -156,7 +156,7 @@
             "Bleep, Bloop, Bop:",
             "I have done the thing,",
             "Tada!",
-            "I pooped a little:"
+            "I pooped a little:",
           ]
           msg.reply "#{prefix} #{url} is now -> #{strip.info.image_url}"

@@ -167,6 +167,7 @@
             "Laughter, sorrow, nonsense",
             "#arrakis",
             "#dickbutt",
+            "I pooped a little:",
           ]
           Twitter.mediaTweet strip.info.image_jpeg, "#{prefix} - #{strip.info.url}", (err, tweet, url) =>
             sorry = msg.random ["sorry", "it's your fault"]


High five!

High five!

error: pathspec 'scripts/hi5.coffee' did not match any file(s) known to git

--- scripts/hi5.coffee
+++ scripts/hi5.coffee
@@ -0,0 +1,13 @@
+# High five!
+#
+# ^5 - high five!
+
+images = [
+  "http://i.imgur.com/PJFpN.gif"
+]
+
+module.exports = (robot) ->
+  robot.respond /(hi|high|\^) ?(five|5)/i, (msg) ->
+    msg.send msg.random images
+
+


Remove test filter "only" call from 'trait/use' tests.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/JoshuaToenyes/stout-core.git/'


Adding ability to send messages via bot

Adding ability to send messages via bot

error: pathspec 'scripts/say.coffee' did not match any file(s) known to git

--- scripts/say.coffee
+++ scripts/say.coffee
@@ -0,0 +1,16 @@
+# Description:
+#   Liona is just so awesome
+#
+# Commands:
+#   hubot <3
+
+whitelistedUsers = process.env.HUBOT_ALLOWED_USERS?.split(',') || []
+
+isWhitelisted = (user) -> whitelistedUsers.indexOf(user.name) isnt -1
+module.exports = (robot) ->
+  robot.respond /say to ([a-zA-Z-_\#]+) (.*)/, (msg) ->
+    if isWhitelisted(msg.message.user)
+      [room, message] = msg.match[1..2]
+      robot.messageRoom room, message
+    else
+      msg.reply "Denied because you #{msg.message.user.name} are not of list #{whitelistedUsers}"


Fix some test assertion comments.

Fix some test assertion comments.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/rfw/basilisk.git/'


a message to a channel should not be fed back to a subscriber on the same channel

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/supersonic.git/'


Fixed: tests for new date format

Fixed: tests for new date format

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/clochix/cozy-ical.git/'


Change naming

Change naming

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -639,7 +639,7 @@

     if inActiveSessions.length
       terminalSessions['Open All']  =
-        callback          : => @openAllSessions { machine, inActiveSessions }
+        callback          : => @openAllSessions { machine, sessions : inActiveSessions }

     if canTerminateSessions
       terminalSessions["Terminate all"] =
@@ -671,9 +671,9 @@

   openAllSessions: (params) ->

-    { machine, inActiveSessions } = params
-
-    for session in inActiveSessions
+    { machine, sessions } = params
+
+    for session in sessions
       @createTerminal { machine, session }


Add Elm component snippet

Add Elm component snippet

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/triforkse/atom-elm.git/'


specs for smart-overflow

specs for smart-overflow

error: pathspec 'test/components/smart-overflow.spec.coffee' did not match any file(s) known to git

--- test/components/smart-overflow.spec.coffee
+++ test/components/smart-overflow.spec.coffee
@@ -0,0 +1,23 @@
+React = require 'react'
+{Testing, expect, sinon, _} = require 'test/helpers'
+
+SmartOverflow = require 'components/smart-overflow'
+
+TestChildComponent = React.createClass
+  render: -> React.createElement('span', {}, 'i am a test')
+
+describe 'SmartOverflow Component', ->
+
+  beforeEach ->
+    @props =
+      className: 'testing'
+      children: React.createElement(TestChildComponent)
+
+  it 'renders with className', ->
+    Testing.renderComponent( SmartOverflow, props: @props ).then ({dom}) ->
+      expect(dom.classList.contains('testing')).to.be.true
+      expect(dom.classList.contains('openstax-smart-overflow')).to.be.true
+
+  it 'renders child components', ->
+    Testing.renderComponent( SmartOverflow, props: @props ).then ({dom}) ->
+      expect(dom.textContent).equal('i am a test')


Add logoutEventHandler to mobile bootstrap

--- src/mobile/components/layout/bootstrap.coffee
+++ src/mobile/components/layout/bootstrap.coffee
@@ -21,6 +21,8 @@
 Sentry = require("@sentry/browser")
 globalReactModules = require('../../../desktop/lib/global_react_modules.tsx')
 hydrateStitch = require('@artsy/stitch/dist/internal/hydrate').hydrate
+analyticsHooks = require('../../lib/analytics_hooks.coffee')
+mediator = require('../../../desktop/lib/mediator.coffee')

 module.exports = ->
   # Add the Gravity XAPP or access token to all ajax requests
@@ -42,6 +44,7 @@
   setupErrorReporting()
   syncAuth()
   checkForAfterSignUpAction()
+  mediator.on('auth:logout', logoutEventHandler)

   # Setup jQuery plugins
   require 'jquery-on-infinite-scroll'
@@ -107,3 +110,13 @@
       else
         $loginSignupBanner.css('display', 'block')
         $mainNav.css('position', 'relative')
+
+logoutEventHandler = ->
+  $.ajax
+    url: '/users/sign_out'
+    type: 'DELETE'
+    success: ->
+      analyticsHooks.trigger 'auth:logged-out'
+      window.location.reload()
+    error: (xhr, status, errorMessage) ->
+      # TODO: Error message handling


today in lol

today in lol

error: pathspec 'scripts/today-lol.coffee' did not match any file(s) known to git

--- scripts/today-lol.coffee
+++ scripts/today-lol.coffee
@@ -0,0 +1,36 @@
+# LoL puns
+#
+# today in lol - get a LoL pun
+#
+
+today = [
+  "I reported Maokai for having a high amount of leaves",
+  "Caitlyn wouldn't let me farm in lane so I reported her for harassment",
+  "I reported a Cho'Gath For intentional feeding",
+  "I reported a Brand for excessive flaming",
+  "I reported Leona for being disruptive",
+  "I reported an Irelia for refusing to surrender",
+  "I reported a Tryndamere For Negative Attitude",
+  "I reported a Blitzcrank For sexual harassment",
+  "I reported Rammus for refusing to communicate with the team",
+  "I reported Trundle for excessive trolling",
+  "I reported Orianna because she kept dropping The Ball",
+  "Annie reported me. ᶘ ᵒᴥᵒᶅ",
+  "I reported our Sona for failure to communicate",
+  "I reported Lee Sin, for having no map awareness",
+  "I reported Lux for using colorful language",
+  "I reported Anivia's Wall for assisting enemy team",
+  "I reported Lee Sin because he didn't read the Summoner's Code",
+  "Nasus was a total ****. Spent all game barking orders",
+  "I reported a Gragas for drunk gaming",
+  "I reported a Shaco for decieving me",
+  "I reported the AFK Teemo for not being on duty",
+  "I reported their Soraka for Assisting the Enemy Team",
+  "I reported Skarner for bugging me",
+  "I reported Wukong - wouldn't stop monkeying around",
+  "I reported Cho'Gath. He kept screaming at me"
+]
+
+module.exports = (robot) ->
+  robot.respond /today in lol/i, (msg) ->
+    msg.send "Today #{msg.random today}."


tabcat.task.finish() makes sure entire task can be uploaded

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/UCSFMemoryAndAging/tabcat.git/'


forgot to add eq builder

forgot to add eq builder

error: pathspec 'client/src/EquationBuilder.coffee' did not match any file(s) known to git

--- client/src/EquationBuilder.coffee
+++ client/src/EquationBuilder.coffee
@@ -0,0 +1,264 @@
+
+class EquationBuilder
+
+  # {Number} How many dice has the user currently put in the added-to-goal area
+  numberInGoal: 0
+
+  # {Number} How many dots/brackets are there
+  numberDots: 0
+
+  # {Number} Tells us whether we are in the middle of adding brackets. 0 = No,
+  # 1 = We have already clicked to add a left bracket
+  bracketClicks: 0
+
+  # {Number} This is the index from the list of dots of the left bracket waiting to be completed
+  leftBracketIndex: 0
+
+  # {Number} The jquery selector of the container where the equation is being built
+  containerSelector: ''
+
+
+  constructor: (@containerSelector) ->
+
+
+  ###*
+   * Add a dice to the end
+   * @param {String} html The html for the element
+   * @return {DomElement} The dice that we added
+  ###
+  addDiceToEnd: (html) ->
+    # If it's the first dice we just added, then we need to add the leftmost brackets dot
+    if(@numberInGoal == 0)
+      @addDot(true, @containerSelector)
+    else if($(@containerSelector + ' li.dot:nth-last-child(2)').attr('data-bracket') is 'right')
+      # If there was a ")." before it then change it to ")"
+      @numberDots--
+      $(@containerSelector + ' li.dot:last-child').remove()
+
+    # Add a dice and dot in pairs
+    theDice = $(html).appendTo(@containerSelector)
+    @addDot(true, @containerSelector)
+
+    # Then adjust the counters to say we added a new dice and dot
+    @numberInGoal++
+    @cleanUpBrackets()
+    return theDice
+
+  ###*
+   * Remove a dice from the container
+   * @param  {Number} index        The index of the brackets from the list of dice
+   * @return {Boolean|DomElement}  The element if it got removed else returns false.
+  ###
+  removeDiceByIndex: (index) ->
+    if(@hasCompletedBrackets())
+      @numberInGoal--
+      deletedElement = $(@containerSelector + ' li.dice[data-index='+index+']').clone()
+      $(@containerSelector + ' li.dice[data-index='+index+']').remove()
+      @cleanUpBrackets()
+      return deletedElement
+    else
+      return false # If we can't delete the dice because we haven't completed brackets
+
+
+  ###*
+   * See whether we are in the middle of adding brackets
+   * @return {Boolean} True iff we added a left bracket but didn't add corresponding right bracket.
+  ###
+  hasCompletedBrackets: () -> @bracketClicks == 0
+
+  # How many dice are in the container.
+  getNumberOfDice: () -> @numberInGoal
+
+
+  ###*
+   * Adds the a dot to the dom before or after an element. It adds the dotlistener click handler.
+   * @param  {boolean} appendPrepend If true, appends to element. False prepends to element.
+   * @param  {String} element The selector of the element to append/prepend to.
+  ###
+  addDot: (appendPrepend, element) ->
+    @numberDots++
+    theHtml = "

"
+    thisReference = this
+    # Either add a dot to the beginning or the end of element, then add the listener
+    if(appendPrepend)
+      $(theHtml).appendTo(element).bind 'click', (event) ->
+        thisReference.dotListener(this)
+    else
+      $(theHtml).prependTo(element).bind 'click', (event) ->
+        thisReference.dotListener(this)
+
+  ###*
+   * What to do when we click on a dot. This is called when we click on a dot/bracket.
+  ###
+  dotListener: (element) ->
+    # If it's a normal dot then change it to a bracket. If it's a bracket then we may need to delete a bracket pair.
+    if($(element).attr('data-bracket') is 'none') then @addBracket(element) else @removeBracket(element)
+
+
+  ###*
+   * Add brackets. We have clicked on a dot and so change it to a bracket.
+   * @param  {DomElement} element The bracket element we clicked on
+  ###
+  addBracket: (element) ->
+    thisReference = this
+    if(@bracketClicks == 0)
+      # When we are clicking to add the left bracket
+      @leftBracketIndex = $(element).index(@containerSelector + ' li.dot')
+      if(@leftBracketIndex < @numberDots-1)
+        # Change the dot to a left bracket
+        $(element).attr('data-bracket','left')
+
+        # Give the newly added left bracket a color
+        #$(element).css('color', '#FFFFFF')
+
+        # Now say that the next click will be for adding a left bracket
+        @bracketClicks = (@bracketClicks+1)%2
+
+        # An equation starting with a left bracket needs a dot before so we can add more brackets.
+        # We just added a left bracket to the start, so prepend a dot at the very start.
+        if(@leftBracketIndex == 0)
+          @leftBracketIndex++
+          @addDot(false, @containerSelector)
+
+    else if(@bracketClicks == 1)
+      # When we are clicking to add the right bracket (we have already clicked to add the left)
+      rightBracketIndex = $(element).index(@containerSelector + ' li.dot')
+
+      # If we clicked on a dot AFTER the place we clicked for the left bracket
+      if(@leftBracketIndex < rightBracketIndex)
+        # Change the dot to a right bracket
+        $(element).attr('data-bracket','right')
+
+        # Now say that the next click will be for adding a right bracket
+        @bracketClicks = (@bracketClicks+1)%2
+
+        @cleanUpBrackets()
+
+        # Now we may need to add an extra dot at the end, when we add a right bracket at the very end
+        if(rightBracketIndex == @numberDots-1)
+          @addDot(true, @containerSelector)
+
+
+
+  ###*
+   * Remove brackets. We have clicked on a dot and so change
+   * @param  {DomElement} element The bracket element we clicked on
+  ###
+  removeBracket: (element) ->
+    thisReference = this
+    # if we are have balanded brackets everywhere...
+    if(@bracketClicks == 0)
+      # If we clicked on a left bracket
+      if($(element).attr('data-bracket') is 'left')
+        nextDots = $(element).nextAll('li.dot')
+        counter = 1
+        matchingRightBracket = undefined
+        # Move from left to right until we find the matching right bracket
+        for dot in nextDots
+          if($(dot).attr('data-bracket') is 'left') then counter++
+          if($(dot).attr('data-bracket') is 'right') then counter--
+          if(counter == 0)
+            matchingRightBracket = dot
+            break
+        # Remove the bracket pairs. Change the pair to dots and call cleanup.
+        $(element).attr('data-bracket','none')
+        $(matchingRightBracket).attr('data-bracket','none')
+        @cleanUpBrackets()
+      # If we clicked on a right bracket
+      else if($(element).attr('data-bracket') is 'right')
+        prevDots = $(element).prevAll('li.dot')
+        counter = 1
+        matchingLeftBracket = undefined
+        # Move from right to to until we find the matching left bracket
+        for dot in prevDots
+          if($(dot).attr('data-bracket') is 'left') then counter--
+          if($(dot).attr('data-bracket') is 'right') then counter++
+          if(counter == 0)
+            matchingLeftBracket = dot
+            break
+        # Remove the bracket pairs. Change the pair to dots and call cleanup.
+        $(element).attr('data-bracket','none')
+        $(matchingLeftBracket).attr('data-bracket','none')
+        @cleanUpBrackets()
+    else if(@bracketClicks == 1)
+      # We added a new left bracket
+      if(not $(element).is("[data-bracketid]"))
+        $(element).attr('data-bracket','none')
+        @bracketClicks = 0
+        @cleanUpBrackets()
+
+
+  ###*
+   * Fix up all the bracketing. Removes redundant dots and calls pairUpBrackets.
+  ###
+  cleanUpBrackets: () ->
+    thisReference = this
+    $(@containerSelector + ' li').each (index, d) ->
+      leftBracketType = $(d).prev().attr('data-bracket')
+      rightBracketType = $(d).attr('data-bracket')
+
+      twoDotsCase = (leftBracketType is 'none') and (rightBracketType is 'none')
+      leftBrkDotCase = (leftBracketType is 'left') and (rightBracketType is 'none')
+      dotRightBrkCase = (leftBracketType is 'none') and (rightBracketType is 'right')
+      leftBkrRightBrkCase1 = (leftBracketType is 'left') and (rightBracketType is 'right')
+      leftBkrRightBrkCase2 = (leftBracketType is 'right') and (rightBracketType is 'left')
+
+      if rightBracketType is 'none' then $(d).css('color', '')
+
+      if (twoDotsCase || leftBrkDotCase || dotRightBrkCase || leftBkrRightBrkCase1 || leftBkrRightBrkCase2)
+        # Adjust the left dot accordingly (since we always delete the right ... see below)
+        if(leftBkrRightBrkCase1)
+          $(d).prev().attr('data-bracket', 'none')
+        else if(leftBkrRightBrkCase2)
+          $(d).prev().attr('data-bracket', 'none')
+        else if(leftBrkDotCase)
+          $(d).prev().attr('data-bracket', 'left')
+        else if(dotRightBrkCase)
+          $(d).prev().attr('data-bracket', 'right')
+
+        # Special case for when there is just one dot left
+        if(thisReference.numberDots == 2)
+          $(d).prev().remove()
+          thisReference.numberDots--
+
+        # Always remove the right dot/bracket
+        thisReference.numberDots--
+        $(d).remove()
+        # Now try and clean up again
+        thisReference.cleanUpBrackets()
+    @pairUpBrackets()
+
+  ###*
+   * Correctly matches up pairing brackets, gives the pair an id and the same colour
+  ###
+  pairUpBrackets: () ->
+    ref = this
+    bracketsCounter = 0
+    $(@containerSelector + ' li.dot').each (index, element) ->
+      if($(element).attr('data-bracket') is 'left')
+        counter = 1
+        matchingRightBracket = undefined
+        $(element).nextAll('li.dot').each (index, dot) ->
+          if($(dot).attr('data-bracket') is 'left') then counter++
+          if($(dot).attr('data-bracket') is 'right') then counter--
+          if(counter == 0)
+            matchingRightBracket = dot
+            return false
+
+        # For the given pair, give them the game color
+        leftBracketColor = ref.getNextBracketColor(bracketsCounter)
+        $(element).css('color', leftBracketColor)
+        $(matchingRightBracket).css('color', leftBracketColor)
+
+        # For the given pair, assign them a pairing identiefer using data-bracketid attribute
+        $(element).attr('data-bracketid', ref.bracketsCounter)
+        $(matchingRightBracket).attr('data-bracketid', ref.bracketsCounter)
+        bracketsCounter += 1
+
+  ###*
+   * Called by pairUpBrackets. Work out the nth brightness colour based on total number of pairs.
+   * @param  {Number} bracketsCounter The ith bracket number when matching pairs from left to right.
+  ###
+  getNextBracketColor: (bracketsCounter) ->
+    brightnessPercent = 80-((bracketsCounter*2)/@numberDots)*50
+    return "hsl(120, 50%, #{brightnessPercent}%)"


Use error field when available as message

--- src/install.coffee
+++ src/install.coffee
@@ -150,7 +150,7 @@
       if error?
         callback("Request for package information failed: #{error.message}")
       else if response.statusCode isnt 200
-        message = body.message ? body
+        message = body.message ? body.error ? body
         callback("Request for package information failed: #{message}")
       else
         if latestVersion = body.releases.latest


fleshed out local commands

fleshed out local commands

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/genericdave/noprob.git/'


space things out a bit

space things out a bit


got rid of todo comments

got rid of todo comments


Restore private activateConfig method to fix settings-view

--- src/package.coffee
+++ src/package.coffee
@@ -156,6 +156,9 @@
         @config.setSchema @name, {type: 'object', properties: @mainModule.config}
         return true
     false
+
+  # TODO: Remove. Settings view calls this method currently.
+  activateConfig: -> @registerConfigSchemaFromMainModule()

   activateStylesheets: ->
     return if @stylesheetsActivated


Add name with owner to publish failed message

Closes #69

--- src/publish.coffee
+++ src/publish.coffee
@@ -190,7 +190,7 @@
           else if response.statusCode isnt 201
             message = body.message ? body.error ? body
             process.stdout.write '\u2717\n'.red
-            callback("Registering package failed: #{message}")
+            callback("Registering package in #{repository} repository failed: #{message}")
           else
             process.stdout.write '\u2713\n'.green
             callback(null, true)


- Call register observers on init

- Call register observers on init

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Mochaleaf/ArrayPipeline.git/'


Fix derped username regex.

Fix derped username regex.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/damianb/crimson.git/'


Added appendData functionality, removed unnecessary files

error: pathspec 'lib/prometheus.coffee' did not match any file(s) known to git

--- lib/prometheus.coffee
+++ lib/prometheus.coffee
@@ -0,0 +1,405 @@
+valIdPairs = {}
+evenOdd = "even"
+appendData = false
+@Bindings =
+  init: ->
+    @mapDataValsToIds()
+    @bindEditables()
+  bindEditables: ->
+       $(".editable").bind("click", ->
+            input = $(this).find("input")
+            select = $(this).find("select")
+            if $(input)
+              $(input).show()
+              $(input).focus()
+              $(input).parent().find("label").hide()
+            if $(select)
+              $(select).show()
+              $(select).focus()
+              $(select).parent().find("label").hide()
+       )
+       inputs = $(".editable").find("input")
+       selects = $(".editable").find("select")
+       $(selects).bind("blur", ->
+            label = $(this).parent().find("label")
+            $(label).html($("#" + this.id + " option:selected").text())
+            $(this).hide()
+            $(label).show()
+       )
+       $(inputs).bind("blur", ->
+            label = $(this).parent().find("label")
+            $(label).html($(this).val())
+            $(this).hide()
+            $(label).show()
+       )
+
+  nullCheck: (val)->
+    if val == null
+      return ""
+    else
+      return val
+  mapDataValsToIds: ->
+     #TODO Optimize, Genericizea
+     pairs = {}
+     jQuery.each($("##{currentForm}").find("select"), (i, val) ->
+          if $(val).attr("data-val") != null
+              valIdPairs[$(val).attr("data-val")] = $(val).attr("id")
+
+     )
+     jQuery.each($("##{currentForm}").find("input"), (i, val) ->
+          if $(val).attr("data-val") != null
+              valIdPairs[$(val).attr("data-val")] = $(val).attr("id")
+
+     )
+
+  bindTables: (data, container) ->
+    row_count = 0
+    tables = $(container).find("table")
+    jQuery.each(tables, (s, ele) ->
+      success = 0
+      jQuery.each(data, (s, data) ->
+        undefinedCount = 0
+        obj = data
+        row_count = $(ele).find("tr").length
+        if $(ele).attr("data-src")
+          property = $(ele).attr("data-src").split(".")
+        if property
+          for i in [0..property.length - 1]
+            obj = obj[property[i]]
+        switch evenOdd
+          when "odd" then evenOdd = "even"
+          when "even" then evenOdd = "odd"
+          else evenOdd = "odd"
+        row = "
"
+        headers = $(ele).find("th")
+        jQuery.each(headers, (k, eles) ->
+           data_options = []
+           try
+              try
+                data_options = ($(eles).attr("data-options").split(" "))
+              catch ex
+                values = eles
+              try
+                property = $(eles).attr("data-val").split(".")
+              catch ex
+              unless property?
+                return null
+              nested = obj
+              for i in [0..property.length - 1]
+                  nested = nested[property[i]]
+              isNull = nested?
+              nested = "" unless isNull
+              undefinedCount++ unless isNull
+              if $(eles).attr("data-val")
+                  input_name = Bindings.getInputName(eles, property)
+                  input_id =  Bindings.getInputId(eles, property, row_count)
+                  input_name = "#{input_name}[#{row_count}]"
+                  input_type = Bindings.getInputType(eles)
+                  change = Bindings.getOnChange()
+                  classes = Bindings.setColumnClasses(data_options)
+                  cls = Bindings.getClasses(eles)
+                  row += ""
+                  row += "#{nested}"
+                  if input_type == "select"
+                    row += Bindings.addTableSelect(input_name, row_count, val, eles, property, data, nested)
+                  else
+                    row += Bindings.addTableInput(input_name, input_id, nested, property, change)
+              if $(eles).attr("data-href")
+                  row += Bindings.addTableLink(eles)
+           catch ex
+                row += Bindings.addEmptyColumn
+                undefinedCount++
+
+          )
+        row += "
"
+        if undefinedCount < headers.length
+          row_count++
+          if not success
+            if not appendData
+              $(ele).find("tbody").html("")
+              success = 1
+          $(ele).find("tbody").append(row)
+          $(ele).find("input").hide()
+          $(ele).find("select").hide()
+      )
+    )
+    Bindings.bindEditables()
+
+
+  getOnChange: (eles) ->
+    change = ""
+    try
+      if $(eles).attr("data-change")
+        change = $(eles).attr("data-change")
+      else
+        change = ""
+    catch ex
+    return change
+
+  getInputType: (eles) ->
+    input_type = undefined
+    try
+      if $(eles).attr("data-input")
+          input_type = $(eles).attr("data-input").toString()
+    catch ex
+      input_type = "text"
+    return input_type
+
+  getInputName: (eles, property) ->
+    if $(eles).attr("data-name")
+        input_name = $(eles).attr("data-name")
+    else if property.length > 1
+        input_name = property.join("_")
+    else
+        input_name = property.toString()
+
+  getInputId: (eles, property, row_count) ->
+    if $(eles).attr("data-id")
+        input_id = $(eles).attr("data-id")
+    else
+        input_id = property.join("_")
+        input_id = "#{input_id}_#{row_count}"
+
+  getId: (eles) ->
+    ele_id = ""
+    if $(eles).attr("data-id")
+      ele_id = $(eles).attr("data-id")
+    return ele_id
+
+  getClasses: (eles) ->
+    if $(eles).attr("data-class")
+      cls = $(eles).attr("data-class")
+    else
+      cls = ""
+    return cls
+
+  addTableLink: (eles) ->
+    row = ""
+    row += " "
+    row += "	"
+    return row
+
+  addTableInput: (name, id, nested, property, change) ->
+    unless nested?
+      return null
+    row  = "<input name='#{name}' id='#{id}' value='#{nested}' class='hidden #{property.join("_")} editable' onchange='#{change}'/>"
+    row += "	"
+    return row
+
+  addEmptyColumn: ->
+    row = "	"
+    return row
+
+  addTableSelect: (input_name, row_count, val, eles, property, data, nested) ->
+    row = ""
+    select_options = data
+    property = $(eles).attr("data-select").split(".")
+    unless property?
+      return null
+    for i in [0..property.length - 1]
+        select_options = select_options[property[i]]
+    input_id = property.join("_")
+    row += "<select id='#{input_id}_#{row_count}' name='#{input_name}' class='hidden'>"
+    jQuery.each($(select_options), (i, val)->
+      row += "<option value='#{val.value}'"
+      if val.text == nested
+        row += "selected='selected'"
+      row += ">#{val.text}</option>"
+    )
+    row += "</select>"
+    row += "	"
+    return row
+
+  setColumnClasses: (options) ->
+    classes = []
+    jQuery.each(options, (i, val) ->
+      if val == "inline-edit"
+        classes.push("editable")
+      else
+        return ""
+    )
+    return classes.join(" ")
+
+  bindData: (data, container) ->
+    @bindSources(data, container, false)
+    @bindValues(data, container, false)
+
+  appendData: (data, container) ->
+    @bindSources(data, container, true)
+    @bindValues(data, container, true)
+
+  bindSources: (data, container, append) ->
+    if append
+      appendData = append
+    else
+      appendData = false
+    if container == undefined
+      container = "body"
+    if data.constructor.toString().indexOf("Array") is -1
+      data = [data]
+    if(data)
+      @bindSelectSources(data, container)
+
+  bindValues: (data, container) ->
+     if container == undefined
+       container = "body"
+     if data.constructor.toString().indexOf("Array") is -1
+       data = [data]
+     if(data)
+       @bindTables(data, container)
+
+       @bindSelects(data, container)
+
+       @bindInputs(data, container)
+
+       @bindCheckboxes(data, container)
+
+       @bindLists(data, container)
+
+       @bindLabels(data, container)
+
+       @bindEditables()
+
+  getEditableInput: (ele, val, name) ->
+    input = "<input value='#{val}' name='#{name}'/>"
+
+  bindLists: (data, container) ->
+    lists = $(container).find("ul[data-val]")
+    jQuery.each(lists, (i, ele) ->
+            success = 0
+            list_count = $(ele).find("li").length
+            if $(ele).attr("data-val")
+              jQuery.each(data, (i, obj) ->
+                property = $(ele).attr("data-val").split(".")
+                try
+                  for i in [0..property.length - 1]
+                      obj = obj[property[i]]
+                  data_options = ""
+                  try
+                    data_options = ($(ele).attr("data-options").split(" "))
+                  catch ex
+                  classes = Bindings.setColumnClasses(data_options)
+                  input = ""
+                  if $(ele).attr("data-name")
+                    name = "#{$(ele).attr('data-name')}[#{list_count}]"
+                  else
+                    name = "#{property.join('_')}[#{list_count}]"
+                  if classes.indexOf("editable") != -1
+                    input = Bindings.getEditableInput(ele, obj, name)
+                  if not success
+                    if not appendData
+                      $(ele).html("")
+                      success = 1
+                  $(ele).append("
#{input}#{obj}
")
+                  list_count++
+                  $(ele).find("select").hide()
+                  $(ele).find("input").hide()
+                catch ex
+              )
+    )
+
+  bindLabels: (data, container) ->
+    labels = $(container).find("label[data-val]")
+    jQuery.each(labels, (i, ele) ->
+            jQuery.each(data, (i, obj) ->
+              if $(ele).attr("data-val")
+                property = $(ele).attr("data-val").split(".")
+                try
+                  data_options = ""
+                  for i in [0..property.length - 1]
+                    obj = obj[property[i]]
+                  try
+                    data_options = $(ele).attr("data-options").split(" ")
+                  catch ex
+                  classes = Bindings.setColumnClasses(data_options)
+                  input = ""
+
+                  if $(ele).attr("data-name")
+                    name = $(ele).attr("data-name")
+                  else
+                    name = property.join("_")
+                  if classes.indexOf("editable") != -1
+                    input = Bindings.getEditableInput(ele, obj, name)
+                  $(ele).html("#{obj} #{input}")
+                  $(ele).find("input").hide()
+                catch ex
+            )
+    )
+
+  bindSelectSources: (data, container) ->
+    selects = $(container).find("select[data-val]")
+    jQuery.each(selects, (i, ele) ->
+            success = 0
+            obj = data
+            if $(ele).attr("data-src")
+              value = ""
+              objects = []
+              src = $(ele).attr("data-src").split(".")
+              jQuery.each(obj, (i, val) ->
+                val = val
+                jQuery.each(src, (i, str) ->
+                  try
+                    if i == src.length - 1
+                      value = val["id"]
+                    val = val[src[i]]
+                  catch ex
+                )
+                if val?
+                  if success == 0
+                    if not appendData
+                      $(ele).html("")
+                    success = 1
+                  $(ele).append("<option value='#{value}'>#{val}</option>")
+              )
+    )
+
+  bindSelects: (data, container) ->
+    selects = $(container).find("select[data-val]")
+    obj = data
+    jQuery.each(selects, (i, ele) ->
+      if $(ele).attr("data-val")
+        property = $(ele).attr("data-val").split(".")
+        jQuery.each(obj, (i, val) ->
+          try
+            for i in [0..property.length - 1]
+              val = val[property[i]]
+            if val?
+              $("##{$(ele).attr("id")} option:contains('#{val}')").attr("selected", "selected");
+          catch ex
+        )
+    )
+
+  bindInputs: (data, container) ->
+       inputs = $(container).find("input:text[data-val]")
+       jQuery.each(inputs, (i, ele) ->
+                   if $(ele).attr("data-val")
+                     jQuery.each(data, (i, obj) ->
+                       property = $(ele).attr("data-val").split(".")
+                       try
+                         for i in [0..property.length - 1]
+                               obj = obj[property[i]]
+                         $(ele).val(obj)
+                       catch ex
+                     )
+
+       )
+
+  bindCheckboxes: (data, container) ->
+       checkboxes = $(container).find("input:checkbox[data-val]")
+       jQuery.each(checkboxes, (i, ele) ->
+              if $(ele).attr("data-val")
+                jQuery.each(data, (i, obj) ->
+                  property = $(ele).attr("data-val").split(".")
+                  try
+                    for i in [0..property.length - 1]
+                      obj = obj[property[i]]
+                    $(ele).prop("checked", Bindings.getBoolean(obj))
+                  catch ex
+                )
+       )
+
+  getBoolean: (obj) ->
+    if obj is 0 or obj is "false" or obj is "" or obj is "0" or obj is null or obj is undefined
+      return false
+    else
+      return true


Stress test tool

Stress test tool

error: pathspec 'stress_test.coffee' did not match any file(s) known to git

--- stress_test.coffee
+++ stress_test.coffee
@@ -0,0 +1,30 @@
+io = require('socket.io-client')
+
+url = process.env.TEST_TARGET || 'https://snap-backend-dev.herokuapp.com'
+
+
+randWord = (len) ->
+  Math.random().toString(36).substr(2, len)
+
+
+
+startPlayer = () ->
+  name = process.env.TEST_NAME || 'John Smith'
+
+  socket = io(url)
+
+
+  socket.on 'connect', ()->
+    setTimeout () ->
+      socket.emit 'new player', name
+
+      console.log "connected a test player #{name}"
+
+      setInterval () ->
+        socket.emit 'new word', randWord(2)
+      ,1000 + Math.random()*500
+    ,Math.random()*2000
+
+for a in [0..70]
+  startPlayer()
+  # console.log randWord(2)


Fix support for trailing markdown in module documentation generator

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/nathan/lemma.git/'


Gruntfile

Gruntfile

error: pathspec 'Gruntfile.coffee' did not match any file(s) known to git

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -0,0 +1,34 @@
+module.exports = (grunt) ->
+  grunt.initConfig
+    pkg: grunt.file.readJSON('package.json')
+
+    coffee:
+      compile:
+        files:
+          'build/<%= pkg.name %>.js': 'src/<%= pkg.name %>.coffee'
+
+    uglify:
+      options:
+        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+      build:
+        files:
+          'build/<%= pkg.name %>.min.js': 'build/<%= pkg.name %>.js'
+
+    cafemocha:
+      test:
+        src: 'test/*.coffee'
+        options:
+          colors: true
+          ignoreLeaks: true
+          ui: 'bdd'
+          reporter: 'spec'
+          require: ['should']
+
+
+  grunt.loadNpmTasks 'grunt-contrib-uglify'
+
+  grunt.loadNpmTasks 'grunt-contrib-coffee'
+
+  grunt.loadNpmTasks 'grunt-cafe-mocha'
+
+  grunt.registerTask 'default', ['cafemocha', 'coffee', 'uglify']


Add in a test, rescale preview images

Add in a test, rescale preview images

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bmedx/modulestore.git/'


Update configuration

Update configuration

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ehmicky/myos.git/'


Update configuration

Update configuration