zh-HANT.coffee update (#4092)

zh-HANT.coffee update (#4092)

* Update zh-HANT.coffee

to line 1225

* Update zh-HANT.coffee

* Update zh-HANT.coffee

* Update zh-HANT.coffee

* Update zh-HANT.coffee

--- app/locale/zh-HANT.coffee
+++ app/locale/zh-HANT.coffee
@@ -9,7 +9,7 @@
     im_a_teacher: "我是老師"
     im_a_student: "我是學生"
     learn_more: "了解更多"
-    classroom_in_a_box: "在一個盒子中的電腦科學教室。"
+    classroom_in_a_box: "教授電腦科學的迷你教室。"
     codecombat_is: "CodeCombat是一個讓學生透過進行遊戲來學習計算機科學的平台。"
     our_courses: "我們的課程經過特別的遊戲測試來超越教室教學，甚至是超越一些先前沒有編程經驗的老師。"
     top_screenshots_hint: "學生們寫程式碼並且隨時觀看他們的更新。"
@@ -45,12 +45,12 @@
     request_demo_title: "讓您的學生從今天開始遊玩吧!"
     request_demo_subtitle: "申請一個demo示範版本，並且讓您的學生在一個小時之內就能上手。"
     get_started_title: "現在就設立您的班級。"
-    get_started_subtitle: "設立一個班級、加入您的學生，並在他們學習電腦科學的過程中掌握他們的進度。"
+    get_started_subtitle: "設立一個班級、加入您的學生，並在他們學習計算機科學的過程中掌握他們的進度。"
     request_demo: "申請一個demo示範版本"
     setup_a_class: "設立一個班級"
     have_an_account: "您是否擁有一個帳號?"
     logged_in_as: "您現在登入的身分為"
-    computer_science: "全年齡向的電腦科學課程"
+    computer_science: "全年齡向的計算機科學課程"
     show_me_lesson_time: "顯示課程預估時間："
     curriculum: "課程總共時數："
     ffa: "學生免費"
@@ -59,11 +59,11 @@
     courses_available_in: "包含JavaScript、Python，以及網頁開發HTML、CSS、jQuery、和Bootstrap的課程。"
     boast: "擁有足夠複雜的關卡，同時能吸引玩家以及程式撰寫者。"
     winning: "成功的結合RPG遊戲和程式設計作業，造就了對孩子有善且能樂在其中的教育環境。"
-    run_class: "如今想要在學校進行一堂電腦科學的課程，您不再需要有電腦科學的背景。"
+    run_class: "如今想要在學校進行一堂計算機科學的課程，您不再需要有計算機科學的背景。"
     goto_classes: "進入我的班級"
     view_profile: "瀏覽我的帳號"
     view_progress: "瀏覽進度"
-    go_to_courses: "進入課堂"
+    go_to_courses: "進入課程"
     want_coco: "想要在您的學校進行CodeCombat嗎？"

   nav:
@@ -82,8 +82,8 @@
     about: "關於"
     contact: "聯繫我們"
     twitter_follow: "在 Twitter 關注"
-#    my_classrooms: "My Classes"
-#    my_courses: "My Courses"
+    my_classrooms: "我的班級"
+    my_courses: "我的課程"
     careers: "招聘"
     facebook: "Facebook臉書"
     twitter: "Twitter推特"
@@ -126,7 +126,7 @@
     unlock: "解鎖" # For purchasing items and heroes
     confirm: "確認"
     owned: "擁有的物品" # For items you own
-    locked: "未解鎖裝備"
+    locked: "未解鎖" #同時用在裝備以及關卡
     purchasable: "可購買" # For a hero you unlocked but haven't purchased
     available: "可用"
     skills_granted: "將習得技能" # Property documentation details
@@ -201,11 +201,11 @@

   share_progress_modal:
     blurb: "您正在創造偉大的旅程！告訴您的家長，您從CodeCombat學到了什麼！" # {change}
-    email_invalid: "郵件地址無效"
-    form_blurb: "在底下輸入家長的郵件，讓我們展示給他們！"
-    form_label: "郵件地址"
-    placeholder: "郵件地址"
-    title: "出色的作品，學徒"
+    email_invalid: "Email地址無效"
+    form_blurb: "在底下輸入家長的Email，讓我們秀給他們看！"
+    form_label: "Email地址"
+    placeholder: "Email地址"
+    title: "做得不錯，同學"

   login:
     sign_up: "註冊"
@@ -241,7 +241,7 @@
     connected_facebook_header: "您已經成功與FB臉書連結!"
     connected_facebook_p: "完成註冊以使用FB臉書帳號登入"
     facebook_exists: "您已經有一個與FB臉書連結的帳號!"
-    hey_students: "學生們，請輸入老師所公佈的班級代碼"
+    hey_students: "同學們，請輸入老師所公佈的班級代碼"
     birthday: "生日"
     parent_email_blurb: "我們知道您已經迫不及待的想去學習編程 — 我們也同樣地感到興奮! 您的家長將會收到一封電子郵件，內容包含了如何為您更進一步的申請一個帳號。 如果您有任何問題，我們很高興收到您的詢問email。Email地址 {{email_link}} "
     classroom_not_found: "這個班級代碼是無效的，請檢察您的拼字或是尋求老師的協助。"
@@ -249,7 +249,7 @@
     account_exists: "此email帳號已經被使用過了："
     sign_in: "登入"
     email_good: "可用Email"
-    name_taken: "此名稱已被註冊！試試{{suggestedName}}?"
+    name_taken: "此名稱已被註冊！試試{{suggestedName}}吧?"
     name_available: "此名稱可用！"
     name_is_email: "使用者名稱不可以使用email格式"
     choose_type: "選擇帳戶類型："
@@ -264,16 +264,16 @@
     student_type_4: "選擇一個英雄！"
     student_type_5: "準備好您的班級代碼！"
     signup_as_student: "註冊成為學生"
-    individuals_or_parents: "私人帳戶＆家長"
-    individual_type: "家長們應該在此為每個於課外學習編程的玩家們申請一個帳號。"
-    signup_as_individual: "創建私人帳戶"
+    individuals_or_parents: "獨立帳戶＆家長"
+    individual_type: "對於想要在課外學習程式語言的學生，家長應該從這個地方註冊帳號。"
+    signup_as_individual: "創建獨立帳戶"
     enter_class_code: "輸入你的課程代碼"
     enter_birthdate: "輸入生日："
     parent_use_birthdate: "親愛的家長，請輸入您的生日"
-    ask_teacher_1: "向老師詢問課程代碼為何"
-    ask_teacher_2: "還不是課堂的一份子？創建一個"
-    ask_teacher_3: "私人帳號"
-    ask_teacher_4: " 而非反過來。"
+    ask_teacher_1: "向老師詢問課程代碼是甚麼"
+    ask_teacher_2: "不是班級的成員嗎？建立一個"
+    ask_teacher_3: "獨立帳號"
+    ask_teacher_4: "吧。"
     about_to_join: "您將會加入："
     enter_parent_email: "輸入家長的email："
     parent_email_error: "系統嘗試寄信時發生錯誤，請確認您的email並再次發送。"
@@ -301,13 +301,13 @@
     books: "書籍"

   common:
-    back: "向後瀏覽" # When used as an action verb, like "Navigate backward"
+    back: "上一步" # When used as an action verb, like "Navigate backward"
     coming_soon: "即將公布！"
-    continue: "繼續前進" # When used as an action verb, like "Continue forward"
+    continue: "下一步" # When used as an action verb, like "Continue forward"
     default_code: "預設程式碼"
     loading: "載入中…"
     overview: "總覽"
-    solution: "解決方案"
+    solution: "解決方法"
     intro: "簡介"
     saving: "儲存中…"
     sending: "發送中…"
@@ -318,13 +318,13 @@
     publish: "發佈"
     create: "創造"
     fork: "分支"
-    play: "進入戰役" # When used as an action verb, like "Play next level"
+    play: "進入關卡" # When used as an action verb, like "Play next level"
     retry: "重試"
     actions: "行為"
     info: "介紹"
     help: "求助"
-    watch: "關注"
-    unwatch: "取消關注"
+    watch: "關看"
+    unwatch: "取消關看"
     submit_patch: "送出補丁"
     submit_changes: "送出修改"
     save_changes: "保存更改"
@@ -382,11 +382,11 @@
     wizard: "巫師"
     first_name: "名"
     last_name: "姓"
-#    last_initial: "Last Initial"
+#    last_initial: "Last Initial" 中文姓名較少使用這個
     username: "使用者名稱"
-#    contact_us: "Contact Us"
-#    close_window: "Close Window"
-#    learn_more: "Learn More"
+    contact_us: "與我們聯繫"
+    close_window: "關閉視窗"
+    learn_more: "了解更多"

   units:
     second: "秒"
@@ -405,11 +405,11 @@
     years: "年"

   play_level:
-#    directions: "Directions"
-#    edit_level: "Edit Level"
-#    explore_codecombat: "Explore CodeCombat"
-#    finished_hoc: "I'm finished with my Hour of Code"
-#    get_certificate: "Get your certificate!"
+    directions: "目標方向"
+    edit_level: "編輯關卡"
+    explore_codecombat: "瀏覽 CodeCombat"
+    finished_hoc: "我已經完成了我的編程時光"
+    get_certificate: "取得您的證書!"
     level_complete: "關卡完成"
     completed_level: "完成關卡："
     course: "課程："
@@ -437,7 +437,7 @@
     reload_title: "重新載入程式碼？"
     reload_really: "確定重設所有的程式碼？"
     reload_confirm: "重設所有程式碼"
-#    test_level: "Test Level"
+    test_level: "測試關卡"
     victory: "勝利"
     victory_title_prefix: ""
     victory_title_suffix: "完成"
@@ -450,7 +450,7 @@
     victory_review: "給我們回饋！"
     victory_review_placeholder: "關卡如何？"
     victory_hour_of_code_done: "您完成了嗎？"
-    victory_hour_of_code_done_yes: "是的，我完成了我的程式碼！"
+    victory_hour_of_code_done_yes: "是的，我完成了我的編程時光！"
     victory_experience_gained: "取得經驗值"
     victory_gems_gained: "取得寶石"
     victory_new_item: "新的物品"
@@ -575,17 +575,17 @@
     choose_hero_caption: "選擇英雄，語言"
     save_load_caption: "…觀看歷史紀錄"
     options_caption: "設置設定"
-    guide_caption: "文件與小撇步"
+    guide_caption: "文件與小提示"
     multiplayer_caption: "跟朋友一起玩！"
     auth_caption: "儲存進度"

   leaderboard:
     view_other_solutions: "查看其他解法"
     scores: "分數"
-    top_players: "頂級玩家由"
+    top_players: "最高玩家在"
     day: "今天"
     week: "這週"
-    all: "長期以來"
+    all: "全部紀錄"
     time: "時間"
     damage_taken: "遭受的傷害"
     damage_dealt: "造成的傷害"
@@ -713,7 +713,7 @@
     health_2: "例表"
     health_3: "裝備血量。"
     speed_1: "移動速度"
-    speed_2: "公尺每秒。"
+    speed_2: "m/s。"
     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: "特定英雄才可遊玩此關卡。"
@@ -725,11 +725,11 @@
     number: "數字"
     array: "陣列"
     object: "物件"
-    string: "串連"
+    string: "字串"
     writable: "可編輯" # Hover over "attack" in Your Skills while playing a level to see most of this
     read_only: "唯讀"
     action: "命令"
-    spell: "技能"
+    spell: "法術"
     action_name: "名稱"
     action_cooldown: "釋放時間"
     action_specific_cooldown: "冷卻時間"
@@ -802,7 +802,7 @@
     lisa_title: "校園專家"
     lisa_blurb: "堅毅不拔的人"
     sean_title: "區域經理"
-#    liz_title: "Territory Manager"
+    liz_title: "區域經理"
     retrostyle_title: "插畫師"
     retrostyle_blurb: "復古風格的遊戲"
     jose_title: "音樂"
@@ -978,10 +978,10 @@
     faq: "FAQ"
     subscribe_prefix: "如果您需要幫助來解決關卡，請"
     subscribe: "訂閱CodeCombat"
-    subscribe_suffix: "並且我們樂意提供程式碼相關的協助。"
-    subscriber_support: "您已經是個CodeCombat訂閱者，我們將提供優先的協助。"
+    subscribe_suffix: "我們樂意提供程式碼相關的協助。"
+    subscriber_support: "如果您是CodeCombat訂閱者，您的Email將得到我們優先的協助。"
     screenshot_included: "包含螢幕截圖。"
-    where_reply: "我們回覆到？"
+    where_reply: "我們應該回覆到哪裡呢？"
     send: "意見反饋"

   account_settings:
@@ -1003,12 +1003,12 @@
     manage_subscription: "點此管理您的訂閱"
     new_password: "新密碼"
     new_password_verify: "確認密碼"
-    type_in_email: "輸入您的Email來確認刪除" # {change}
+    type_in_email: "輸入您的Email或使用者名程來確認帳號刪除"
     type_in_email_progress: "輸入您的Email，確認您真的要刪除進度"
     type_in_password: "還有輸入您的密碼。"
-    email_subscriptions: "訂閱"
+    email_subscriptions: "Email訂閱"
     email_subscriptions_none: "無Email訂閱"
-    email_announcements: "通知"
+    email_announcements: "公告"
     email_announcements_description: "接收關於CodeCombat的新聞和開發消息。"
     email_notifications: "通知"
     email_notifications_summary: "您在CodeCombat的活動，會透過Email自動通知，您可選擇開啟或關閉。"
@@ -1016,7 +1016,7 @@
     email_any_notes_description: "停用後您將不會收到來自CodeCombat的任何通知。"
     email_news: "新聞"
     email_recruit_notes: "工作機會"
-    email_recruit_notes_description: "如果您玩得不錯，我們或許會考慮給您一份工作。"
+    email_recruit_notes_description: "如果您玩得不錯，我們或許會考慮給您一份(更好的)工作。"
     contributor_emails: "貢獻者電郵"
     contribute_prefix: "我們在尋找志同道合的人！請到 "
     contribute_page: "貢獻頁面"
@@ -1077,24 +1077,24 @@
     create_clan: "建立新的部落"
     private_preview: "預覽"
     private_clans: "私密部落"
-    public_clans: "公共部落"
+    public_clans: "公開部落"
     my_clans: "我的部落"
     clan_name: "部落名字"
     name: "名字"
     chieftain: "首領"
     edit_clan_name: "編輯部落的名稱"
-    edit_clan_description: "編輯部落的描述"
+    edit_clan_description: "編輯部落的敘述"
     edit_name: "編輯名稱"
-    edit_description: "編輯描述"
+    edit_description: "編輯敘述"
     private: "私密"
-    summary: "綜述"
+    summary: "合計"
     average_level: "平均等級"
     average_achievements: "平均成就"
     delete_clan: "刪除部落"
     leave_clan: "離開部落"
     join_clan: "加入部落"
     invite_1: "邀請："
-    invite_2: "*透過這個鏈接來邀請玩家加入部落。"
+    invite_2: "*透過這個連結來邀請玩家加入部落。"
     members: "成員"
     progress: "進度"
     not_started_1: "尚未開始"
@@ -1110,11 +1110,11 @@
     view_attempt: "點擊查閱解題的嘗試"
     latest_achievement: "最新成就"
     playtime: "遊戲時間"
-    last_played: "最後玩了"
+    last_played: "最後遊玩"
     leagues_explanation: "在部落裡與其他成員組成聯盟一起參加下面的多人競技場。"
     track_concepts1: "追蹤慨念"
-    track_concepts2a: "由每位學生學習"
-    track_concepts2b: "由每位成員學習"
+    track_concepts2a: "已經由每位學生學習"
+    track_concepts2b: "已經由每位成員學習"
     track_concepts3a: "查看每位同學達到的等級"
     track_concepts3b: "查看每位成員達到的等級"
     track_concepts4a: "查看你的學生"
@@ -1201,211 +1201,211 @@
     topics: "題目"
     hours_content: "內容時間："
     get_free: "取得免費課程！"
-#    enroll_paid: "Enroll Students in Paid Courses"
-#    you_have1: "You have"
-#    you_have2: "unused student licenses"
-#    use_one: "Use 1 student license for"
-#    use_multiple: "Use licenses for the following students:"
-#    already_enrolled: "already enrolled"
-#    licenses_remaining: "licenses remaining:"
-#    insufficient_enrollments: "insufficient student licenses"
-#    get_enrollments: "Get More Licenses"
-#    change_language: "Change Course Language"
-#    keep_using: "Keep Using"
-#    switch_to: "Switch To"
-#    greetings: "Greetings!"
-#    back_classrooms: "Back to my classrooms"
-#    back_courses: "Back to my courses"
-#    edit_details: "Edit class details"
-#    enrolled_courses: "enrolled in paid courses:"
-#    purchase_enrollments: "Purchase Student Licenses"
-#    remove_student: "remove student"
-#    assign: "Assign"
-#    to_assign: "to assign paid courses."
-#    student: "Student"
-#    teacher: "Teacher"
-#    complete: "Complete"
-#    none: "None"
-#    play_campaign_title: "Play the Campaign"
-#    play_campaign_description: "You’re ready to take the next step! Explore hundreds of challenging levels, learn advanced programming skills, and compete in multiplayer arenas!"
-#    create_account_title: "Create an Account"
-#    create_account_description: "Sign up for a FREE CodeCombat account and gain access to more levels, more programming skills, and more fun!"
-#    preview_campaign_title: "Preview Campaign"
-#    preview_campaign_description: "Take a sneak peek at all that CodeCombat has to offer before signing up for your FREE account."
-#    arena: "Arena"
-#    arena_soon_title: "Arena Coming Soon"
-#    arena_soon_description: "We are working on a multiplayer arena for classrooms at the end of"
-#    not_enrolled1: "Not enrolled"
-#    not_enrolled2: "Ask your teacher to enroll you in the next course."
-#    next_course: "Next Course"
-#    coming_soon1: "Coming soon"
-#    coming_soon2: "We are hard at work making more courses for you!"
-#    available_levels: "Available Levels"
-#    welcome_to_courses: "Adventurers, welcome to Courses!"
-#    ready_to_play: "Ready to play?"
-#    start_new_game: "Start New Game"
-#    play_now_learn_header: "Play now to learn"
-#    play_now_learn_1: "basic syntax to control your character"
-#    play_now_learn_2: "while loops to solve pesky puzzles"
-#    play_now_learn_3: "strings & variables to customize actions"
-#    play_now_learn_4: "how to defeat an ogre (important life skills!)"
-#    welcome_to_page: "My Student Dashboard"
-#    completed_hoc: "Amazing! You've completed the Hour of Code course!"
-#    ready_for_more_header: "Ready for more? Play the campaign mode!"
-#    ready_for_more_1: "Use gems to unlock new items!"
-#    ready_for_more_2: "Play through brand new worlds and challenges"
-#    ready_for_more_3: "Learn even more programming!"
-#    saved_games: "Saved Games"
-#    hoc: "Hour of Code"
-#    my_classes: "Current Classes"
-#    class_added: "Class successfully added!"
-#    view_levels: "view all levels in course"
-#    join_class: "Join A Class"
-#    join_class_2: "Join class"
-#    ask_teacher_for_code: "Ask your teacher if you have a CodeCombat class code! If so, enter it below:"
-#    enter_c_code: "<Enter Class Code>"
-#    join: "Join"
-#    joining: "Joining class"
-#    course_complete: "Course Complete"
-#    play_arena: "Play Arena"
-#    view_project: "View Project"
-#    start: "Start"
-#    last_level: "Last level played"
-#    welcome_to_hoc: "Adventurers, welcome to our Hour of Code!"
-#    logged_in_as: "Logged in as:"
+    enroll_paid: "招收學生到付費課程中"
+    you_have1: "您擁有"
+    you_have2: "個未使用的學生註冊碼"
+    use_one: "用1個學生註冊碼到"
+    use_multiple: "將註冊碼用在下列學生上:"
+    already_enrolled: "已經招收"
+    licenses_remaining: "註冊碼剩餘:"
+    insufficient_enrollments: "學生註冊碼不足"
+    get_enrollments: "取得更多註冊碼"
+    change_language: "更換班級使用的語言"
+    keep_using: "繼續使用"
+    switch_to: "切換至"
+    greetings: "您好!"
+    back_classrooms: "回到我的教室"
+    back_courses: "回到我的課程"
+    edit_details: "編輯班級資訊"
+    enrolled_courses: "已招收付費課程:"
+    purchase_enrollments: "購買學生註冊碼"
+    remove_student: "移除學生"
+    assign: "指派"
+    to_assign: "分配付費課程。"
+    student: "學生"
+    teacher: "老師"
+    complete: "完成"
+    none: "無"
+    play_campaign_title: "參加活動"
+    play_campaign_description: "你已經可以進行下個步驟! 探索上百個具有挑戰性的關卡，學習進階的編成技巧，並且在多人競技場中競賽!"
+    create_account_title: "建立一個帳號"
+    create_account_description: "註冊一個免費的CodeCombat帳號並且取得更多的關卡、更多的編成技巧，以及更多的樂趣!"
+    preview_campaign_title: "預覽活動"
+    preview_campaign_description: "在註冊您的免費帳號之前，搶先體驗CodeCombat提供的完整內容。"
+    arena: "競技場"
+    arena_soon_title: "即將開放的競技場"
+    arena_soon_description: "我們正在建立教室專用的多人競技場在最後的"
+    not_enrolled1: "未被招收"
+    not_enrolled2: "請求您的老師在下一個課程中招收您。"
+    next_course: "下個課程"
+    coming_soon1: "即將開放"
+    coming_soon2: "我們正在努力為您製作更多的課程!"
+    available_levels: "可進入的關卡"
+    welcome_to_courses: "冒險者們，歡迎來到課程之中!"
+    ready_to_play: "準備好開始遊玩了嗎?"
+    start_new_game: "開始一個新的遊戲"
+    play_now_learn_header: "馬上開始玩與學習"
+    play_now_learn_1: "控制您的角色的基本文法"
+    play_now_learn_2: "當您陷入討厭謎題無法解開的迴圈時"
+    play_now_learn_3: "用字串和變數來完成想要的動作"
+    play_now_learn_4: "如何去擊敗一個食人魔(重要的生存技巧!)"
+    welcome_to_page: "我的學生面板"
+    completed_hoc: "太令人驚訝了! 您已經完成了編程時光!"
+    ready_for_more_header: "想要更多的課程嗎? 那就進入競技模式吧!"
+    ready_for_more_1: "用寶石來解鎖新的物品!"
+    ready_for_more_2: "全新的世界以及全新的挑戰"
+    ready_for_more_3: "學習更多的程式語言!"
+    saved_games: "儲存遊戲"
+    hoc: "編程時光"
+    my_classes: "現在的教室"
+    class_added: "您已經成功的加入一個教室!"
+    view_levels: "觀看課程中的所有關卡"
+    join_class: "加入一個班級"
+    join_class_2: "加入班級"
+    ask_teacher_for_code: "詢問您的老師是否有一個您的CodeCombat教室代碼! 如果有的話，輸入在後方:"
+    enter_c_code: "<輸入班級代碼>"
+    join: "加入"
+    joining: "加入班級中"
+    course_complete: "課程完結"
+    play_arena: "進入競技場"
+    view_project: "觀看計畫"
+    start: "開始"
+    last_level: "最後遊玩的關卡"
+    welcome_to_hoc: "冒險者們，歡迎進入我們的編程時光!"
+    logged_in_as: "用以下身分登入:"
     not_you: "不是您嗎?"
-#    welcome_back: "Hi adventurer, welcome back!"
+    welcome_back: "Hi 冒險者，觀迎回來!"
     continue_playing: "繼續進行遊戲"
-#    more_options: "More options:"
-#    option1_header: "Invite Students by Email"
-#    option1_body: "Note: If your students do not have email addresses, they can enter your unique Class Code when creating a Student Account to make email addresses optional."
-#    thank_you_pref: "Thank you for your purchase! You can now assign"
-#    thank_you_suff: "more students to paid courses."
-#    return_to_class: "Return to classroom"
-#    return_to_course_man: "Return to course management."
-#    students_not_enrolled: "students not enrolled"
-#    total_all_classes: "Total Across All Classes"
-#    how_many_enrollments: "How many additional student licenses do you need?"
-#    each_student_access: "Each student in a class will get access to Courses 2-4 once they are enrolled in paid courses. You may assign each course to each student individually."
-#    purchase_now: "Purchase Now"
-#    enrollments: "licenses"
-#    remove_student1: "Remove Student"
-#    are_you_sure: "Are you sure you want to remove this student from this class?"
-#    remove_description1: "Student will lose access to this classroom and assigned classes. Progress and gameplay is NOT lost, and the student can be added back to the classroom at any time."
-#    remove_description2: "The activated paid license will not be returned."
-#    keep_student: "Keep Student"
-#    removing_user: "Removing user"
-#    to_join_ask: "To join a class, ask your teacher for an unlock code."
-#    join_this_class: "Join Class"
-#    enter_here: "<enter unlock code here>"
-#    successfully_joined: "Successfully joined"
-#    click_to_start: "Click here to start taking"
-#    my_courses: "My Courses"
-#    classroom: "Classroom"
-#    use_school_email: "use your school email if you have one"
-#    unique_name: "a unique name no one has chosen"
-#    pick_something: "pick something you can remember"
-#    class_code: "Class Code"
-#    optional_ask: "optional - ask your teacher to give you one!"
-#    optional_school: "optional - what school do you go to?"
+    more_options: "更多選項:"
+    option1_header: "用Email邀請學生"
+    option1_body: "注意: 如果您的學生沒有Email，他們可以在建立學生帳號時輸入您特別的班級代碼取代Email地址。"
+    thank_you_pref: "感謝您的購買! 您現在可以分配"
+    thank_you_suff: "更多學生到付費課程中。"
+    return_to_class: "回到教室"
+    return_to_course_man: "回到課程管理。"
+    students_not_enrolled: "學生們未被招收"
+    total_all_classes: "所有班級的總計"
+    how_many_enrollments: "您需要多少個額外的學生註冊碼呢?"
+    each_student_access: "一旦學生被招收到付費課程中，每個學生將會取得2至4個課程。你可以獨立的分配每個課程給每位學生。"
+    purchase_now: "現在購買"
+    enrollments: "註冊碼"
+    remove_student1: "移除學生"
+    are_you_sure: "您確定要將這個學生從班級中移除嗎?"
+    remove_description1: "學生將會失去進入這個教室和分配課程的資格。 但遊戲進度並不會失去，所以可以在重新加入教室的時候繼續進度。"
+    remove_description2: "已經啟動的付費註冊碼將不能退費。"
+    keep_student: "保留學生"
+    removing_user: "移除使用者中"
+    to_join_ask: "向您的老師請求一個解鎖碼已加入班級。"
+    join_this_class: "加入班級"
+    enter_here: "<在此輸入解鎖碼>"
+    successfully_joined: "成功的加入了"
+    click_to_start: "點擊這邊來開始"
+    my_courses: "我的課程"
+    classroom: "教室"
+    use_school_email: "如果擁有學校Email的話請使用"
+    unique_name: "一個沒有人使用的、獨一無二的名子"
+    pick_something: "選擇一個您可以記住的"
+    class_code: "班級代碼"
+    optional_ask: "選項 - 請您的老師給您一個!"
+    optional_school: "選項 - 您就讀哪一所學校?"
     start_playing: "開始遊戲"
-#    skip_this: "Skip this, I'll create an account later!"
-#    welcome: "Welcome"
-#    getting_started: "Getting Started with Courses"
-#    download_getting_started: "Download Getting Started Guide [PDF]"
-#    getting_started_1: "Create a new class by clicking the green 'Create New Class' button below."
-#    getting_started_2: "Once you've created a class, click the blue 'Add Students' button."
-#    getting_started_3: "You'll see student's progress below as they sign up and join your class."
-#    educator_wiki_pref: "Or check out our new"
-#    educator_wiki_mid: "educator wiki"
-#    educator_wiki_suff: "to browse the guide online."
-#    your_classes: "Your Classes"
-#    no_classes: "No classes yet!"
-#    create_new_class1: "create new class"
-#    available_courses: "Available Courses"
-#    unused_enrollments: "Unused licenses available:"
-#    students_access: "All students get access to Introduction to Computer Science for free. One license per student is required to assign them to paid CodeCombat courses. A single student does not need multiple licenses to access all paid courses."
-#    active_courses: "active courses"
-#    no_students: "No students yet!"
-#    add_students1: "add students"
-#    view_edit: "view/edit"
-#    students_enrolled: "students enrolled"
-#    students_assigned: "students assigned"
-#    length: "Length:"
-#    subtitle: "Review course overviews and levels" # Flat style redesign
-#    changelog: "View latest changes to course levels."
-#    select_language: "Select language"
-#    select_level: "Select level"
-#    play_level: "Play Level"
+    skip_this: "跳過這步，我將會晚一點建立帳號!"
+    welcome: "歡迎"
+    getting_started: "開始課程"
+    download_getting_started: "下載引導手冊 [PDF]"
+    getting_started_1: "點擊下方綠色的'建立新的班級'按鈕來建立一個新的班級。"
+    getting_started_2: "您建立班級之後，點擊藍色的'加入學生'按鈕。"
+    getting_started_3: "當學生註冊並加入您的班級，您將會在下方看到學生的進度。"
+    educator_wiki_pref: "或者查看我們新的"
+    educator_wiki_mid: "教育者 wiki"
+    educator_wiki_suff: "在線上瀏覽引導手冊。"
+    your_classes: "您的班級"
+    no_classes: "還沒有任何班級!"
+    create_new_class1: "建立新的班級"
+    available_courses: "可取得的課程"
+    unused_enrollments: "可使用的未啟動註冊碼:"
+    students_access: "所有學生可以取得免費的計算機科學介紹。每位學生(只)需要一個註冊碼來進入付費CodeCombat課程。"
+    active_courses: "啟動課程"
+    no_students: "還沒有加入任何學生!"
+    add_students1: "加入學生"
+    view_edit: "觀看/編輯"
+    students_enrolled: "已招收學生"
+    students_assigned: "已指派學生"
+    length: "長度:"
+    subtitle: "回顧課程的總覽以及關卡" # Flat style redesign
+    changelog: "觀看課程關卡的最後編輯。"
+    select_language: "選擇語言"
+    select_level: "選擇關卡"
+    play_level: "進入關卡"
     concepts_covered: "課目覆蓋"
-#    print_guide: "Print Guide (PDF)"
-#    view_guide_online: "Level Overviews and Solutions"
-#    grants_lifetime_access: "Grants access to all Courses."
-#    enrollment_credits_available: "Licenses Available:"
-#    language_select: "Select a language" # ClassroomSettingsModal
-#    language_cannot_change: "Language cannot be changed once students join a class."
-#    learn_p: "Learn Python"
-#    learn_j: "Learn JavaScript"
-#    avg_student_exp_label: "Average Student Programming Experience"
-#    avg_student_exp_desc: "This will help us understand how to pace courses better."
-#    avg_student_exp_select: "Select the best option"
-#    avg_student_exp_none: "No Experience - little to no experience"
-#    avg_student_exp_beginner: "Beginner - some exposure or block-based"
-#    avg_student_exp_intermediate: "Intermediate - some experience with typed code"
-#    avg_student_exp_advanced: "Advanced - extensive experience with typed code"
-#    avg_student_exp_varied: "Varied Levels of Experience"
-#    student_age_range_label: "Student Age Range"
-#    student_age_range_younger: "Younger than 6"
-#    student_age_range_older: "Older than 18"
-#    student_age_range_to: "to"
+    print_guide: "列印指導手冊 (PDF)"
+    view_guide_online: "關卡總覽以及解決方法"
+    grants_lifetime_access: "允許取得所有課程。"
+    enrollment_credits_available: "可用的註冊碼:"
+    language_select: "選擇一個語言" # ClassroomSettingsModal
+    language_cannot_change: "一旦學生加入班級後將無法更換語言。"
+    learn_p: "學習 Python"
+    learn_j: "學習 JavaScript"
+    avg_student_exp_label: "平均學生編程經驗"
+    avg_student_exp_desc: "這將會幫助我們了解如何更好的進行課程。"
+    avg_student_exp_select: "選擇最好的選項"
+    avg_student_exp_none: "無經驗 - 稍微有一點經驗"
+    avg_student_exp_beginner: "初新者 - 有些基礎或是了解"
+    avg_student_exp_intermediate: "中階 - 有些編程經驗"
+    avg_student_exp_advanced: "進階 - 較多編程經驗"
+    avg_student_exp_varied: "更換編程經驗等級"
+    student_age_range_label: "學生年齡範圍"
+    student_age_range_younger: "小於6歲"
+    student_age_range_older: "大於18歲"
+    student_age_range_to: "到"
     create_class: "創建課堂"
-#    class_name: "Class Name"
-#    teacher_account_restricted: "Your account is a teacher account and cannot access student content."
-#    account_restricted: "A student account is required to access this page."
-#    update_account_login_title: "Log in to update your account"
-#    update_account_title: "Your account needs attention!"
-#    update_account_blurb: "Before you can access your classes, choose how you want to use this account."
-#    update_account_current_type: "Current Account Type:"
-#    update_account_account_email: "Account Email/Username:"
-#    update_account_am_teacher: "I am a teacher"
-#    update_account_keep_access: "Keep access to classes I've created"
-#    update_account_teachers_can: "Teacher accounts can:"
-#    update_account_teachers_can1: "Create/manage/add classes"
-#    update_account_teachers_can2: "Assign/enroll students in courses"
-#    update_account_teachers_can3: "Unlock all course levels to try out"
-#    update_account_teachers_can4: "Access new teacher-only features as we release them"
-#    update_account_teachers_warning: "Warning: You will be removed from all classes that you have previously joined and will not be able to play as a student."
-#    update_account_remain_teacher: "Remain a Teacher"
-#    update_account_update_teacher: "Update to Teacher"
-#    update_account_am_student: "I am a student"
-#    update_account_remove_access: "Remove access to classes I have created"
-#    update_account_students_can: "Student accounts can:"
-#    update_account_students_can1: "Join classes"
-#    update_account_students_can2: "Play through courses as a student and track your own progress"
-#    update_account_students_can3: "Compete against classmates in arenas"
-#    update_account_students_can4: "Access new student-only features as we release them"
-#    update_account_students_warning: "Warning: You will not be able to manage any classes that you have previously created or create new classes."
-#    update_account_remain_student: "Remain a Student"
-#    update_account_update_student: "Update to Student"
-#    need_a_class_code: "You'll need a Class Code for the class you're joining:"
-#    update_account_not_sure: "Not sure which one to choose? Email"
-#    update_account_confirm_update_student: "Are you sure you want to update your account to a Student experience?"
-#    update_account_confirm_update_student2: "You will not be able to manage any classes that you have previously created or create new classes. Your previously created classes will be removed from CodeCombat and cannot be restored."
-#    instructor: "Instructor: "
-#    youve_been_invited_1: "You've been invited to join "
-#    youve_been_invited_2: ", where you'll learn "
-#    youve_been_invited_3: " with your classmates in CodeCombat."
-#    by_joining_1: "By joining "
-#    by_joining_2: "will be able to help reset your password if you forget or lose it. You can also verify your email address so that you can reset the password yourself!"
-#    sent_verification: "We've sent a verification email to:"
-#    you_can_edit: "You can edit your email address in "
-#    account_settings: "Account Settings"
-#    select_your_hero: "Select Your Hero"
-#    select_your_hero_description: "You can always change your hero by going to your Courses page and clicking \"Change Hero\""
-#    select_this_hero: "Select this Hero"
-#    current_hero: "Current Hero:"
-#    change_hero: "Change Hero"
-#    web_dev_language_transition: "All classes program in HTML / JavaScript for this course.  Classes that have been using Python will start with extra JavaScript intro levels to ease the transition.  Classes that are already using JavaScript will skip the intro levels."
+    class_name: "班級名稱"
+    teacher_account_restricted: "您的帳號是老師帳號，無法取得學生專用內容。"
+    account_restricted: "需要學生帳號才能取得此頁內容。"
+    update_account_login_title: "登入以升級您的帳號"
+    update_account_title: "您的帳號需要關注!"
+    update_account_blurb: "在您可以進入您的班級之前，選擇您想要怎麼使用這個帳號。"
+    update_account_current_type: "帳號現在類別:"
+    update_account_account_email: "帳號 Email/使用者名稱:"
+    update_account_am_teacher: "我是一個老師"
+    update_account_keep_access: "保有我所建立班級的存取資格"
+    update_account_teachers_can: "老師帳號可以:"
+    update_account_teachers_can1: "建立/管理/加入班級"
+    update_account_teachers_can2: "分配/招收 學生進入課程"
+    update_account_teachers_can3: "解鎖所有的課程關卡來使用"
+    update_account_teachers_can4: "當我們推出時取得新的老師限定功能"
+    update_account_teachers_warning: "警告: 您將從所有加入的班級中被移除並且將無法以學生身分遊玩。"
+    update_account_remain_teacher: "維持一個老師"
+    update_account_update_teacher: "轉換成為老師"
+    update_account_am_student: "我是一個學生"
+    update_account_remove_access: "移除我所建立班級的存取資格"
+    update_account_students_can: "學生帳號能:"
+    update_account_students_can1: "加入班級"
+    update_account_students_can2: "以學生身分在不同的課程中遊玩並且追蹤您自己的進度"
+    update_account_students_can3: "與同學們在競技場中競爭"
+    update_account_students_can4: "當我們推出時取得新的學生限定功能"
+    update_account_students_warning: "警告: 您且將無法管理任何您先前建立的班級或是建立新的班級。"
+    update_account_remain_student: "維持一個學生"
+    update_account_update_student: "轉換成為學生"
+    need_a_class_code: "您將需要一個班級代碼以進入已加入的班級:"
+    update_account_not_sure: "還沒確定要選擇哪一個嗎? Email"
+    update_account_confirm_update_student: "您確定想要轉換您的帳號成為學生體驗嗎?"
+    update_account_confirm_update_student2: "您將會無法管理任何您先前建立的班級或是建立新的班級。 您先前建立的班級將會從CodeCombat中移除並且無法復原。"
+    instructor: "介紹員: "
+    youve_been_invited_1: "您已經被邀請加入 "
+    youve_been_invited_2: "，您將學習的地方 "
+    youve_been_invited_3: " 與您在CodeCombat中的同學一起。"
+    by_joining_1: "藉由加入 "
+    by_joining_2: "將可以幫助您重設密碼，假如您忘記了。 您也可以由您的email驗證，並自行重設密碼!"
+    sent_verification: "我們已經寄出了一封驗證信至:"
+    you_can_edit: "您可以更改您的信箱在 "
+    account_settings: "帳號設定中"
+    select_your_hero: "選擇您的英雄"
+    select_your_hero_description: "您隨時可以在課程頁面中點擊\"更換英雄\"來更換您的英雄"
+    select_this_hero: "選擇這個英雄"
+    current_hero: "現在使用英雄:"
+    change_hero: "更換英雄"
+    web_dev_language_transition: "在這個課程中所有的班級都使用 HTML / JavaScript。 已經使用Python的班級將會從額外的JavaScript介紹關卡開始以銜接內容。 已經使用JavaScript的班級將會跳過介紹關卡。"

 #  teacher:
 #    assigning_course: "Assigning course"


Show proper complete number, closes #313

--- app/models/Archive.coffee
+++ app/models/Archive.coffee
@@ -49,7 +49,7 @@
     Math.ceil ((@stats.complete / @stats.total) * 100)

   recordsComplete: =>
-    @formatNumber Math.floor(@classification_count / 4)
+    @formatNumber @stats.complete

   total: =>
     @formatNumber @stats.total


fix for issue 'signature is not canonical' error message #509

--- app/js/controllers/blocks.coffee
+++ app/js/controllers/blocks.coffee
@@ -17,10 +17,6 @@
             # ignore the case of missing blocks before #1
             deferred.resolve(0)
             return deferred.promise
-
-    Blockchain.get_blocks_with_missed(10000, 10).then (blocks) =>
-        console.log "merged blocks"
-        console.log blocks

     refresh_blocks = ->
         BlockchainAPI.get_block_count().then (head_block) =>


Fix stage leaking into additionalGoals (#441)

--- app/lib/world/GoalManager.coffee
+++ app/lib/world/GoalManager.coffee
@@ -30,8 +30,9 @@
     @initThangTeams()
     @addGoal goal for goal in @initialGoals if @initialGoals
     if @options?.session and @options?.additionalGoals
+      additionalGoals = _.cloneDeep(@options.additionalGoals)
       capstoneStage = @options.capstoneStage || 1 # passed in from PlayLevelView
-      stages = _.filter(@options.additionalGoals, (ag) -> ag.stage <= capstoneStage and ag.stage > 0)
+      stages = _.filter(additionalGoals, (ag) -> ag.stage <= capstoneStage and ag.stage > 0)
       goals = _.map(stages, (stage) -> stage.goals.map((goal) ->
         goal.stage = stage.stage
         return goal


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -190,7 +190,7 @@
   engine.frameAdvance()

   equals object.I.x, 1
-  equals object
+  equals object.I.age 1

 test "#setState", 3, ->
   engine = Engine()


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -28,6 +28,6 @@
   fastEnemy.include(Follow)
   fastEnemy.follow(player)

-  ok fastEnemy.I.velocity.equal(Point(0, -5)), ''
+  ok fastEnemy.I.velocity.equal(Point(0, -5)), 'fastEne'

 module()


norm with optional value

norm with optional value

--- test/matrix.coffee
+++ test/matrix.coffee
@@ -161,6 +161,15 @@
     equals result.x, p1.x - p2.x
     equals result.y, p1.y - p2.y

+  test "#norm", ->
+    p = Point(2, 0)
+
+    normal = p.norm()
+    equals normal.x, 1
+
+    normal = p.norm(5)
+    equals normal.x, 5
+
   test "#scale", ->
     p1 = Point(5, 6)
     scalar = 2


add test case for #69

add test case for #69

--- test/player.coffee
+++ test/player.coffee
@@ -55,6 +55,16 @@
                 p.play(20000)
                 p.once 'timeupdate', (pos) ->
                     assert.ok pos >= 20000
+                    done()
+
+            p.setUrl(long_mp3).play(10000)
+
+        test 'startTime为0时可重头开始播放', (done) ->
+            p.once 'timeupdate', (pos) ->
+                assert.ok pos >= 10000
+                p.play(0)
+                p.once 'timeupdate', (pos) ->
+                    assert.ok 0 <= pos < 1000
                     done()

             p.setUrl(long_mp3).play(10000)


Remove PromiseRenderer from discussion preview

--- app/talk/discussion-preview.cjsx
+++ app/talk/discussion-preview.cjsx
@@ -1,7 +1,6 @@
 React = require 'react'
 {Link} = require 'react-router'
 resourceCount = require './lib/resource-count'
-PromiseRenderer = require '../components/promise-renderer'
 LatestCommentLink = require './latest-comment-link'
 Thumbnail = require '../components/thumbnail'
 apiClient = require 'panoptes-client/lib/api-client'
@@ -15,6 +14,22 @@

   contextTypes:
     geordi: React.PropTypes.object
+
+  getInitialState: ->
+    subject: null
+
+  componentDidMount: ->
+    @updateSubject @props.discussion
+
+  componentWillReceiveProps: (newProps) ->
+    @updateSubject newProps.discussion if newProps.discussion isnt @props.discussion
+
+  updateSubject: (discussion)->
+    if discussion.focus_id and discussion.focus_type is 'Subject'
+      apiClient.type 'subjects'
+        .get discussion.focus_id
+        .then (subject) =>
+          @setState {subject}

   logDiscussionClick: ->
     @context.geordi?.logEvent
@@ -45,13 +60,11 @@

-        {if discussion.focus_id and (discussion.focus_type is 'Subject')
+        {if @state.subject?

-            <PromiseRenderer catch={null} promise={apiClient.type('subjects').get(discussion.focus_id)}>{(subject) =>
-              <Thumbnail src={getSubjectLocation(subject).src} width={100} />
-            }</PromiseRenderer>
+            <Thumbnail src={getSubjectLocation(@state.subject).src} width={100} />

-          }
+        }

           { if discussion.sticky}


Use normal Contact logic for homepage contact link

--- app/views/HomeView.coffee
+++ app/views/HomeView.coffee
@@ -19,7 +19,7 @@
     'click .logout-btn': 'logoutAccount'
     'click .setup-class-btn': 'onClickSetupClass'
     'click .try-chapter-1': 'onClickGenericTryChapter1'
-    'click .contact-us': 'onClickContactUs'
+    'click .contact-us': 'onClickContactModal'
     'click a': 'onClickAnchor'
     'click button.press-engage': ()->@onCarouselDirectMove(0)
     'click button.press-explore': ()->@onCarouselDirectMove(1)
@@ -62,10 +62,6 @@
   onClickTeacherButton: (e) ->
     @homePageEvent('Started Signup')
     @openModalView(new CreateAccountModal({startOnPath: 'teacher'}))
-
-  onClickContactUs: (e) ->
-    DirectContactModal = require('ozaria/site/views/core/DirectContactModal').default
-    @openModalView(new DirectContactModal())

   # Provides a uniform interface for collecting information from the homepage.
   # Always provides the category Homepage and includes the user role.


Fixing Issue #622 with a light refactor to start using a 'tags' property on all nodes.

--- nodes.coffee
+++ nodes.coffee
@@ -28,6 +28,9 @@
 # being requested by the surrounding function), information about the current
 # scope, and indentation level.
 exports.BaseNode = class BaseNode
+
+  constructor: ->
+    @tags = {}

   # Common logic for determining whether to wrap this node in a closure before
   # compiling it, or to compile directly. We need to wrap if this node is a
@@ -42,9 +45,7 @@
   compile: (o) ->
     @options = merge o or {}
     @tab     = o.indent
-    unless this instanceof ValueNode or this instanceof CallNode
-      del @options, 'operation'
-      del @options, 'chainRoot' unless this instanceof AccessorNode or this instanceof IndexNode
+    del @options, 'chainRoot' unless this instanceof AccessorNode or this instanceof IndexNode
     top     = if @topSensitive() then @options.top else del @options, 'top'
     closure = @isStatement(o) and not @isPureStatement() and not top and
               not @options.asStatement and not (this instanceof CommentNode) and
@@ -157,6 +158,7 @@
   isStatement:  -> yes

   constructor: (nodes) ->
+    super()
     @expressions = compact flatten nodes or []

   # Tack an expression on to the end of this expression list.
@@ -239,6 +241,7 @@
   class: 'LiteralNode'

   constructor: (@value) ->
+    super()

   makeReturn: ->
     if @isStatement() then this else super()
@@ -269,6 +272,7 @@
   children:         ['expression']

   constructor: (@expression) ->
+    super()

   makeReturn: ->
     this
@@ -293,6 +297,7 @@

   # A **ValueNode** has a base and a list of property accesses.
   constructor: (@base, @properties) ->
+    super()
     @properties or= []

   # Add a property access to the list.
@@ -351,7 +356,7 @@
   # evaluate a anything twice when building the soak chain.
   compileNode: (o) ->
     only        = del o, 'onlyFirst'
-    op          = del o, 'operation'
+    op          = @tags.operation
     props       = if only then @properties[0...@properties.length - 1] else @properties
     o.chainRoot or= this
     @base.parenthetical = yes if @parenthetical and not props.length
@@ -388,6 +393,7 @@
   isStatement: -> yes

   constructor: (@lines) ->
+    super()

   makeReturn: ->
     this
@@ -406,6 +412,7 @@
   children: ['variable', 'args']

   constructor: (variable, @args) ->
+    super()
     @isNew    = false
     @isSuper  = variable is 'super'
     @variable = if @isSuper then null else variable
@@ -484,6 +491,7 @@
   children: ['child', 'parent']

   constructor: (@child, @parent) ->
+    super()

   # Hooks one constructor into another's prototype chain.
   compileNode: (o) ->
@@ -500,6 +508,7 @@
   children: ['name']

   constructor: (@name, tag) ->
+    super()
     @prototype = if tag is 'prototype' then '.prototype' else ''
     @soakNode = tag is 'soak'

@@ -518,6 +527,7 @@
   children: ['index']

   constructor: (@index) ->
+    super()

   compileNode: (o) ->
     o.chainRoot.wrapped or= @soakNode
@@ -536,6 +546,7 @@
   children: ['from', 'to']

   constructor: (@from, @to, exclusive) ->
+    super()
     @exclusive = !!exclusive
     @equals = if @exclusive then '' else '='

@@ -607,6 +618,7 @@
   children: ['range']

   constructor: (@range) ->
+    super()

   compileNode: (o) ->
     from     = @range.from.compile(o)
@@ -625,6 +637,7 @@
   topSensitive: -> true

   constructor: (props) ->
+    super()
     @objects = @properties = props or []

   compileNode: (o) ->
@@ -652,6 +665,7 @@
   children: ['objects']

   constructor: (@objects) ->
+    super()
     @objects or= []
     @compileSplatLiteral = (o) ->
       SplatNode.compileSplattedArray.call(this, @objects, o)
@@ -687,6 +701,7 @@
   # Initialize a **ClassNode** with its name, an optional superclass, and a
   # list of prototype property assignments.
   constructor: (@variable, @parent, @properties) ->
+    super()
     @properties or= []
     @returns    = false

@@ -758,6 +773,7 @@
   children: ['variable', 'value']

   constructor: (@variable, @value, @context) ->
+    super()

   topSensitive: ->
     true
@@ -857,6 +873,7 @@
   children: ['params', 'body']

   constructor: (@params, @body, tag) ->
+    super()
     @params or= []
     @body   or= new Expressions
     @bound  = tag is 'boundfunc'
@@ -927,6 +944,7 @@
   children: ['name']

   constructor: (@name, @attach, @splat) ->
+    super()
     @value = literal @name

   compileNode: (o) ->
@@ -945,6 +963,7 @@
   children: ['name']

   constructor: (name) ->
+    super()
     name = literal(name) unless name.compile
     @name = name

@@ -1009,6 +1028,7 @@
   isStatement: -> yes

   constructor: (condition, opts) ->
+    super()
     if opts and opts.invert
       condition = new ParentheticalNode condition if condition instanceof OpNode
       condition = new OpNode('!', condition)
@@ -1078,10 +1098,13 @@
   children: ['first', 'second']

   constructor: (@operator, @first, @second, flip) ->
+    super()
     @operator = @CONVERSIONS[@operator] or @operator
     @flip     = !!flip
     if @first instanceof ValueNode and @first.base instanceof ObjectNode
       @first = new ParentheticalNode @first
+    @first.tags.operation = yes
+    @second.tags.operation = yes if @second

   isUnary: ->
     not @second
@@ -1102,7 +1125,6 @@
     super(idt, @class + ' ' + @operator)

   compileNode: (o) ->
-    o.operation = true
     return @compileChain(o)      if @isChainable() and @first.unwrap() instanceof OpNode and @first.unwrap().isChainable()
     return @compileAssignment(o) if indexOf(@ASSIGNMENT, @operator) >= 0
     return @compileUnary(o)      if @isUnary()
@@ -1153,6 +1175,7 @@
   children: ['object', 'array']

   constructor: (@object, @array) ->
+    super()

   isArray: ->
     @array instanceof ValueNode and @array.isArray()
@@ -1182,6 +1205,7 @@
   isStatement:  -> yes

   constructor: (@attempt, @error, @recovery, @ensure) ->
+    super()

   makeReturn: ->
     @attempt  = @attempt.makeReturn() if @attempt
@@ -1209,6 +1233,7 @@
   isStatement: -> yes

   constructor: (@expression) ->
+    super()

   # A **ThrowNode** is already a return, of sorts...
   makeReturn: ->
@@ -1228,6 +1253,7 @@
   children: ['expression']

   constructor: (@expression) ->
+    super()

   compileNode: (o) ->
     test = ExistenceNode.compileTest(o, @expression)[0]
@@ -1253,6 +1279,7 @@
   children: ['expression']

   constructor: (@expression) ->
+    super()

   isStatement: (o) ->
     @expression.isStatement(o)
@@ -1287,6 +1314,7 @@
   isStatement: -> yes

   constructor: (@body, source, @name, @index) ->
+    super()
     @index  or= null
     @source = source.source
     @guard  = source.guard
@@ -1379,7 +1407,7 @@
   topSensitive: -> true

   constructor: (@condition, @body, @tags) ->
-    @tags      or= {}
+    @tags or= {}
     if @tags.invert
       if @condition instanceof OpNode and @condition.isInvertable()
         @condition.invert()
@@ -1472,10 +1500,12 @@

   # Compile the IfNode as a ternary operator.
   compileTernary: (o) ->
-    o.operation = true
+    @bodyNode().tags.operation = @condition.tags.operation = yes
+    @elseBodyNode().tags.operation = yes if @elseBody
     ifPart      = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
     elsePart    = if @elseBody then @elseBodyNode().compile(o) else 'null'
-    "#{ifPart} : #{elsePart}"
+    code        = "#{ifPart} : #{elsePart}"
+    if @tags.operation then "(#{code})" else code

 # Faux-Nodes
 # ----------


Fixes #2287 -- tweak extends to make jshint easier

--- nodes.coffee
+++ nodes.coffee
@@ -1949,7 +1949,7 @@
   # Correctly set up a prototype chain for inheritance, including a reference
   # to the superclass for `super()` calls, and copies of any static properties.
   extends: -> """
-    function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }
+    function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
   """

   # Create a function bound to the current value of "this".


Removed extra index variable left from code thrash

--- nodes.coffee
+++ nodes.coffee
@@ -1059,10 +1059,8 @@
     delete o.bare
     vars   = []
     exprs  = []
-    for param,index in @params when param.splat
-      for p in @params
-        o.scope.add p.name.value, 'var', yes if p.name.value
-
+    for param in @params when param.splat
+      o.scope.add p.name.value, 'var', yes for p in @params when p.name.value
       splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
                           new Value new Literal 'arguments'
       break


Fixing first part of #614 -- improperly cached existential operator, when first operand is a function call.

--- nodes.coffee
+++ nodes.coffee
@@ -1114,15 +1114,14 @@
     second = @second.compile o
     second = "(#{second})" if @second instanceof OpNode
     o.scope.find(first) if first.match(IDENTIFIER)
-    return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar)) } ? #{firstVar} : #{second}" if @operator is '?='
+    return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar))[0] } ? #{firstVar} : #{second}" if @operator is '?='
     "#{first} = #{firstVar} #{ @operator.substr(0, 2) } #{second}"

   # If this is an existence operator, we delegate to `ExistenceNode.compileTest`
   # to give us the safe references for the variables.
   compileExistence: (o) ->
-    [first, second] = [@first.compile(o), @second.compile(o)]
-    test = ExistenceNode.compileTest(o, @first)
-    "#{test} ? #{first} : #{second}"
+    [test, ref] = ExistenceNode.compileTest(o, @first)
+    "#{test} ? #{ref} : #{ @second.compile(o) }"

   # Compile a unary **OpNode**.
   compileUnary: (o) ->
@@ -1215,14 +1214,14 @@
   constructor: (@expression) ->

   compileNode: (o) ->
-    ExistenceNode.compileTest(o, @expression)
+    ExistenceNode.compileTest(o, @expression)[0]

   # The meat of the **ExistenceNode** is in this static `compileTest` method
   # because other nodes like to check the existence of their variables as well.
   # Be careful not to double-evaluate anything.
   @compileTest: (o, variable) ->
-    [first, second] = variable.compileReference o
-    "(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)"
+    [first, second] = variable.compileReference o, precompile: yes
+    ["(typeof #{first} !== \"undefined\" && #{second} !== null)", second]

 #### ParentheticalNode


removed extra lines from the compilation of trailing `then` with `if`/`switch`

--- nodes.coffee
+++ nodes.coffee
@@ -1367,7 +1367,7 @@
       for cond in flatten [conditions]
         cond  = cond.invert() unless @subject
         code += idt1 + "case #{ cond.compile o, LEVEL_PAREN }:\n"
-      code += block.compile(o, LEVEL_TOP) + '\n'
+      code += body + '\n' if body = block.compile o, LEVEL_TOP
       break if i is @cases.length - 1 and not @otherwise
       for expr in block.expressions by -1 when expr not instanceof Comment
         code += idt2 + 'break;\n' unless expr instanceof Return
@@ -1432,7 +1432,8 @@
     cond     = @condition.compile o, LEVEL_PAREN
     o.indent = @idt 1
     body     = @ensureExpressions(@body).compile o
-    ifPart   = "if (#{cond}) {\n#{body}\n#{@tab}}"
+    body     = "\n#{body}\n#{@tab}" if body
+    ifPart   = "if (#{cond}) {#{body}}"
     ifPart   = @tab + ifPart unless child
     return ifPart unless @elseBody
     ifPart + ' else ' + if @isChain


:poultry_leg::wheelchair: Updated in browser at strd6.github.io/editor

--- noise.coffee
+++ noise.coffee
@@ -1,3 +1,7 @@
+###
+Create a 2 second noise buffer
+###
+
 module.exports = (context) ->
   node = context.createBufferSource()
   frameCount = context.sampleRate * 2
@@ -13,6 +17,6 @@
   node.loop = true
   node.start(0)

-  node.frequency = node.playbackRate
+  # node.frequency = node.playbackRate

   return node


Fix 404 bug

Fix 404 bug

--- apps/artist/routes.coffee
+++ apps/artist/routes.coffee
@@ -18,7 +18,7 @@
     artist.related().artworks.fetch(cache: true, data: sort: '-iconicity', published: true, size: 7)
   ]).spread((artistRequest, artworksRequest) ->
     if artistRequest.state is 'rejected'
-      res.backboneError(artist, artistRequest.reason.res)
+      res.backboneError(artist, artistRequest.reason)
     else
       if req.params.tab? or artist.href() is res.locals.sd.CURRENT_PATH
         res.locals.sd.ARTIST = artist.toJSON()


register: change copy of signup with GitHub button

--- client/Login/AppView.coffee
+++ client/Login/AppView.coffee
@@ -117,7 +117,7 @@
       click       : registerHandler

     @github       = new KDButtonView
-      title       : "Sign in with GitHub"
+      title       : "Sign up with GitHub"
       style       : 'solid github'
       icon        : yes
       callback    : ->


Update includes

Update includes

--- client/Main/includes.coffee
+++ client/Main/includes.coffee
@@ -209,6 +209,7 @@

   "providers/config.coffee",
   "providers/computeprovider.coffee",
+  "providers/computeprovider-ui.coffee",

   "providers/provideritemview.coffee",
   "providers/providerbaseview.coffee",


Implement delete singletons and instances to kd

--- lib/core/kd.coffee
+++ lib/core/kd.coffee
@@ -21,6 +21,8 @@
       if @[key] then throw new Error "#{key} is already registered"
       else @[key] = val

+  deleteSingletons: -> singletons = []
+
   registerSingleton: (name, obj, override = no)->
     if (existing = singletons[name])?
       if override
@@ -43,6 +45,8 @@

   deleteInstance: (id) ->
     delete instances[id]
+
+  deleteInstances: -> instances = []

   getSingleton: getSingleton


Cleanup @mention code

Cleanup @mention code

--- lib/formats.coffee
+++ lib/formats.coffee
@@ -285,11 +285,11 @@
   string.replace /[\\^$*+?.()|{}\[\]]/g, "\\$1"

 postprocessAtMentions = (text) ->
-  users = {}
-  Meteor.users.find().forEach (user) ->
-    users[user.username] = user.profile.fullname
-  return text unless 0 < _.size users
-  text.replace ///@(#{(escapeRe user for user of users).join '|'})\b///, (match, user) ->
+  users = Meteor.users.find {}, fields: username: 1
+  .fetch()
+  return text unless 0 < users.length
+  users = (escapeRe user.username for user in users)
+  text.replace ///@(#{users.join '|'})\b///, (match, user) ->
     "@#{linkToAuthor (routeGroup?() ? wildGroup), user}"

 katex = require 'katex'


Remove a TODO; existing code is adequate.

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -226,7 +226,6 @@

   # method_name is for error messages
   check_for_duplicates: (method_name) ->
-    # TODO: use each with index and slice to avoid last element
     return if @_list.length < 2
     for outer in [0..@_list.length-2]           # given @_list = [ a, b, c ], these loops produce the pairs (a,b), (a,c), (b,c)
       for inner in [outer+1..@_list.length-1]


:art: Make the env fix cross platform

:art: Make the env fix cross platform

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -32,9 +32,8 @@
           resolve(data.stderr.join(''))
       if isNodeExecutable
         options.env ?= {}
-        for prop in process.env
-          if process.env.hasOwnProperty(prop) and prop isnt 'OS'
-            options.env[prop] = process.env[prop]
+        for prop,value of process.env
+          options.env[prop] = value unless prop is 'OS'
         spawnedProcess = new BufferedNodeProcess({command, args, options, stdout, stderr, exit})
       else
         spawnedProcess = new BufferedProcess({command, args, stdout, stderr, exit})
@@ -108,3 +107,4 @@
           return filePath
       startDir.pop()
     return null
+


Add missing path require

Add missing path require

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -1,3 +1,4 @@
+path = require 'path'
 SelectorLinter = require 'atom-selector-linter'
 CSON = require 'season'
 fs = require 'fs-plus'
@@ -5,9 +6,9 @@
 exports.getSelectorDeprecations = ->
   linter = new SelectorLinter(maxPerPackage: 50)
   linter.checkPackage(pkg) for pkg in atom.packages.getLoadedPackages()
-
+
   userKeymapPath = atom.keymaps.getUserKeymapPath()
-
+
   if fs.isFileSync(userKeymapPath)
     try
       userKeymap = CSON.readFileSync(userKeymapPath)
@@ -18,9 +19,9 @@
         packagePath: ""
         sourcePath: userKeymapPath
       })
-
+
   userStyleSheetPath = atom.styles.getUserStyleSheetPath()
-
+
   if fs.isFileSync(userStyleSheetPath)
     try
       userStyleSheet = fs.readFileSync(userStyleSheetPath, 'utf8')
@@ -31,7 +32,7 @@
         packagePath: ""
         sourcePath: userStyleSheetPath
       })
-
+
   linter.getDeprecations()

 exports.getSelectorDeprecationsCount = ->


pool.patch() also applies the patch.

pool.patch() also applies the patch.

--- src/api/pool.coffee
+++ src/api/pool.coffee
@@ -1,4 +1,5 @@
-{$wait} = require '../fibers-utils'
+$debug = (require 'debug') 'xo:api:vm'
+{$coroutine, $wait} = require '../fibers-utils'

 #=====================================================================

@@ -37,8 +38,21 @@
   catch
     @throw 'NO_SUCH_OBJECT'

-  {sessionId} = @getXAPI pool
+  xapi = @getXAPI pool
   host = @getObject pool.master, 'host'
+
+  taskRef = $wait xapi.call 'task.create', 'Patch upload from XO', ''
+  @watchTask taskRef
+    .then $coroutine (patchRef) ->
+      $debug 'Patch upload succeeded'
+      xapi.call 'pool_patch.pool_apply', patchRef
+      return
+    .catch (error) ->
+      $debug 'Patch upload failed: %j', error
+      return
+    .finally $coroutine ->
+      xapi.call 'task.destroy', taskRef
+      return

   url = $wait @registerProxyRequest {
     # Receive a POST but send a PUT.
@@ -47,7 +61,8 @@
     hostname: host.address
     pathname: '/pool_patch_upload'
     query: {
-      session_id: sessionId
+      session_id: xapi.sessionId
+      task_id: taskRef
     }
   }


fix bug

fix bug

--- bin/FaceLiveServer.coffee
+++ bin/FaceLiveServer.coffee
@@ -26,9 +26,9 @@
     if err
       throw err
     else if reply == facePass
-      "you're #{usr}"
+      res.send "you're #{usr}"
     else
-      "not authed"
+      res.send "not authed"

 http.listen port, ->
   console.log "listening on *:", port


clean up a bunch of these repetitive getters

--- lib/runtime.coffee
+++ lib/runtime.coffee
@@ -96,104 +96,105 @@
         runtime

 exports.createBinopsInterface = (module, abi) ->
-        return Object.create null, {
-                "^":          { get: -> abi.createExternalFunction module, "_ejs_op_bitwise_xor", types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "&":          { get: -> abi.createExternalFunction module, "_ejs_op_bitwise_and", types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "|":          { get: -> abi.createExternalFunction module, "_ejs_op_bitwise_or",  types.EjsValue, [types.EjsValue, types.EjsValue] }
-                ">>":         { get: -> abi.createExternalFunction module, "_ejs_op_rsh",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "<<":         { get: -> abi.createExternalFunction module, "_ejs_op_lsh",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                ">>>":        { get: -> abi.createExternalFunction module, "_ejs_op_ursh",        types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "<<<":        { get: -> abi.createExternalFunction module, "_ejs_op_ulsh",        types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "%":          { get: -> abi.createExternalFunction module, "_ejs_op_mod",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "+":          { get: -> abi.createExternalFunction module, "_ejs_op_add",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "*":          { get: -> abi.createExternalFunction module, "_ejs_op_mult",        types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "/":          { get: -> abi.createExternalFunction module, "_ejs_op_div",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "-":          { get: -> abi.createExternalFunction module, "_ejs_op_sub",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "<":          { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_lt",          types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "<=":         { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_le",          types.EjsValue, [types.EjsValue, types.EjsValue] }
-                ">":          { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_gt",          types.EjsValue, [types.EjsValue, types.EjsValue] }
-                ">=":         { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_ge",          types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "===":        { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_strict_eq",   types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "==":         { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_eq",          types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "!==":        { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_strict_neq",  types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "!=":         { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_neq",         types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "instanceof": { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_instanceof",  types.EjsValue, [types.EjsValue, types.EjsValue] }
-                "in":         { get: -> returns_ejsval_bool abi.createExternalFunction module, "_ejs_op_in",          types.EjsValue, [types.EjsValue, types.EjsValue] }
+        createBinop = (n) -> abi.createExternalFunction module, n, types.EjsValue, [types.EjsValue, types.EjsValue]
+        return Object.create null, {
+                "^":          { get: ->                     createBinop "_ejs_op_bitwise_xor" }
+                "&":          { get: ->                     createBinop "_ejs_op_bitwise_and" }
+                "|":          { get: ->                     createBinop "_ejs_op_bitwise_or" }
+                ">>":         { get: ->                     createBinop "_ejs_op_rsh" }
+                "<<":         { get: ->                     createBinop "_ejs_op_lsh" }
+                ">>>":        { get: ->                     createBinop "_ejs_op_ursh" }
+                "<<<":        { get: ->                     createBinop "_ejs_op_ulsh" }
+                "%":          { get: ->                     createBinop "_ejs_op_mod" }
+                "+":          { get: ->                     createBinop "_ejs_op_add" }
+                "*":          { get: ->                     createBinop "_ejs_op_mult" }
+                "/":          { get: ->                     createBinop "_ejs_op_div" }
+                "-":          { get: ->                     createBinop "_ejs_op_sub" }
+                "<":          { get: -> returns_ejsval_bool createBinop "_ejs_op_lt" }
+                "<=":         { get: -> returns_ejsval_bool createBinop "_ejs_op_le" }
+                ">":          { get: -> returns_ejsval_bool createBinop "_ejs_op_gt" }
+                ">=":         { get: -> returns_ejsval_bool createBinop "_ejs_op_ge" }
+                "===":        { get: -> returns_ejsval_bool createBinop "_ejs_op_strict_eq" }
+                "==":         { get: -> returns_ejsval_bool createBinop "_ejs_op_eq" }
+                "!==":        { get: -> returns_ejsval_bool createBinop "_ejs_op_strict_neq" }
+                "!=":         { get: -> returns_ejsval_bool createBinop "_ejs_op_neq" }
+                "instanceof": { get: -> returns_ejsval_bool createBinop "_ejs_op_instanceof" }
+                "in":         { get: -> returns_ejsval_bool createBinop "_ejs_op_in" }
         }

 exports.createAtomsInterface = (module) ->
-        return Object.create null, {
-                "null":      { get: -> module.getOrInsertGlobal           "_ejs_atom_null",                 types.EjsValue }
-                "undefined": { get: -> module.getOrInsertGlobal           "_ejs_atom_undefined",            types.EjsValue }
-                "length":    { get: -> module.getOrInsertGlobal           "_ejs_atom_length",               types.EjsValue }
-                "__ejs":     { get: -> module.getOrInsertGlobal           "_ejs_atom___ejs",                types.EjsValue }
-                "object":    { get: -> module.getOrInsertGlobal           "_ejs_atom_object",               types.EjsValue }
-                "function":  { get: -> module.getOrInsertGlobal           "_ejs_atom_function",             types.EjsValue }
-                "prototype": { get: -> module.getOrInsertGlobal           "_ejs_atom_prototype",            types.EjsValue }
-                "Object":    { get: -> module.getOrInsertGlobal           "_ejs_atom_Object",               types.EjsValue }
-                "Array":     { get: -> module.getOrInsertGlobal           "_ejs_atom_Array",                types.EjsValue }
+        getGlobal = (n) -> module.getOrInsertGlobal n, types.EjsValue
+        return Object.create null, {
+                "null":      { get: -> getGlobal "_ejs_atom_null" }
+                "undefined": { get: -> getGlobal "_ejs_atom_undefined" }
+                "length":    { get: -> getGlobal "_ejs_atom_length" }
+                "__ejs":     { get: -> getGlobal "_ejs_atom___ejs" }
+                "object":    { get: -> getGlobal "_ejs_atom_object" }
+                "function":  { get: -> getGlobal "_ejs_atom_function" }
+                "prototype": { get: -> getGlobal "_ejs_atom_prototype" }
+                "Object":    { get: -> getGlobal "_ejs_atom_Object" }
+                "Array":     { get: -> getGlobal "_ejs_atom_Array" }
         }

 exports.createGlobalsInterface = (module) ->
-        return Object.create null, {
-                "Object":       { get: -> module.getOrInsertGlobal           "_ejs_Object",                 types.EjsValue }
-                "Object_prototype": { get: -> module.getOrInsertGlobal           "_ejs_Object_prototype",   types.EjsValue }
-                "Boolean":      { get: -> module.getOrInsertGlobal           "_ejs_Boolean",                types.EjsValue }
-                "String":       { get: -> module.getOrInsertGlobal           "_ejs_String",                 types.EjsValue }
-                "Number":       { get: -> module.getOrInsertGlobal           "_ejs_Number",                 types.EjsValue }
-                "Array":        { get: -> module.getOrInsertGlobal           "_ejs_Array",                  types.EjsValue }
-                "DataView":     { get: -> module.getOrInsertGlobal           "_ejs_DataView",               types.EjsValue }
-                "Date":         { get: -> module.getOrInsertGlobal           "_ejs_Date",                   types.EjsValue }
-                "Error":        { get: -> module.getOrInsertGlobal           "_ejs_Error",                  types.EjsValue }
-                "EvalError":    { get: -> module.getOrInsertGlobal           "_ejs_EvalError",              types.EjsValue }
-                "RangeError":   { get: -> module.getOrInsertGlobal           "_ejs_RangeError",             types.EjsValue }
-                "ReferenceError": { get: -> module.getOrInsertGlobal         "_ejs_ReferenceError",         types.EjsValue }
-                "SyntaxError":  { get: -> module.getOrInsertGlobal           "_ejs_SyntaxError",            types.EjsValue }
-                "TypeError":    { get: -> module.getOrInsertGlobal           "_ejs_TypeError",              types.EjsValue }
-                "URIError":     { get: -> module.getOrInsertGlobal           "_ejs_URIError",               types.EjsValue }
-                "Function":     { get: -> module.getOrInsertGlobal           "_ejs_Function",               types.EjsValue }
-                "JSON":         { get: -> module.getOrInsertGlobal           "_ejs_JSON",                   types.EjsValue }
-                "Math":         { get: -> module.getOrInsertGlobal           "_ejs_Math",                   types.EjsValue }
-                "Map":          { get: -> module.getOrInsertGlobal           "_ejs_Map",                    types.EjsValue }
-                "Proxy":        { get: -> module.getOrInsertGlobal           "_ejs_Proxy",                  types.EjsValue }
-                "Promise":      { get: -> module.getOrInsertGlobal           "_ejs_Promise",                types.EjsValue }
-                "Reflect":      { get: -> module.getOrInsertGlobal           "_ejs_Reflect",                types.EjsValue }
-                "RegExp":       { get: -> module.getOrInsertGlobal           "_ejs_RegExp",                 types.EjsValue }
-                "Symbol":       { get: -> module.getOrInsertGlobal           "_ejs_Symbol",                 types.EjsValue }
-                "Set":          { get: -> module.getOrInsertGlobal           "_ejs_Set",                    types.EjsValue }
-                "console":      { get: -> module.getOrInsertGlobal           "_ejs_console",                types.EjsValue }
-                "ArrayBuffer":  { get: -> module.getOrInsertGlobal           "_ejs_ArrayBuffer",            types.EjsValue }
-                "Int8Array":    { get: -> module.getOrInsertGlobal           "_ejs_Int8Array",              types.EjsValue }
-                "Int16Array":   { get: -> module.getOrInsertGlobal           "_ejs_Int16Array",             types.EjsValue }
-                "Int32Array":   { get: -> module.getOrInsertGlobal           "_ejs_Int32Array",             types.EjsValue }
-                "Uint8Array":   { get: -> module.getOrInsertGlobal           "_ejs_Uint8Array",             types.EjsValue }
-                "Uint16Array":  { get: -> module.getOrInsertGlobal           "_ejs_Uint16Array",            types.EjsValue }
-                "Uint32Array":  { get: -> module.getOrInsertGlobal           "_ejs_Uint32Array",            types.EjsValue }
-                "Float32Array": { get: -> module.getOrInsertGlobal           "_ejs_Float32Array",           types.EjsValue }
-                "Float64Array": { get: -> module.getOrInsertGlobal           "_ejs_Float64Array",           types.EjsValue }
-                "XMLHttpRequest": { get: -> module.getOrInsertGlobal         "_ejs_XMLHttpRequest",         types.EjsValue }
-                "process":      { get: -> module.getOrInsertGlobal           "_ejs_Process",                types.EjsValue }
-
-                "require":      { get: -> module.getOrInsertGlobal           "_ejs_require",                types.EjsValue }
-                "isNaN":        { get: -> module.getOrInsertGlobal           "_ejs_isNaN",                  types.EjsValue }
-                "isFinite":     { get: -> module.getOrInsertGlobal           "_ejs_isFinite",               types.EjsValue }
-                "parseInt":     { get: -> module.getOrInsertGlobal           "_ejs_parseInt",               types.EjsValue }
-                "parseFloat":   { get: -> module.getOrInsertGlobal           "_ejs_parseFloat",             types.EjsValue }
-                "decodeURI":    { get: -> module.getOrInsertGlobal           "_ejs_decodeURI",              types.EjsValue }
-                "encodeURI":    { get: -> module.getOrInsertGlobal           "_ejs_encodeURI",              types.EjsValue }
-                "decodeURIComponent":  { get: -> module.getOrInsertGlobal           "_ejs_decodeURIComponent",              types.EjsValue }
-                "encodeURIComponent":  { get: -> module.getOrInsertGlobal           "_ejs_encodeURIComponent",              types.EjsValue }
-
-                "undefined": { get: -> module.getOrInsertGlobal "_ejs_undefined", types.EjsValue }
-                "Infinity":  { get: -> module.getOrInsertGlobal "_ejs_Infinity",  types.EjsValue }
-                "NaN":       { get: -> module.getOrInsertGlobal "_ejs_nan",       types.EjsValue }
-
+        getGlobal = (n) -> module.getOrInsertGlobal n, types.EjsValue
+        return Object.create null, {
+                "Object":             { get: -> getGlobal "_ejs_Object" }
+                "Object_prototype":   { get: -> getGlobal "_ejs_Object_prototype" }
+                "Boolean":            { get: -> getGlobal "_ejs_Boolean" }
+                "String":             { get: -> getGlobal "_ejs_String" }
+                "Number":             { get: -> getGlobal "_ejs_Number" }
+                "Array":              { get: -> getGlobal "_ejs_Array" }
+                "DataView":           { get: -> getGlobal "_ejs_DataView" }
+                "Date":               { get: -> getGlobal "_ejs_Date" }
+                "Error":              { get: -> getGlobal "_ejs_Error" }
+                "EvalError":          { get: -> getGlobal "_ejs_EvalError" }
+                "RangeError":         { get: -> getGlobal "_ejs_RangeError" }
+                "ReferenceError":     { get: -> getGlobal "_ejs_ReferenceError" }
+                "SyntaxError":        { get: -> getGlobal "_ejs_SyntaxError" }
+                "TypeError":          { get: -> getGlobal "_ejs_TypeError" }
+                "URIError":           { get: -> getGlobal "_ejs_URIError" }
+                "Function":           { get: -> getGlobal "_ejs_Function" }
+                "JSON":               { get: -> getGlobal "_ejs_JSON" }
+                "Math":               { get: -> getGlobal "_ejs_Math" }
+                "Map":                { get: -> getGlobal "_ejs_Map" }
+                "Proxy":              { get: -> getGlobal "_ejs_Proxy" }
+                "Promise":            { get: -> getGlobal "_ejs_Promise" }
+                "Reflect":            { get: -> getGlobal "_ejs_Reflect" }
+                "RegExp":             { get: -> getGlobal "_ejs_RegExp" }
+                "Symbol":             { get: -> getGlobal "_ejs_Symbol" }
+                "Set":                { get: -> getGlobal "_ejs_Set" }
+                "console":            { get: -> getGlobal "_ejs_console" }
+                "ArrayBuffer":        { get: -> getGlobal "_ejs_ArrayBuffer" }
+                "Int8Array":          { get: -> getGlobal "_ejs_Int8Array" }
+                "Int16Array":         { get: -> getGlobal "_ejs_Int16Array" }
+                "Int32Array":         { get: -> getGlobal "_ejs_Int32Array" }
+                "Uint8Array":         { get: -> getGlobal "_ejs_Uint8Array" }
+                "Uint16Array":        { get: -> getGlobal "_ejs_Uint16Array" }
+                "Uint32Array":        { get: -> getGlobal "_ejs_Uint32Array" }
+                "Float32Array":       { get: -> getGlobal "_ejs_Float32Array" }
+                "Float64Array":       { get: -> getGlobal "_ejs_Float64Array" }
+                "XMLHttpRequest":     { get: -> getGlobal "_ejs_XMLHttpRequest" }
+                "process":            { get: -> getGlobal "_ejs_Process" }
+                "require":            { get: -> getGlobal "_ejs_require" }
+                "isNaN":              { get: -> getGlobal "_ejs_isNaN" }
+                "isFinite":           { get: -> getGlobal "_ejs_isFinite" }
+                "parseInt":           { get: -> getGlobal "_ejs_parseInt" }
+                "parseFloat":         { get: -> getGlobal "_ejs_parseFloat" }
+                "decodeURI":          { get: -> getGlobal "_ejs_decodeURI" }
+                "encodeURI":          { get: -> getGlobal "_ejs_encodeURI" }
+                "decodeURIComponent": { get: -> getGlobal "_ejs_decodeURIComponent" }
+                "encodeURIComponent": { get: -> getGlobal "_ejs_encodeURIComponent" }
+                "undefined":          { get: -> getGlobal "_ejs_undefined" }
+                "Infinity":           { get: -> getGlobal "_ejs_Infinity" }
+                "NaN":                { get: -> getGlobal "_ejs_nan" }
                 # kind of a hack, but since we don't define these...
-                "window":       { get: -> module.getOrInsertGlobal           "_ejs_undefined",              types.EjsValue }
-                "document":     { get: -> module.getOrInsertGlobal           "_ejs_undefined",              types.EjsValue }
+                "window":             { get: -> getGlobal "_ejs_undefined" }
+                "document":           { get: -> getGlobal "_ejs_undefined" }
         }

 exports.createSymbolsInterface = (module) ->
-        return Object.create null, {
-                "create":      { get: -> module.getOrInsertGlobal           "_ejs_Symbol_create",           types.EjsValue }
+        getGlobal = (n) -> module.getOrInsertGlobal n, types.EjsValue
+        return Object.create null, {
+                "create":      { get: -> getGlobal "_ejs_Symbol_create" }
         }


Fix broken path when using nested alias params

Resolves #236

--- lib/session.coffee
+++ lib/session.coffee
@@ -537,7 +537,7 @@
       if Array.isArray(path[path.length - 1])
         keys = {}
         for p, i in path[path.length - 1] by 2
-          keys[p] = path[path.length - 1][i + 1]
+          keys[p.join('.')] = path[path.length - 1][i + 1]
       else if path[path.length - 1] == '*'
         keys = {}


Using exports for bindable

Using exports for bindable

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -58,5 +58,4 @@
       callbacks.each (callback) ->
         callback.apply(self, parameters)

-window.Bindable = Bindable
-
+(exports ? this)["Bindable"] = Bindable


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -49,7 +49,7 @@
     eventCallbacks[event].push(callback)

   ###*
-  The unbind method removes a specific event listener, or all event listeners if
+  Removes a specific event listener, or all event listeners if
   no specific listener is given.

   <code><pre>


Update Rivets.Binding::setObserver to work with the new KeypathObserver implementation.

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -26,9 +26,9 @@
     @binder = {routine: @binder} if @binder instanceof Function

   setObserver: =>
-    @observer = new KeypathObserver @view, @view.models, @keypath, (target) =>
+    @observer = new KeypathObserver @view, @view.models, @keypath, (obs) =>
       @unbind true if @key
-      @model = target
+      @model = obs.target
       @bind true if @key
       @sync()


Use <%- %> instead of _.escape

Use <%- %> instead of _.escape

--- backbone.tableview.coffee
+++ backbone.tableview.coffee
@@ -55,7 +55,7 @@
     """
     searchTemplate: _.template """
         <div class="<%= classSize %>">
-            <input type="text" class="search-query input-block-level pull-right" placeholder="<%= model.detail || model %>" value="<%= _.escape(data[model.query || "q"] || "") %>"></input>
+            <input type="text" class="search-query input-block-level pull-right" placeholder="<%= model.detail || model %>" value="<%- data[model.query || "q"] || "" %>"></input>
         </div>
     """
     paginationTemplate: _.template """


The error module was not being required.

There would of been a different error if this did go through.

--- src/cli/ruby.coffee
+++ src/cli/ruby.coffee
@@ -1,5 +1,6 @@
 path = require "path"
 shell = require "shelljs"
+error = require "./../error"
 logger = require "./../logger"
 log = logger.create "ruby"
 synt_rb = path.join __dirname, "..", "..", "ruby", "bin", "synt"


Removed the selectedfiles event.

Removed the selectedfiles event.

It’s a very confusing unnecessary event.

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -47,7 +47,6 @@
     "dragenter"
     "dragover"
     "dragleave"
-    "selectedfiles"
     "addedfile"
     "removedfile"
     "thumbnail"
@@ -266,9 +265,6 @@
     dragenter: (e) -> @element.classList.add "dz-drag-hover"
     dragover: (e) -> @element.classList.add "dz-drag-hover"
     dragleave: (e) -> @element.classList.remove "dz-drag-hover"
-
-    # Called whenever files are dropped or selected
-    selectedfiles: noop

     # Called whenever there are no files left in the dropzone anymore, and the
     # dropzone should be displayed as if in the initial state.
@@ -502,7 +498,6 @@
         @hiddenFileInput.addEventListener "change", =>
           files = @hiddenFileInput.files
           if files.length
-            @emit "selectedfiles", files
             @handleFiles files
           setupHiddenFileInput()
       setupHiddenFileInput()
@@ -688,7 +683,6 @@
     @emit "drop", e

     files = e.dataTransfer.files
-    @emit "selectedfiles", files

     # Even if it's a folder, files.length will contain the folders.
     if files.length


Looking for dropzones should even work in IE6 now.

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -717,6 +717,16 @@

 contentLoaded window, ->
-  dropzones = document.querySelectorAll ".dropzone"
+  if false #document.querySelectorAll
+    dropzones = document.querySelectorAll ".dropzone"
+  else
+    dropzones = [ ]
+    # IE :(
+    checkElements = (elements) ->
+      for el in elements
+        dropzones.push el if /dropzone/.test el.className
+    checkElements document.getElementsByTagName "div"
+    checkElements document.getElementsByTagName "form"
+
   new Dropzone dropzone for dropzone in dropzones


put user ID in URL

put user ID in URL

--- client/game.coffee
+++ client/game.coffee
@@ -42,6 +42,14 @@
     message: "Want to play some Noughts?", inviteCallback

 displayNotice = (msg) -> $(".nNotification").text(msg)
+
+setUrl = (gameId=null) ->
+  if gameId
+    window.history.pushState({}, "",
+        "?user_id=#{Meteor.userId()}&game_id=#{gameId}")
+  else
+    window.history.pushState({}, "",
+        "?user_id=#{Meteor.userId()}")

 PlayScreen = me.ScreenObject.extend
   handleClick_: (tile) ->
@@ -97,7 +105,6 @@
 handleNewGameClick = ->
   Meteor.call "newGame", Meteor.userId(), (err, gameId) ->
     if err then throw err
-    window.history.pushState({}, "", "?game_id=#{gameId}")
     showInviteDialog (inviteResponse) ->
       return if not inviteResponse
       invitedUser = inviteResponse.to[0]
@@ -125,6 +132,10 @@
 onSubscribe = ->
   $(".nLoading").css({display: "none"})
   gameId = $.url().param("game_id")
+
+  Meteor.autorun ->
+    setUrl(Session.get("gameId"))
+
   if gameId
     game = noughts.Games.findOne(gameId)
     displayError("Game not found for gameId: " + gameId) unless game
@@ -141,7 +152,8 @@
     displayError("Game not found for requestIds: " + requestIds) unless game
     Session.set("gameId", game._id)
     me.state.change(me.state.PLAY)
-  $(".nNewGamePromo").css({display: "block"})
+  else
+    $(".nNewGamePromo").css({display: "block"})

 # Only runs the second time it's called, to ensure both facebook and melon.js
 # are loaded


added more fleshed out reply functionality

--- client/main.coffee
+++ client/main.coffee
@@ -5,9 +5,10 @@
   ###

   class Post extends Backbone.Model
-    respond: (response) ->
+    respond: (response) =>

       p = new Post(
+        id: 1.1
         editing: false
         content: response.posttext
         linkedcontent: response.linkdata
@@ -17,7 +18,7 @@
         responses: ''
       )
       pv = new PostView(model: p)
-      $('#assignments').append(pv.render())
+      $('#response' + @id).append(pv.render())
       #@save(
       #  responses:
       #    @get( 'responses' ).push( p.cid )
@@ -56,7 +57,8 @@
       @renderPostContent()
       $(@el).find('.inner-question').tagbox({editing: true, tags:@model.get('tags')})
       $(@el).find('.inner-question').omnipost({callback: @model.respond, editing:true})
-
+      responsediv = $("

")
+      $(@el).find('.inner-question').append(responsediv)
       return $(@el)

   class StreamView extends Backbone.View
@@ -86,6 +88,7 @@
       postCollection.fetch()
     mockup: ->
       p = new Post(
+        id: 1
         editing: false
         content: 'This is an example post.'
         linkedcontent: ''
@@ -97,6 +100,7 @@
       pv = new PostView(model: p)
       $('#assignments').append(pv.render())
       p1 = new Post(
+        id: 2
         editing: false
         content: 'This is an example post.'
         linkedcontent: 'www.imdb.com'


Fix callback on initialize

Fix callback on initialize

--- src/endpoint.coffee
+++ src/endpoint.coffee
@@ -19,7 +19,8 @@

     if listen
       @_io.listen 3000
-      callback()
+
+    callback()

   close: ->


#140: document some graphite functions

#140: document some graphite functions

--- src/graphite.coffee
+++ src/graphite.coffee
@@ -16,7 +16,7 @@
   Html = require 'html'
   Documentation = require 'documentation'

-  graphite = modules.create 'graphite', ({fn, component_fn, cmd, settings}) ->
+  graphite = modules.create 'graphite', ({fn, component_fn, cmd, settings, doc}) ->
     build_function_doc = (ctx, doc) ->
       FunctionDocsComponent {ctx, docs: docs.function_docs[doc.function_name]}

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

     default_target_command = 'img'

-    fn 'q', 'Escapes a Graphite metric query', (targets...) ->
+    doc 'q',
+      'Escapes a metric query'
+      """
+      Use `q` to reference a metric name in the DSL.
+
+      The Graphite API uses unquoted strings to specify metric names and patterns.
+      The string argument to `q` will be passed directly to the API.
+
+      For example, `sumSeries(q('twitter.*.tweetcount'))` will be sent as `sumSeries(twitter.*.tweetcount)`.
+      """
+
+    fn 'q', (targets...) ->
       for t in targets
         unless _.isString t
           throw new TypeError "#{t} is not a string"
@@ -83,16 +94,29 @@
         @add_component React.DOM.h3 {}, 'Parameters'
         @help 'graphite_parameters'

-    fn 'params', 'Generates the parameters for a Graphite render call', (args...) ->
+    doc 'params',
+      'Generates the parameters for a render API call'
+      '''
+      `params` interprets its arguments in the same way as [`get_data`](help:graphite.get_data),
+      but simply returns the arguments that would be passed to the API.
+
+      For example:
+      ```
+      options areaMode: 'stacked'
+      object params sumSeries(q('twitter.*.tweetcount')), width: 1024, height: 768
+      ```
+      '''
+
+    fn 'params', (args...) ->
       result = args_to_params @, args
       @value result

-    component_fn 'url', 'Generates a URL for a Graphite image', (args...) ->
+    component_fn 'url', 'Generates a URL for a graph image', (args...) ->
       params = args_to_params @, args
       url = graphite.render_url params
       React.DOM.pre {}, React.DOM.a {href: url, target: 'blank'}, url

-    fn 'img', 'Renders a Graphite graph image', (args...) ->
+    fn 'img', 'Renders a graph image', (args...) ->
       params = args_to_params @, args
       url = graphite.render_url params
       @async ->


clean up data promised by find

clean up data promised by find

--- src/graphite.coffee
+++ src/graphite.coffee
@@ -231,7 +231,7 @@
     @value @async ->
       $result = @output()
       promise = graphite.complete(query).then (response) ->
-        response.metrics
+        _.map response.metrics, ({path, name, is_leaf}) -> {path, name, is_leaf: is_leaf == '1'}

       promise._lead_render = ->
         promise.done (metrics) =>
@@ -239,7 +239,7 @@
           for node in metrics
             $li = $ '<li class="cm-string"/>'
             text = node.path
-            text += '*' if node.is_leaf == '0'
+            text += '*' unless node.is_leaf
             node_parts = text.split '.'
             for part, i in node_parts
               if i > 0
@@ -250,10 +250,11 @@
               $li.append $span
             do (text) =>
               $li.on 'click', =>
-                if node.is_leaf == '0'
+                if node.is_leaf
+                  @run "q(#{JSON.stringify text})"
+                else
                   @run "find #{JSON.stringify text}"
-                else
-                  @run "q(#{JSON.stringify text})"
+
             $ul.append $li
           $result.append $ul
       promise


deleting underused modules

deleting underused modules

--- src/hittable.coffee
+++ src/hittable.coffee
@@ -1,11 +0,0 @@
-Hittable = (I, self) ->
-  I ||= {}
-
-  $.reverseMerge I,
-    health: 25
-
-  hit: ->
-    I.health--
-
-    self.destroy() if I.health < 0
-


Add arrow snippets and remove unneeded quotes

--- user/snippets.cson
+++ user/snippets.cson
@@ -53,7 +53,7 @@
 		prefix: "placeholder"
 		body: "::-webkit-input-placeholder{    $1}\n::-ms-input-placeholder{        $1}\n:-ms-input-placeholder{         $1}\n::-moz-placeholder{opacity: 1;  $1}\n:-moz-placeholder{              $1}\n::placeholder{                  $1}\n:placeholder-shown{             $1}\n"

-	"Comment":
+	Comment:
 		prefix: "/"
 		body: "/** $1 */"

@@ -140,7 +140,7 @@
 	"Patterns (Multiline)":
 		prefix: "Pat"
 		body: "patterns: [{\n\t$1\n}]"
-	"Include":
+	Include:
 		prefix: "inc"
 		body: "include: \"#$1\""
 	"Extended Expression":
@@ -182,20 +182,25 @@
 		body: "console.info $0"

 ".source.coffee .oniguruma":
-	"Include":
+	Include:
 		prefix: "inc"
 		body: "Include “#$1”"

 # Perl
 ".source.perl":
-	"Invocant":
+	Invocant:
 		prefix: "me"
 		body: "my \$self = shift;\n"
-	"Executable":
+	Executable:
 		prefix: "pl"
 		body: "#!/usr/bin/env perl\nuse strict;\nuse warnings;\nuse autodie;\n\n$1"
-
+	Dumper:
+		prefix: "dumper"
+		body: "use Data::Dumper;"
+	Dump:
+		prefix: "dump"
+		body: "print Dumper(${1:$data});"

 # Ruby
 ".source.ruby":
@@ -250,10 +255,16 @@
 	×:
 		prefix: "x"
 		body: "× "
+	→:
+		prefix: "->"
+		body: "→"
+	←:
+		prefix: "<-"
+		body: "←"

 # Shell-scripts
 ".source.shell":
-	"Getopts":
+	Getopts:
 		prefix: "opt"
 		body: "while getopts ${1:opt} ${2:option}; do\n\tcase $${2:option} in\n\t\t${3:o) SOME=THING;;}\n\tesac\ndone\nshift $((OPTIND - 1))"


One more event cleanup

One more event cleanup

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -48,6 +48,7 @@
     @addLine(x, y)

   endDraw: (x, y) ->
+    return unless @isDrawing
     @isDrawing = false
     @addLine(x, y)


start new element visual under elementbox element

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -183,8 +183,16 @@

   placingObject: null

-  makePlacingObject: (obj, x, y) ->
-    @placingObject = @ve.surface.addElement(obj, 0, 0)
+  makePlacingObject: (obj) ->
+    @ve.raycaster.setFromCamera(@pos, @ve.camera)
+    bix = @ve.raycaster.intersectObject(@ve.surface.baseRect.obj3d)
+    x = y = 0
+    if bix.length > 0
+      ix = bix[bix.length - 1]
+      x = ix.point.x
+      y = ix.point.y
+
+    @placingObject = @ve.surface.addElement(obj, x, y)

   #onmousedown handlers
   baseDown: (event) ->
@@ -197,7 +205,8 @@
         e = ixs[0].object.userData
         console.log "! ebox select -- " + e.constructor.name
         console.log e
-        @makePlacingObject(e, 0, 0)
+
+        @makePlacingObject(e)
         @ve.container.onmousemove = (eve) => @placingMove(eve)
         @ve.container.onmousedown = (eve) => @placingDown(eve)


clean queue and make setInterval configurable untested

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -1,5 +1,6 @@
 $ ->
   window.image_queue = []
+  window.settings ={interval_time:20000}
   image_box = $("#image_box")
   current_image = $("#current_image")
   top_nave = $("#top_nav")
@@ -19,11 +20,20 @@
       window.image_queue.push(response.file)
       image.attr("src","file"+response.file)
     $.get "/next", {current_image}, callback, "json"
-  setInterval(()=>
-    img  = $("#current_image").find("img")
-    current_src = img.attr("src") if img
-    console.log current_src
-    get_next_image(current_src)
-  ,20000)
+
+  clean_queue ()->
+    while window.image_queue.length>50
+      window.image_queue.shift()
+  setInterval(clean_queue,window.settings.interval_time*6)
+
+  create_image_rotate = () =>
+    clearInterval(window.rotate_image_invterval) if window.rotate_image_invterval
+    window.rotate_image_invterval = setInterval(()=>
+      img  = $("#current_image").find("img")
+      current_src = img.attr("src") if img
+      console.log current_src
+      get_next_image(current_src)
+    ,window.settings.interval_time)

   get_next_image()
+  create_image_rotate()


Ace: viewAppended: wait appStorage to prevent flickering and load issues

--- client/ace/lib/ace.coffee
+++ client/ace/lib/ace.coffee
@@ -12,9 +12,7 @@

 prependWithOrigin = (path) -> [location.origin, path].join '/'

-module.exports =
-
-class Ace extends KDView
+module.exports = class Ace extends KDView

   ACE_READY = no

@@ -90,11 +88,10 @@

     @hide()

-    @appStorage.fetchStorage (storage) -> # XXX: wtf? -og
-
-    if ACE_READY
-    then @scriptLoaded()
-    else Ace.once 'ScriptLoaded', @bound 'scriptLoaded'
+    @appStorage.fetchStorage =>
+      if ACE_READY
+      then @scriptLoaded()
+      else Ace.once 'ScriptLoaded', @bound 'scriptLoaded'

   scriptLoaded: ->


Update context.cson format

Update context.cson format

--- menus/context.cson
+++ menus/context.cson
@@ -3,7 +3,8 @@
 ###

 'context-menu':
-  '.workspace .editor:not(.mini)':
-    'Toggle Preview': 'preview:toggle'
-    'Toggle Preview Options': 'preview:toggle-options'
-    'Select Preview Renderer': 'preview:select-renderer'
+  'atom-text-editor': [
+    {label: 'Toggle Preview', command: 'preview:toggle'}
+    {label: 'Toggle Preview Options', command: 'preview:toggle-options'}
+    {label: 'Select Preview Renderer', command: 'preview:select-renderer'}
+  ]


accept and understand finnish style date input

--- web/js/utils.coffee
+++ web/js/utils.coffee
@@ -24,7 +24,7 @@

     @language: () ->
       window.appLang.substring(0, 2)
-
+
     @utcOffset: () ->
       @_utcOffset ?= parseInt($('meta[name=x-timezone-offset]').attr('content'), 10)

@@ -54,7 +54,7 @@
       moment(d).format("YYYY-MM-DD")

     @parseDateAndTimeFromHTML5Input: (date, time) ->
-      moment("#{date} #{time}", "YYYY-MM-DD HH:mm").toDate()
+      moment("#{date} #{time}", ["YYYY-MM-DD HH:mm", "DD.MM.YYYY HH:mm"]).toDate()

     @addMinutes: (date, n) ->
       new Date(date.getTime() + n * 60 * 1000)


Increase auto pagination scroll threshold

--- coffee/views/posts.coffee
+++ coffee/views/posts.coffee
@@ -15,7 +15,7 @@
     container: "#main"
     itemView: PostView

-    SCROLL_THRESHOLD: 100
+    SCROLL_THRESHOLD: 300

     getView: (model) ->
       new @itemView model: model, dialog: @subview "dialog"


Use undefined as initial value.

Use undefined as initial value.

--- minimongoid.coffee
+++ minimongoid.coffee
@@ -1,5 +1,5 @@
 class Minimongoid
-  id: null
+  id: undefined
   attributes: {}

   constructor: (attributes = {}) ->
@@ -39,7 +39,7 @@
   # Demongoize is just an alias for mongoize.
   @demongoize: @mongoize

-  @_collection: null
+  @_collection: undefined

   # Rubists will love me, everyone else will burn me!
   #


Add getSchema method also for legacy ports

--- src/lib/Port.coffee
+++ src/lib/Port.coffee
@@ -25,6 +25,7 @@
     "#{@node} #{@name.toUpperCase()}"

   getDataType: -> @type
+  getSchema: -> null
   getDescription: -> @description

   attach: (socket) ->


improvements to validate prefix and fix frefix

--- lib/atom-ternjs-autocomplete.coffee
+++ lib/atom-ternjs-autocomplete.coffee
@@ -30,24 +30,35 @@
         @documentation = documentation

     isValidPrefix: (prefix) ->
-        if prefix.length? > 1
+        return true if prefix[prefix.length - 1] is '\.'
+        if prefix.length > 1
             prefix = '_' + prefix
         return true if prefix.replace(/\s/g, '').length is 0
         try (new Function("var " + prefix))()
         catch e then return false
         return true

+    fixPrefix: (prefix) ->
+        if prefix.lastIndexOf(' ') is prefix.length - 1
+            return ''
+        if (!prefix.replace(/\s/g, '').length) or prefix.endsWith(';')
+            return ''
+        prefix
+
     requestHandler: (options) ->
         return [] unless options?.editor? and options?.buffer? and options?.cursor?
         prefix = options.prefix

-        if prefix[prefix.length - 1].indexOf('.') is -1
-            if (!@isValidPrefix(prefix) or prefix.indexOf('..') != -1) and !@force
-                @documentation.hide()
-                return []
+        # .. crashes the server
+        if prefix.indexOf('..') != -1
+            @documentation.hide()
+            return []

-        if (!prefix.replace(/\s/g, '').length) or prefix.endsWith(';')
-            prefix = ''
+        if !@isValidPrefix(prefix) and !@force
+            @documentation.hide()
+            return []
+
+        prefix = @fixPrefix(prefix)

         that = this


Fix issue with wrapping stats

Fix issue with wrapping stats

--- scripts/stats.coffee
+++ scripts/stats.coffee
@@ -16,13 +16,13 @@
             entries = []
             color = 0
             key = []
-            for i in [0...30]
+            for i in [1...30]
                 a = new Date(thirty_days_ago.getTime())
                 a.setDate(a.getDate() + i)
                 labels.push("#{months[a.getMonth()]} #{a.getDate()}")
             for v in window.versions
                 data = []
-                for i in [0...30]
+                for i in [1...30]
                     a = new Date(thirty_days_ago.getTime())
                     a.setDate(a.getDate() + i)
                     events = _.filter(download_stats, (d) ->
@@ -73,12 +73,12 @@
             labels = []
             entries = []
             color = 0
-            for i in [0..30]
+            for i in [1...30]
                 a = new Date(thirty_days_ago.getTime())
                 a.setDate(a.getDate() + i)
                 labels.push("#{months[a.getMonth()]} #{a.getDate()}")
             data = []
-            for i in [0..30]
+            for i in [1...30]
                 a = new Date(thirty_days_ago.getTime())
                 a.setDate(a.getDate() + i)
                 events = _.filter(follower_stats, (d) ->


Closes #2869 - Added Swedish chat translation - thanks to @pitah81 ❤

--- public/assets/chat/chat.coffee
+++ public/assets/chat/chat.coffee
@@ -371,6 +371,23 @@
         'Since you didn\'t respond in the last %s minutes your conversation with %s got closed.': 'Поскольку вы не отвечали в течение последних %s минут, ваш разговор с %s был закрыт.'
         'Since you didn\'t respond in the last %s minutes your conversation got closed.': 'Поскольку вы не отвечали в течение последних %s минут, ваш разговор был закрыт.'
         'We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you!': 'К сожалению, ожидание свободного места требует больше времени. Повторите попытку позже или отправьте нам электронное письмо. Спасибо!'
+      'sv':
+        'Chat with us!': 'Chatta med oss!'
+        'Scroll down to see new messages': 'Rulla ner för att se nya meddelanden'
+        'Online': 'Online'
+        'Offline': 'Offline'
+        'Connecting': 'Ansluter'
+        'Connection re-established': 'Anslutningen återupprättas'
+        'Today': 'I dag'
+        'Send': 'Skicka'
+        'Chat closed by %s': 'Chatt stängd av %s'
+        'Compose your message...': 'Skriv ditt meddelande...'
+        'All colleagues are busy.': 'Alla kollegor är upptagna.'
+        'You are on waiting list position %s.': 'Du är på väntelistan som position %s.'
+        'Start new conversation': 'Starta ny konversation'
+        'Since you didn\'t respond in the last %s minutes your conversation with %s got closed.': 'Eftersom du inte svarat inom %s minuterna i din konversation med %s så stängdes chatten.'
+        'Since you didn\'t respond in the last %s minutes your conversation got closed.': 'Då du inte svarat inom de senaste %s minuterna så avslutades din chatt.'
+        'We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you!': 'Vi är ledsna, det tar längre tid som förväntat att få en ledig plats. Försök igen senare eller skicka ett e-postmeddelande till oss. Tack!'
     sessionId: undefined
     scrolledToBottom: true
     scrollSnapTolerance: 10