convert to new BS tab tags

convert to new BS tab tags

--- src/components/exercise.cjsx
+++ src/components/exercise.cjsx
@@ -38,30 +38,30 @@

   renderIntroTab: ->
     id = @props.id
-    <BS.TabPane eventKey="intro" tab="Intro">
+    <BS.Tab eventKey="intro" title="Intro">

         Exercise Stimulus
         <textarea onChange={@updateStimulus} defaultValue={ExerciseStore.getStimulus(id)}></textarea>

-    </BS.TabPane>
+    </BS.Tab>

   renderMpqTabs: ->
     {questions} = ExerciseStore.get(@props.id)
     for question, i in questions
-      <BS.TabPane key={question.id} eventKey={"question-#{i}"} tab={"Question #{i+1}"}>
+      <BS.Tab key={question.id} eventKey={"question-#{i}"} title={"Question #{i+1}"}>
         <Question id={question.id}
           sync={@sync}
           canMoveLeft={i isnt 0}
           canMoveRight={i isnt questions.length - 1}
           moveQuestion={@moveQuestion}
           removeQuestion={_.partial(@removeQuestion, question.id)} />
-      </BS.TabPane>
+      </BS.Tab>

   renderSingleQuestionTab: ->
     {questions} = ExerciseStore.get(@props.id)
-    <BS.TabPane key={0} eventKey='question-0' tab='Question'>
+    <BS.Tab key={0} eventKey='question-0' title='Question'>
       <Question id={_.first(questions)?.id} sync={@sync} />
-    </BS.TabPane>
+    </BS.Tab>

   exercisePreviewData: (ex) ->
     content: ex
@@ -100,20 +100,20 @@
          <BS.Button onClick={@addQuestion} className="add-mpq"
            bsStyle="primary">Add Question</BS.Button>}

-        <BS.TabbedArea defaultActiveKey='question-0' animation={false}
+        <BS.Tabs defaultActiveKey='question-0' animation={false}
           activeKey={tab} onSelect={@selectTab}
         >
           {if showMPQ then @renderIntroTab()}
           {if showMPQ then @renderMpqTabs() else @renderSingleQuestionTab()}
-          <BS.TabPane eventKey='tags' tab='Tags'>
+          <BS.Tab eventKey='tags' title='Tags'>
             <ExerciseTags exerciseId={@props.id} sync={@sync} />
-          </BS.TabPane>
+          </BS.Tab>
           { if not ExerciseStore.isNew(@props.id)
-            <BS.TabPane eventKey='assets' tab='Assets'>
+            <BS.Tab eventKey='assets' title='Assets'>
               <Attachments exerciseId={@props.id} />
-            </BS.TabPane>
+            </BS.Tab>
           }
-        </BS.TabbedArea>
+        </BS.Tabs>

       <ExercisePreview


Update en.coffee

Update en.coffee

--- app/locale/en.coffee
+++ app/locale/en.coffee
@@ -342,6 +342,7 @@
     tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus"
     tip_strong_opponents: "Even the strongest of opponents always has a weakness. - Itachi Uchiha"
     tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen."
+    solve_then_write: "First, solve the problem. Then, write the code. - John Johnson"

   game_menu:
     inventory_tab: "Inventory"


Updated top banner blurb away from holiday season messaging

--- app/locale/en.coffee
+++ app/locale/en.coffee
@@ -90,7 +90,7 @@
     teachers_love_codecombat_blurb2: "Would recommend CodeCombat to other computer science teachers"
     teachers_love_codecombat_blurb3: "Say that CodeCombat helps them support students’ problem solving abilities"
     teachers_love_codecombat_subblurb: "In partnership with McREL International, a leader in research-based guidance and evaluations of educational technology."
-    top_banner_blurb: "Parents, give your child the gift of coding and personalized instruction this holiday season!"
+    top_banner_blurb: "Parents, give your child the gift of coding and personalized instruction with our live teachers!" # {change}
     try_the_game: "Try the game"
     classroom_edition: "Classroom Edition:"
     learn_to_code: "Learn to code:"


Small fixes to previous merge

Small fixes to previous merge

--- app/locale/hu.coffee
+++ app/locale/hu.coffee
@@ -96,7 +96,7 @@
     careers: "Karrier"
 #    facebook: "Facebook"
 #    twitter: "Twitter"
-#    create_a_class: "Hozz létre egy Osztályt" # "Create a Class"
+    create_a_class: "Hozz létre egy Osztályt"
     other: "Egyéb"
     learn_to_code: "Tanulj meg Kódolni!"

@@ -700,7 +700,7 @@
 #    more_info_2: "teachers forum"
 #    more_info_3: "is a good place to connect with fellow educators who are using CodeCombat."

-#  teachers_quote:
+  teachers_quote:
 #    name: "Quote Form"
 #    title: "Request a Quote"
 #    subtitle: "Get CodeCombat in your classroom, club, school or district!"


Finish until line: 1440

Finish until line: 1440

--- app/locale/id.coffee
+++ app/locale/id.coffee
@@ -269,15 +269,15 @@
     student_type_3: "Bersaing dalam arena"
     student_type_4: "Pilih jagoanmu!"
     student_type_4: "Choose your hero!"
-    student_type_5: "Persiapkan Kelas Kodemu!"
+    student_type_5: "Persiapkan Kode Kelasmu!"
     signup_as_student: "Masuk sebagai siswa"
     individuals_or_parents: "Individu dan Orang tua"
     individual_type: "Untuk param pemain yang belajar kode di luar dari kelas. Para orang tua harus mendaftar akun di sini"
     signup_as_individual: "Daftar sebagai individu"
-    enter_class_code: "Masukkan Kelas Kode kamu"
+    enter_class_code: "Masukkan Kode Kelas kamu"
     enter_birthdate: "Masukkan tanggal lahirmu:"
     parent_use_birthdate: "Para orang tua, gunakan tanggal lahirmu."
-    ask_teacher_1: "Tanyakan gurumu untuk Kelas Kode kamu"
+    ask_teacher_1: "Tanyakan gurumu untuk Kode Kelas kamu"
     ask_teacher_2: "Bukan bagian dari kelas? Buatlah"
     ask_teacher_3: "Akun Individu"
     ask_teacher_4: " sebagai gantinya."
@@ -328,7 +328,7 @@
     next: "Selanjutnya"
     default_code: "Kode Asli"
     loading: "Memuat..."
-    overview: "Garis Besar"
+    overview: "Ikhtisar"
     processing: "Memproses..."
     solution: "Solusi"
     table_of_contents: "Daftar Isi"
@@ -1270,174 +1270,174 @@
     track_concepts8: "untuk bergabung"
     private_require_sub: "Klan privat membutuhkan langganan untuk membuat atau bergabung."

-#  courses:
-#    create_new_class: "Create New Class"
-#    solutions_require_licenses: "Level solutions are available for teachers who have licenses."
-#    unnamed_class: "Unnamed Class"
-#    edit_settings1: "Edit Class Settings"
-#    add_students: "Add Students"
-#    stats: "Statistics"
-#    student_email_invite_blurb: "Your students can also use class code __classCode__ when creating a Student Account, no email required."
-#    total_students: "Total students:"
-#    average_time: "Average level play time:"
-#    total_time: "Total play time:"
-#    average_levels: "Average levels completed:"
-#    total_levels: "Total levels completed:"
-#    students: "Students"
-#    concepts: "Concepts"
-#    play_time: "Play time:"
-#    completed: "Completed:"
-#    enter_emails: "Separate each email address by a line break or commas"
-#    send_invites: "Invite Students"
-#    number_programming_students: "Number of Programming Students"
-#    number_total_students: "Total Students in School/District"
-#    enroll: "Enroll"
-#    enroll_paid: "Enroll Students in Paid Courses"
-#    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_classroom: "Back to classroom"
-#    back_courses: "Back to my courses"
-#    edit_details: "Edit class details"
-#    purchase_enrollments: "Purchase Student Licenses"
-#    remove_student: "remove student"
-#    assign: "Assign"
-#    to_assign: "to assign paid courses."
-#    student: "Student"
-#    teacher: "Teacher"
-#    arena: "Arena"
-#    available_levels: "Available Levels"
-#    started: "started"
-#    complete: "complete"
-#    practice: "practice"
-#    required: "required"
-#    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"
-#    my_classes: "Current Classes"
-#    class_added: "Class successfully added!"
-#    view_levels: "view all levels"
-#    view_project_gallery: "view my classmates' projects"
-#    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"
-#    not_you: "Not you?"
-#    continue_playing: "Continue Playing"
-#    option1_header: "Invite Students by Email"
-#    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."
-#    license_will_revoke: "This student's paid license will be revoked and made available to assign to another student."
-#    keep_student: "Keep Student"
-#    removing_user: "Removing user"
-#    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"
-#    concepts_covered: "Concepts covered"
-#    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."
-#    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"
-#    create_class: "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."
-#    unsubscribe_warning: "Warning: You will be unsubscribed from your monthly subscription."
-#    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:"
-#    current_hero_female: "Current 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."
-#    course_membership_required_to_play: "You'll need to join a course to play this level."
-#    license_required_to_play: "Ask your teacher to assign a license to you so you can continue to play CodeCombat!"
-#    update_old_classroom: "New school year, new levels!"
-#    update_old_classroom_detail: "To make sure you're getting the most up-to-date levels, make sure you create a new class for this semester by clicking Create a New Class on your"
-#    teacher_dashboard: "teacher dashboard"
-#    update_old_classroom_detail_2: "and giving students the new Class Code that appears."
-#    view_assessments: "View Assessments"
-#    view_challenges: "view challenge levels"
-#    challenge: "Challenge:"
-#    challenge_level: "Challenge Level:"
-#    status: "Status:"
-#    assessments: "Assessments"
-#    challenges: "Challenges"
-#    level_name: "Level Name:"
-#    keep_trying: "Keep Trying"
-#    start_challenge: "Start Challenge"
-#    locked: "Locked"
-#    concepts_used: "Concepts Used:"
+  courses:
+    create_new_class: "Buat Kelas Baru"
+    solutions_require_licenses: "Solusi level akan tersedia bagi guru yang memiliki lisensi"
+    unnamed_class: "Kelas Tanpa Nama"
+    edit_settings1: "Ubah Pengaturan Kelas"
+    add_students: "Tambah Siswa"
+    stats: "Statistik"
+    student_email_invite_blurb: "Murid-murid anda juga bisa menggunakan kode kelas __classCode__ ketika membuat Akun Siswa, tanpa membutuhkan email."
+    total_students: "Jumlah siswa:"
+    average_time: "Rata-rata waktu bermain level:"
+    total_time: "Total waktu bermain:"
+    average_levels: "Rata-rata level yang terselesaikan:"
+    total_levels: "Total level yang terselesaikan:"
+    students: "Murid-murid"
+    concepts: "Konsep-konsep"
+    play_time: "Waktu bermain:"
+    completed: "Terselesaikan:"
+    enter_emails: "Pisahkan tiap alamat email dengan jeda baris ataupun koma"
+    send_invites: "Undang Siswa"
+    number_programming_students: "Jumlah Siswa Programming"
+    number_total_students: "Jumlah Siswa di Sekolah/Wilayah"
+    enroll: "Daftar"
+    enroll_paid: "Daftarkan Siswa di Kursus Berbayar"
+    get_enrollments: "Dapatkan Lisensi Lebih"
+    change_language: "Mengganti Bahasa"
+    keep_using: "Tetap Menggunakan"
+    switch_to: "Mengganti Menjadi"
+    greetings: "Hai!"
+    back_classrooms: "Kembali ke ruang kelasku"
+    back_classroom: "Kembali ke ruang kelas"
+    back_courses: "Kembali ke kursusku"
+    edit_details: "Ubah detail kelas"
+    purchase_enrollments: "Membeli Lisensi Siswa"
+    remove_student: "hapus siswa"
+    assign: "Daftarkan"
+    to_assign: "daftarkan kursus berbayar."
+    student: "Siswa"
+    teacher: "Guru"
+    arena: "Arena"
+    available_levels: "Level yang Tersedia"
+    started: "dimulai"
+    complete: "selesai"
+    practice: "latihan"
+    required: "wajib"
+    welcome_to_courses: "Para petualang, selamat datang di Kursus!"
+    ready_to_play: "Siap untuk bermain?"
+    start_new_game: "Memulai Permainan Baru"
+    play_now_learn_header: "Bermain sekarang untuk belajar"
+    play_now_learn_1: "sintaks dasar untuk mengontrol karaktermu"
+    play_now_learn_2: "perulangan untuk memecahkan puzzle yang menganggu"
+    play_now_learn_3: "strings & variabel-variabel untuk mengatur tindakan-tindakan"
+    play_now_learn_4: "bagaimana cara mengalahkan raksasa (keahlian hidup yang penting!)"
+    welcome_to_page: "Beranda Siswaku"
+    my_classes: "Kelas Saat Ini"
+    class_added: "Kelas berhasil ditambahkan!"
+    view_levels: "lihat semua level"
+    view_project_gallery: "lihat proyek teman kelasku"
+    join_class: "Bergabung Ke Kelas"
+    join_class_2: "Ikut Kelas"
+    ask_teacher_for_code: "Tanya ke gurumu jika kamu memiliki kode kelas CodeCombat! Jika iya, masukkan kode di:"
+    enter_c_code: "<Masukkan Kode Kelas>"
+    join: "Bergabung"
+    joining: "Ikuti kelas"
+    course_complete: "Kursus Selesai"
+    play_arena: "Arena Bermain"
+    view_project: "Lihat Proyek"
+    start: "Mulai"
+    last_level: "Level terakhir yang dimainkan"
+    not_you: "Bukan Kamu?"
+    continue_playing: "Lanjutkan Bermain"
+    option1_header: "Undang Siswa Melalui Email"
+    remove_student1: "Hapus Siswa"
+    are_you_sure: "Apakah anda yakin ingin menghapus siswa ini dari kelas ini?"
+    remove_description1: "Siswa akan kehilangan akses untuk kelas ini dan kelas yang diikuti. Perkembangan dan gameplay TIDAK hilang, dan siswa dapat dimasukkan kembali ke kelas kapanpun"
+    remove_description2: "Lisensi berbayar yang telah aktif tidak dapat dikembalikan."
+    license_will_revoke: "Lisensi berbayar siswa ini akan dicabut dan menjadi tersedia untuk diberikan ke siswa lain."
+    keep_student: "Simpan Siswa"
+    removing_user: "Menghapus siswa"
+    subtitle: "Mengulas ikhtisar kursus dan level" # Flat style redesign
+    changelog: "Melihat perubahan terakhir level kursus."
+    select_language: "Pilih bahasa"
+    select_level: "Pilih level"
+    play_level: "Mainkan Level"
+    concepts_covered: "Konsep tercakup"
+    view_guide_online: "Level Ikhtisar dan Solusi"
+    grants_lifetime_access: "Berikan akses ke semua Kursus."
+    enrollment_credits_available: "Lisensi Tersedia:"
+    language_select: "Pilih bahasa" # ClassroomSettingsModal
+    language_cannot_change: "Bahasa tidak dapat diganti setelah siswa bergabung ke kelas."
+    avg_student_exp_label: "Pengalaman Pemrograman Rata-rata Siswa"
+    avg_student_exp_desc: "Ini akan membantu kita mengerti bagaimana cara menjalankan kursus lebih baik."
+    avg_student_exp_select: "Pilih opsi terbaik"
+    avg_student_exp_none: "Belum Berpengalaman - sedikit atau belum berpengalaman"
+    avg_student_exp_beginner: "Pemula - memiliki beberapa pemaparan atau basis-blok"
+    avg_student_exp_intermediate: "Menengah - ada pengalaman dengan mengetik kode"
+    avg_student_exp_advanced: "Lanjutan - pengalaman luas dengan mengetik kode"
+    avg_student_exp_varied: "Level Variasi Pengalaman"
+    student_age_range_label: "Jarak Usia Siswa"
+    student_age_range_younger: "Lebih muda dari 6"
+    student_age_range_older: "Lebih tua dari 18"
+    student_age_range_to: "sampai"
+    create_class: "Buat Kelas"
+    class_name: "Nama Kelas"
+    teacher_account_restricted: "Akun kamu adalah akun guru dan tidak dapat mengakses konten siswa."
+    account_restricted: "Akun siswa diperlukan untuk mengakses halaman ini."
+    update_account_login_title: "Masuk atau perbaharui akunmu"
+    update_account_title: "Akunmu membutuhkan perhatian!"
+    update_account_blurb: "Sebelum kamu dapat mengakses kelasmu, pilihlah bagaimana kamu ingin menggunakan akun ini."
+    update_account_current_type: "Tipe Akun Saat Ini:"
+    update_account_account_email: "Akun Email/Username:"
+    update_account_am_teacher: "Saya adalah seorang guru"
+    update_account_keep_access: "Pertahankan akses ke kelas yang saya buat"
+    update_account_teachers_can: "Akun guru dapat:"
+    update_account_teachers_can1: "Membuat/mengatur/menambah kelas"
+    update_account_teachers_can2: "Tentukan/daftarkan siswa dalam kursus"
+    update_account_teachers_can3: "Membuka semua level kursus untuk mencoba"
+    update_account_teachers_can4: "Akses fitur terbaru hanya untuk guru ketika kita mengeluarkan fitur tersebut"
+    update_account_teachers_warning: "Perhatian: Kamu akan dihapus dari semua kelas yang kamu telah ikuti sebelumnya dan tidak akan dapat bermain kembali sebagai siswa."
+    update_account_remain_teacher: "Tetap Sebagai Guru"
+    update_account_update_teacher: "Perbaharui Sebagai Guru"
+    update_account_am_student: "Saya adalah seorang siswa"
+    update_account_remove_access: "Hapus akses ke kelas yang telah saya buat"
+    update_account_students_can: "Akun siswa dapat:"
+    update_account_students_can1: "Bergabung ke kelas"
+    update_account_students_can2: "Bermain melalui kursus sebagai siswa dan merekam proses milikmu"
+    update_account_students_can3: "Bersaing dengan teman kelas di arena"
+    update_account_students_can4: "Mengakses fitur baru hanya untuk siswa selagi kita mengeluarkan fitur tersebut"
+    update_account_students_warning: "Perhatian: Kamu tidak dapat mengatur kelas manapun yang telah kamu buat sebelumnya ataupun membuat kelas baru."
+    unsubscribe_warning: "Perhatian: Kamu akan berhenti berlangganan dari langganan bulananmu."
+    update_account_remain_student: "Tetap sebagai Siswa"
+    update_account_update_student: "Perbaharui sebagai Siswa"
+    need_a_class_code: "Kamu akan membutuhkan Kode Kelas untuk kelas yang kamu ikuti:"
+    update_account_not_sure: "Tidak yakin yang mana yang dipilih? Email"
+    update_account_confirm_update_student: "Apakah kamu yakin ingin memperbaharui akunmu menjadi sebuah pengalaman Siswa?"
+    update_account_confirm_update_student2: "Kamu akan tidak dapat mengatur kelas manapun yang kamu buat sebelumnya atau membuat kelas baru. Kelas yang terbuat sebelumnya olehmu akan dihapus dari CodeCombat dan tidak dapat dikembalikan."
+    instructor: "Pengajar: "
+    youve_been_invited_1: "Kamu telah diundang untuk bergabung "
+    youve_been_invited_2: ", dimana kamu akan belajar "
+    youve_been_invited_3: " dengan teman kelasmu di CodeCombat."
+    by_joining_1: "Dengan bergabung "
+    by_joining_2: "akan dapat membantu mengulang kata kunci kamu jika kamu lupa atau menghilangkannya. Kamu juga dapat melakukan verifikasi emailmu sehingga kamu dapat mengulang kata kuncimu sendiri!"
+    sent_verification: "Kami telah mengirim verifikasi email ke:"
+    you_can_edit: "Kamu dapat mengganti alamat emailmu di "
+    account_settings: "Pengaturan Akun"
+    select_your_hero: "Pilih Jagoan Kamu"
+    select_your_hero_description: "Kamu dapat selalu mengganti jagoanmu dengan pergi ke halaman Kursus dan memilih \"Ganti Jagoan\""
+    select_this_hero: "Pilih Jagoan Ini"
+    current_hero: "Jagoan Saat Ini:"
+    current_hero_female: "Jagoan Saat Ini:"
+    web_dev_language_transition: "Semua kelas program dalam HTML / JavaScript untuk kursus ini. Kelas yang telah menggunakan Python akan mulai dengan level pengenalan extra JavaScript untuk mempermudah transisi. Kelas yang telah menggunakan JavaScript akan melewati level pengenalan."
+    course_membership_required_to_play: "Kamu butuh bergabung dengan sebuah kursus untuk memainkan level ini."
+    license_required_to_play: "Tanyakan gurumu untuk memberikan lisensi ke kamu supaya kamu dapat melanjutkan bermain CodeCombat!"
+    update_old_classroom: "Tahun ajaran baru, level baru!"
+    update_old_classroom_detail: "Untuk memastikan kamu mendapatkan level paling baru, pastikan kamu membuat kelas baru untuk semester ini dengan menekan Buat Kelas Baru di "
+    teacher_dashboard: "beranda guru"
+    update_old_classroom_detail_2: "dan berikan siswa-siswa Kelas Kode yang baru muncul"
+    view_assessments: "Lihat Penilaian"
+    view_challenges: "lihat level tantangan"
+    challenge: "Tantangan:"
+    challenge_level: "Level Tantangan:"
+    status: "Status:"
+    assessments: "Penilaian"
+    challenges: "Tantangan"
+    level_name: "Nama Level:"
+    keep_trying: "Terus Mencoba"
+    start_challenge: "Memulai Tantangan"
+    locked: "Terkunci"
+    concepts_used: "Konsep yang Digunakan:"

 #  project_gallery:
 #    no_projects_published: "Be the first to publish a project in this course!"


Update ja.coffee

Update ja.coffee

--- app/locale/ja.coffee
+++ app/locale/ja.coffee
@@ -114,9 +114,9 @@
     log_in: "パスワードでログイン"
     required: "ログインする必要があります"
     login_switch: "すでにアカウントをお持ちですか？"
-#    school_name: "School Name and City"
-#    optional: "optional"
-#    school_name_placeholder: "Example High School, Springfield, IL"
+    school_name: "学校名と市名"
+    optional: "オプション"
+    school_name_placeholder: "エグザンプル高校, なんとか市, 日本"

   recover:
     recover_account_title: "パスワードを忘れた場合"
@@ -576,17 +576,17 @@
     josh_c_blurb: "ゲームのデザイン"
     carlos_title: "地域部長, ブラジル"
     carlos_blurb: "サラリーマン"
-#    maka_title: "Customer Advocate"
-#    maka_blurb: "Storyteller"
+    maka_title: "顧客主義"
+    maka_blurb: "ストーリーテラー"

   teachers:
     who_for_title: "CodeCombat が必要なひと"
     who_for_1: "私たちは CodeCombat を9歳以上の生徒にオススメしています。プログラミングの経験は必要ありません。" # {change}
     who_for_2: "私たちは男女問わず遊べるように CodeCombat をデザインしました。" # {change}
-#    more_info_title: "Where can I find more information?"
-#    more_info_1: "Our"
-#    more_info_2: "teachers forum"
-#    more_info_3: "is a good place to connect with fellow educators who are using CodeCombat."
+    more_info_title: "もっと情報がほしい人"
+    more_info_1: "私たちの"
+    more_info_2: "ティーチャーフォーラム"
+    more_info_3: "でCodeCombatを使っている教育者の仲間とつながりましょう。"

   teachers_survey:
 #    title: "Teacher Survey"
@@ -1211,7 +1211,7 @@
 #    games_played: "Games played"
     ratio: "比率  "
     leaderboard: "リーダーボード"
-#    battle_as: "Battle as "
+    battle_as: "対戦  "
     summary_your: "あなたの "
     summary_matches: "戦闘数 - "
     summary_wins: " 勝利数, "
@@ -1222,7 +1222,7 @@
     rank_submitted: "ランキングに送信されました。"
     rank_failed: "ランキングに送信できませんでした。"
     rank_being_ranked: "ランキングにのっています"
-#    rank_last_submitted: "submitted "
+    rank_last_submitted: "送信"
 #    help_simulate: "Help simulate games?"
 #    code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in."
 #    no_ranked_matches_pre: "No ranked matches for the "
@@ -1256,11 +1256,11 @@
     rules: "ルール"
     winners: "勝者"
     league: "リーグ"
-#    red_ai: "Red CPU" # "Red AI Wins", at end of multiplayer match playback
-#    blue_ai: "Blue CPU"
-#    wins: "Wins" # At end of multiplayer match playback
-#    humans: "Red" # Ladder page display team name
-#    ogres: "Blue"
+    red_ai: "レッドCPU" # "Red AI Wins", at end of multiplayer match playback
+    blue_ai: "ブルーCPU"
+    wins: "勝利" # At end of multiplayer match playback
+    humans: "レッド" # Ladder page display team name
+    ogres: "ブルー"

   user:
     stats: "ステータス"


Fix titlebar applicationDelegate instance

--- src/components/titlebar.cjsx
+++ src/components/titlebar.cjsx
@@ -1,5 +1,5 @@
 React = require 'react'
-ApplicationDelegate = require '../application-delegate'
+ApplicationDelegate = new (require '../application-delegate')

 class Titlebar extends React.Component
   @displayName = 'Titlebar'


Update `projects.cson` with new property key order

The project-manager package keeps changing these lines whenever the file
is checked/updated, and I'm getting sick of reverting them each time.

--- projects.cson
+++ projects.cson
@@ -1,51 +1,51 @@
 [
 	{
 		title: ".atom"
-		icon: "atom-icon"
 		paths: [
 			"~/.atom"
 		]
+		icon: "atom-icon"
 		devMode: true
 	}
 	{
 		title: ".files"
-		icon: "terminal-icon"
 		paths: [
 			"~/.files"
 		]
+		icon: "terminal-icon"
 		devMode: true
 	}
 	{
 		title: "language-roff"
-		icon: "manpage-icon"
 		paths: [
 			"~/Labs/language-roff"
 		]
+		icon: "manpage-icon"
 		devMode: true
 	}
 	{
 		title: "file-icons"
-		icon: "atom-icon"
 		paths: [
 			"~/Labs/file-icons/atom"
 			"~/Labs/file-icons/source"
 		]
+		icon: "atom-icon"
 		devMode: true
 	}
 	{
 		title: "Utils"
-		icon: "icon-tools"
 		paths: [
 			"~/Labs/Utils"
 		]
+		icon: "icon-tools"
 		devMode: true
 	}
 	{
 		title: "YASR"
-		icon: "emacs-icon"
 		paths: [
 			"~/Labs/YASR"
 		]
+		icon: "emacs-icon"
 		devMode: true
 	}
 ]


change style of mark crosshair

change style of mark crosshair

--- app/controllers/marking-tool.coffee
+++ app/controllers/marking-tool.coffee
@@ -19,16 +19,20 @@
     circle: 'move'

   initialize: ->
-    @hr = @addShape 'line', x1: 0, y1: -@size, x2: 0, y2: @size, stroke: "rgb(#{@color})", strokeWidth: 1
-    @vr = @addShape 'line', x1: -@size, y1: 0, x2: @size, y2: 0, stroke: "rgb(#{@color})", strokeWidth: 1
+    # @hr = @addShape 'line', x1: 0, y1: -@size, x2: 0, y2: @size, stroke: "rgb(#{@color})", strokeWidth: 1
+    @hr = @addShape 'line', x1: 0, y1: -@size, x2: 0, y2: -4, stroke: "rgb(255,215,0)", strokeWidth: 1
+    @hr = @addShape 'line', x1: 0, y1: 4, x2: 0, y2: @size, stroke: "rgb(255,215,0)", strokeWidth: 1
+
+    # @vr = @addShape 'line', x1: -@size, y1: 0, x2: @size, y2: 0, stroke: "rgb(#{@color})", strokeWidth: 1
+    @vr = @addShape 'line', x1: -@size, y1: 0, x2: -4, y2: 0, stroke: "rgb(#{@color})", strokeWidth: 1
+    @vr = @addShape 'line', x1: 4, y1: 0, x2: @size, y2: 0, stroke: "rgb(#{@color})", strokeWidth: 1
+
     @circle = @addShape 'circle', cx: 0, cy: 0, r: @size, fill: 'rgba(255, 215, 0, 0)'

   onInitialClick: (e) ->
-    # console.log 'MarkingTool: onInitialClick()' # STI
     @onInitialDrag e

   onInitialDrag: (e) ->
-    # console.log 'MarkingTool: onInitialDrag()' # STI
     @['on *drag circle'] e

   'on *drag circle': (e) =>


try to sent response after timeout

try to sent response after timeout

--- src/controllers/index.coffee
+++ src/controllers/index.coffee
@@ -161,21 +161,18 @@
           if (new Date(). getTime() - start_time) > 5000
             console.log 'waiting for too long... i will try again'
             get_from_flickr = true
-            console.log "Headers are already sent: " + res.headerSent
-            if get_from_db == true and not res.headerSent
+            if get_from_db == true
               res.send result

           else if counter == place_ids.length
             get_from_flickr = true
             console.log "Photos from flickr are ready; db status: " + get_from_db
-            console.log "Headers are already sent: " + res.headerSent
-            if get_from_db == true and not res.headerSent
+            if get_from_db == true
               res.send result

       catch e
         console.log e
-        if not res.headerSent
-          res.send result
+        res.send result

     console.log "I've sent all geo requests and now waiting for responses"


Remove slo-mo

Remove slo-mo

--- src/lazerscripts/stage1/camera_crew.coffee
+++ src/lazerscripts/stage1/camera_crew.coffee
@@ -48,7 +48,6 @@
           topDesaturation: 0.3
           bottomDesaturation: 0.3
         )
-        @slowMotionMoment()
         => Crafty.audio.play("explosion")
       )
       => @entity.flip('Y')


More tests.

More tests.

--- test/tests.coffee
+++ test/tests.coffee
@@ -11,7 +11,7 @@
 Redis = require 'ioredis'

-describe 'Chat service', ->
+describe 'Chat service.', ->

   states  = [ 'memory' , 'redis' ]

@@ -63,7 +63,7 @@

   states.forEach (state) ->

-    describe "State: #{state}", ->
+    describe "State: #{state}.", ->

       afterEach afterEachFn

@@ -109,47 +109,7 @@
             done()

-      describe 'Data management', ->
-
-        it 'should restore user state from onConnect hook', (done) ->
-          userName = 'user'
-          onConnect = (server, socket, cb) ->
-            cb null, userName, { whitelistOnly : true }
-          chatServer = new ChatService { port : port }
-          , { onConnect : onConnect }
-          , state
-          socket1 = ioClient.connect url1, makeParams(user1)
-          socket1.on 'loginConfirmed', (u) ->
-            expect(u).equal(userName)
-            chatServer.chatState.getOnlineUser userName, (error, u) ->
-              expect(u.username).equal(userName)
-              u.directMessagingState.whitelistOnlyGet (error, data) ->
-                expect(data).true
-                done()
-
-        it 'should restore room state from onStart hook', (done) ->
-          room = null
-          msg1 = { author : user1, textMessage : "message", timestamp : 0 }
-          fn = ->
-            chatServer.chatState.getRoom 'roomName', (error, r) ->
-              expect(r.name).equal(room.name)
-              r.roomState.getList 'whitelist', (error, list) ->
-                expect(list).include(user1)
-                r.roomState.messagesGet (error, data) ->
-                  expect(data).instanceof(Array)
-                  expect(data[0]).deep.equal(msg1)
-                  done()
-          onStart = (server, cb) ->
-            room = new Room server, 'roomName'
-            room.initState { whitelist : [ user1 ]
-              , owner : user2
-              , lastMessages : [ msg1 ] }
-            , ->
-              server.chatState.addRoom room, ->
-                cb null, room
-                fn()
-          chatServer = new ChatService { port : port }
-          , { onStart : onStart }, state
+      describe 'User management', ->

         it 'should support server side user disconnection', (done) ->
           chatServer = new ChatService { port : port }, null, state
@@ -241,7 +201,7 @@
             chatServer = null

-      describe 'Rooms', ->
+      describe 'Room management', ->

         it 'should create and delete rooms', (done) ->
           chatServer = new ChatService { port : port
@@ -261,6 +221,19 @@
                       expect(data).empty
                       done()

+        it 'should check existing rooms before adding new ones', (done) ->
+          chatServer = new ChatService { port : port }, null, state
+          chatServer.chatState.addRoom roomName1, ->
+            chatServer.chatState.addRoom roomName1, (error, data) ->
+              expect(error).ok
+              done()
+
+        it 'should check existing rooms before removing a room', (done) ->
+          chatServer = new ChatService { port : port }, null, state
+          chatServer.chatState.removeRoom roomName1, (error, data) ->
+            expect(error).ok
+            done()
+
         it 'should reject room management when option is disabled'
         , (done) ->
           chatServer = new ChatService { port : port }, null, state
@@ -274,18 +247,38 @@
                   expect(error).ok
                   done()

-        it 'should check existing rooms before adding new ones', (done) ->
-          chatServer = new ChatService { port : port }, null, state
-          chatServer.chatState.addRoom roomName1, ->
-            chatServer.chatState.addRoom roomName1, (error, data) ->
-              expect(error).ok
-              done()
-
-        it 'should check existing rooms before removing a room', (done) ->
-          chatServer = new ChatService { port : port }, null, state
-          chatServer.chatState.removeRoom roomName1, (error, data) ->
-            expect(error).ok
-            done()
+        it 'should list all rooms', (done) ->
+          chatServer = new ChatService { port : port }, null, state
+          room1 = new Room chatServer, roomName1
+          room1.initState { whitelistOnly : true }, ->
+            room2 = new Room chatServer, roomName2
+            chatServer.chatState.addRoom room1, ->
+              chatServer.chatState.addRoom room2, ->
+                socket1 = ioClient.connect url1, makeParams(user1)
+                socket1.on 'loginConfirmed', ->
+                  socket1.emit 'listRooms', (error, data) ->
+                    expect(data).an('array')
+                    expect(data).include(roomName2)
+                    expect(data).include(roomName1)
+                    done()
+
+        it 'should send access remove on room delete', (done) ->
+          chatServer = new ChatService { port : port
+            , enableRoomsManagement : true }
+          , null, state
+          room = new Room chatServer, roomName1
+          room.roomState.ownerSet user1, ->
+            chatServer.chatState.addRoom room, ->
+              socket1 = ioClient.connect url1, makeParams(user1)
+              socket1.on 'loginConfirmed', ->
+                socket1.emit 'roomJoin', roomName1, (error, data) ->
+                  socket1.emit 'roomDelete', roomName1
+                  socket1.once 'roomAccessRemoved', (r) ->
+                    expect(r).equal(roomName1)
+                    done()
+
+
+      describe 'Room messaging', ->

         it 'should emit join and leave for user\'s sockets', (done) ->
           chatServer = new ChatService { port : port }, null, state
@@ -368,6 +361,9 @@
                     ]
                     , -> done()

+
+      describe 'Room permissions', ->
+
         it 'should reject room messages from not joined users', (done) ->
           txt = 'Test message.'
           message = { textMessage : txt }
@@ -381,21 +377,6 @@
                 socket1.emit 'roomHistory', roomName1, (error, data) ->
                   expect(error).ok
                   done()
-
-        it 'should list all rooms', (done) ->
-          chatServer = new ChatService { port : port }, null, state
-          room1 = new Room chatServer, roomName1
-          room1.initState { whitelistOnly : true }, ->
-            room2 = new Room chatServer, roomName2
-            chatServer.chatState.addRoom room1, ->
-              chatServer.chatState.addRoom room2, ->
-                socket1 = ioClient.connect url1, makeParams(user1)
-                socket1.on 'loginConfirmed', ->
-                  socket1.emit 'listRooms', (error, data) ->
-                    expect(data).an('array')
-                    expect(data).include(roomName2)
-                    expect(data).include(roomName1)
-                    done()

         it 'should send whitelistonly mode', (done) ->
           chatServer = new ChatService { port : port }, null, state
@@ -421,7 +402,6 @@
                   expect(data).include(user1)
                   done()

-
         it 'should reject send lists to not joined users', (done) ->
           chatServer = new ChatService { port : port }, null, state
           room = new Room chatServer, roomName1
@@ -445,7 +425,35 @@
                   expect(error).ok
                   done()

-        it 'should allow list modify for admins', (done) ->
+        it 'should check for existing user names on adding' , (done) ->
+          chatServer = new ChatService { port : port
+            , enableRoomsManagement : true }
+          , null, state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', (u) ->
+            socket1.emit 'roomCreate', roomName1, false, (error, data) ->
+              socket1.emit 'roomJoin',  roomName1, (error, data) ->
+                socket1.emit 'roomAddToList', roomName1, 'adminlist', [user2]
+                , (error, data) ->
+                  socket1.emit 'roomAddToList', roomName1, 'adminlist', [user2]
+                  , (error, data) ->
+                    expect(error).ok
+                    done()
+
+        it 'should check for existing list names on deleting' , (done) ->
+          chatServer = new ChatService { port : port
+            , enableRoomsManagement : true }
+          , null, state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', (u) ->
+            socket1.emit 'roomCreate', roomName1, false, (error, data) ->
+              socket1.emit 'roomJoin',  roomName1, (error, data) ->
+                socket1.emit 'roomRemoveFromList', roomName1, 'adminlist'
+                , [user2], (error, data) ->
+                  expect(error).ok
+                  done()
+
+        it 'should allow wl and bl modifications for admins', (done) ->
           chatServer = new ChatService { port : port }, null, state
           room = new Room chatServer, roomName1
           room.roomState.addToList 'adminlist', [user1], ->
@@ -463,20 +471,7 @@
                           expect(data).not.include(user2)
                           done()

-        it 'should reject userlist modifications', (done) ->
-          chatServer = new ChatService { port : port }, null, state
-          room = new Room chatServer, roomName1
-          room.roomState.addToList 'adminlist', [user1], ->
-            chatServer.chatState.addRoom room, ->
-              socket1 = ioClient.connect url1, makeParams(user1)
-              socket1.on 'loginConfirmed', ->
-                socket1.emit 'roomJoin',  roomName1, (error, data) ->
-                  socket1.emit 'roomAddToList', roomName1, 'userlist', [user2]
-                  , (error, data) ->
-                    expect(error).ok
-                    done()
-
-        it 'should reject admin list modifications for admins', (done) ->
+        it 'should reject adminlist modifications for admins', (done) ->
           chatServer = new ChatService { port : port }, null, state
           room = new Room chatServer, roomName1
           room.roomState.addToList 'adminlist', [user1,user2], ->
@@ -492,7 +487,39 @@
                         expect(error).ok
                         done()

-        it 'should reject lists modify for users', (done) ->
+        it 'should reject list modifications with owner for admins'
+        , (done) ->
+          chatServer = new ChatService { port : port
+            , enableRoomsManagement : true }
+          , null, state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', (u) ->
+            socket1.emit 'roomCreate', roomName1, false, (error, data) ->
+              socket1.emit 'roomJoin',  roomName1, (error, data) ->
+                socket1.emit 'roomAddToList', roomName1, 'adminlist', [user2],
+                (error, data) ->
+                  socket2 = ioClient.connect url1, makeParams(user2)
+                  socket2.on 'loginConfirmed', ->
+                    socket2.emit 'roomJoin',  roomName1, (error, data) ->
+                      socket2.emit 'roomAddToList', roomName1, 'whitelist'
+                      , [user1], (error, data) ->
+                        expect(error).ok
+                        done()
+
+        it 'should reject userlist modifications', (done) ->
+          chatServer = new ChatService { port : port }, null, state
+          room = new Room chatServer, roomName1
+          room.roomState.addToList 'adminlist', [user1], ->
+            chatServer.chatState.addRoom room, ->
+              socket1 = ioClient.connect url1, makeParams(user1)
+              socket1.on 'loginConfirmed', ->
+                socket1.emit 'roomJoin',  roomName1, (error, data) ->
+                  socket1.emit 'roomAddToList', roomName1, 'userlist', [user2]
+                  , (error, data) ->
+                    expect(error).ok
+                    done()
+
+        it 'should reject lists modifications for non-admins', (done) ->
           chatServer = new ChatService { port : port }, null, state
           room = new Room chatServer, roomName1
           chatServer.chatState.addRoom room, ->
@@ -500,6 +527,18 @@
             socket1.on 'loginConfirmed', ->
               socket1.emit 'roomJoin', roomName1, (error, data) ->
                 socket1.emit 'roomAddToList', roomName1, 'adminlist', [user2]
+                , (error, data) ->
+                  expect(error).ok
+                  done()
+
+        it 'should reject mode changes for non-admins', (done) ->
+          chatServer = new ChatService { port : port }, null, state
+          room = new Room chatServer, roomName1
+          chatServer.chatState.addRoom room, ->
+            socket1 = ioClient.connect url1, makeParams(user1)
+            socket1.on 'loginConfirmed', ->
+              socket1.emit 'roomJoin', roomName1, (error, data) ->
+                socket1.emit 'roomSetWhitelistMode', roomName1, true
                 , (error, data) ->
                   expect(error).ok
                   done()
@@ -583,21 +622,6 @@
                             expect(r).equal(roomName1)
                             done()

-        it 'should send access remove on room delete', (done) ->
-          chatServer = new ChatService { port : port
-            , enableRoomsManagement : true }
-          , null, state
-          room = new Room chatServer, roomName1
-          room.roomState.ownerSet user1, ->
-            chatServer.chatState.addRoom room, ->
-              socket1 = ioClient.connect url1, makeParams(user1)
-              socket1.on 'loginConfirmed', ->
-                socket1.emit 'roomJoin', roomName1, (error, data) ->
-                  socket1.emit 'roomDelete', roomName1
-                  socket1.once 'roomAccessRemoved', (r) ->
-                    expect(r).equal(roomName1)
-                    done()
-
         it 'should remove disconnected users' , (done) ->
           chatServer = new ChatService { port : port
             , enableUserlistUpdates : true }
@@ -616,7 +640,8 @@
                       expect(u).equal(user2)
                       done()

-      describe 'Direct', ->
+
+      describe 'Direct messaging', ->

         it 'should send direct messages', (done) ->
           txt = 'Test message.'
@@ -666,6 +691,9 @@
                   expect(msg?.textMessage).equal(txt)
                   done()

+
+      describe 'Direct permissions', ->
+
         it 'should check user permission', (done) ->
           txt = 'Test message.'
           message = { textMessage : txt }
@@ -693,13 +721,19 @@
           socket1.on 'loginConfirmed', ->
             socket2 = ioClient.connect url1, makeParams(user2)
             socket2.on 'loginConfirmed', ->
-              chatServer.chatState.getOnlineUser user2,  (error, user) ->
+              chatServer.chatState.getOnlineUser user2, (error, user) ->
                 user.directMessagingState.whitelistOnlySet true, ->
                   user.directMessagingState.addToList 'whitelist', [user1], ->
                     socket1.emit 'directMessage', user2, message
                     , (error, data) ->
                       expect(error).not.ok
-                      done()
+                      expect(data.textMessage).equal(txt)
+                      user.directMessagingState.removeFromList 'whitelist'
+                      , [user1], ->
+                        socket1.emit 'directMessage', user2, message
+                        ,(error, data) ->
+                          expect(error).ok
+                          done()

         it 'should allow user to modify own lists', (done) ->
           chatServer = new ChatService { port : port
@@ -732,6 +766,31 @@
               expect(error).ok
               done()

+        it 'should check for existing user names on deleting' , (done) ->
+          chatServer = new ChatService { port : port
+            , enableDirectMessages : true }
+          , null, state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', ->
+            socket1.emit 'directRemoveFromList', 'blacklist', [user2]
+            , (error, data) ->
+              expect(error).ok
+              done()
+
+        it 'should check for existing list names on adding' , (done) ->
+          chatServer = new ChatService { port : port
+            , enableDirectMessages : true }
+          , null, state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', ->
+            socket1.emit 'directAddToList', 'blacklist', [user2]
+            , (error, data) ->
+              expect(error).not.ok
+              socket1.emit 'directAddToList', 'blacklist', [user2]
+              , (error, data) ->
+                expect(error).ok
+                done()
+
         it 'should allow user to modify own mode', (done) ->
           chatServer = new ChatService { port : port
             , enableDirectMessages : true }
@@ -744,7 +803,58 @@
                 expect(data).true
                 done()

-      describe 'Other', ->
+      describe 'Hooks', ->
+
+        it 'should restore user state from onConnect hook', (done) ->
+          userName = 'user'
+          onConnect = (server, socket, cb) ->
+            cb null, userName, { whitelistOnly : true }
+          chatServer = new ChatService { port : port }
+          , { onConnect : onConnect }
+          , state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', (u) ->
+            expect(u).equal(userName)
+            chatServer.chatState.getOnlineUser userName, (error, u) ->
+              expect(u.username).equal(userName)
+              u.directMessagingState.whitelistOnlyGet (error, data) ->
+                expect(data).true
+                done()
+
+        it 'should restore room state from onStart hook', (done) ->
+          room = null
+          msg1 = { author : user1, textMessage : "message", timestamp : 0 }
+          fn = ->
+            chatServer.chatState.getRoom 'roomName', (error, r) ->
+              expect(r.name).equal(room.name)
+              r.roomState.getList 'whitelist', (error, list) ->
+                expect(list).include(user1)
+                r.roomState.messagesGet (error, data) ->
+                  expect(data).instanceof(Array)
+                  expect(data[0]).deep.equal(msg1)
+                  done()
+          onStart = (server, cb) ->
+            expect(server).instanceof(ChatService)
+            room = new Room server, 'roomName'
+            room.initState { whitelist : [ user1 ]
+              , owner : user2
+              , lastMessages : [ msg1 ] }
+            , ->
+              server.chatState.addRoom room, ->
+                cb null, room
+                fn()
+          chatServer = new ChatService { port : port }
+          , { onStart : onStart }, state
+
+        it 'should exectute onClose hook', (done) ->
+          err = 'error'
+          closeHook = (server, error, cb) ->
+            expect(server).instanceof(ChatService)
+            expect(error).equal(err)
+            cb()
+          chatServer1 = new ChatService { port : port }
+          , { onClose : closeHook }, state
+          chatServer1.close done, err

         it 'should execute before and after messages hooks', (done) ->
           someData = 'data'
@@ -811,6 +921,8 @@
           , { serverErrorHook : fn }, state
           chatServer.errorBuilder.handleServerError error

+      describe 'Various', ->
+
         it 'should return raw error objects', (done) ->
           chatServer = new ChatService { port : port
           , useRawErrorObjects : true },
@@ -823,13 +935,23 @@
               expect(error.args[0]).equal('room1')
               done()

-        it 'should validate message arguments', (done) ->
+        it 'should validate message argument types', (done) ->
           chatServer = new ChatService {port : port
             , enableRoomsManagement : true},
           null, state
           socket1 = ioClient.connect url1, makeParams(user1)
           socket1.on 'loginConfirmed', (u) ->
             socket1.emit 'roomCreate', null, false, (error, data) ->
+              expect(error).ok
+              done()
+
+        it 'should validate message argument count', (done) ->
+          chatServer = new ChatService {port : port
+            , enableRoomsManagement : true},
+          null, state
+          socket1 = ioClient.connect url1, makeParams(user1)
+          socket1.on 'loginConfirmed', (u) ->
+            socket1.emit 'roomCreate', (error, data) ->
               expect(error).ok
               done()


fix bootstrap update consequences

fix bootstrap update consequences

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -5,12 +5,12 @@
 #FIXME: jsplumb have not main js file. need to do something
 #= require jsPlumb/dist/js/jquery.jsPlumb-1.7.2
 #
-#= require bootstrap-sass-official/affix
-#= require bootstrap-sass-official/transition
-#= require bootstrap-sass-official/alert
-#= require bootstrap-sass-official/collapse
-#= require bootstrap-sass-official/button
-#= require bootstrap-sass-official/modal
+#= require bootstrap-sass-official/bootstrap/affix
+#= require bootstrap-sass-official/bootstrap/transition
+#= require bootstrap-sass-official/bootstrap/alert
+#= require bootstrap-sass-official/bootstrap/collapse
+#= require bootstrap-sass-official/bootstrap/button
+#= require bootstrap-sass-official/bootstrap/modal
 #
 #= require lodash
 #= require moment


Explicit require of cyt.js so we can properly override it in bookyt.js.

Fixes position updates in sortable lists like salary templates.

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -13,6 +13,7 @@
 #= require accounting
 #= require accounting-jquery
 #= require bootstrap-datepicker
+#= require cyt
 #= require_self
 #= require bookyt/table_select
 #= require_tree .


#35 Triggerin multiple event error fixed

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -11,7 +11,6 @@
 # GO AFTER THE REQUIRES BELOW.
 #
 #= require jquery
-#= require jquery.turbolinks
 #= require jquery_ujs
 #= require jquery.ui.autocomplete
 #= require jquery.ui.sortable


issue 1689: fix menu highlighting with browser back/forward buttons

Pjax only runs inline JS once, so the model highlight on the left nav
didn't change when users navigated with the back and forward browser
buttons.  Added some code in ui.coffee to run code inside script tags
when pjax notifies of a "popstate" event.

--- app/assets/javascripts/rails_admin/ui.coffee
+++ app/assets/javascripts/rails_admin/ui.coffee
@@ -75,3 +75,15 @@
     $(elems).prop('checked', true)
   else
     $(elems).prop('checked',false)
+
+# when the user hits the back button, the inline JS <script> that
+# highlights the current model in the left menu doesn't get run by
+# pjax, so this code runs it:
+# https://github.com/defunkt/jquery-pjax/issues/241#issuecomment-13251065
+$(document).on 'pjax:popstate', () ->
+  $(document).one 'pjax:end', (event) ->
+    $(event.target).find('script').each () ->
+      $.globalEval(this.text || this.textContent || this.innerHTML || '')
+      return
+    return
+  return


Add path as a derived animation property

--- framer/Animation.coffee
+++ framer/Animation.coffee
@@ -430,14 +430,17 @@

 		# Only animate numeric properties for now
 		for k, v of properties
-			if k in ["frame", "size", "point", "midPoint"] # Derived properties
+			if k in ["frame", "size", "point", "midPoint", "path"] # Derived properties
 				switch k
 					when "frame" then derivedKeys = ["x", "y", "width", "height"]
 					when "size" then derivedKeys = ["width", "height"]
 					when "point" then derivedKeys = ["x", "y"]
 					when "midPoint" then derivedKeys = ["midX", "midY"]
+					when "path" then derivedKeys = ["x", "y", "rotation"]
 					else derivedKeys = []
 				if SVG.isPath(v)
+					if k is "path"
+						layer.midPoint = v.start
 					for derivedKey in derivedKeys
 						animatableProperties[derivedKey] = v
 				else if _.isObject(v)


Don't clip TimePoint by default

Don't clip TimePoint by default

But do it selectively some places.  Fixes #3480

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -32,12 +32,7 @@
             else
               p = @p
               @_o =
-                if p >= 0 && p < 1
-                  Math.round(ruler.range.l + p * (ruler.range.u - ruler.range.l))
-                else if p < 0
-                  -Infinity
-                else if p >= 1
-                  Infinity
+                Math.round(ruler.range.l + p * (ruler.range.u - ruler.range.l))
           set: (o) ->
             return if o == @_o
             @_o = o
@@ -48,11 +43,7 @@
           get: ->
             if '_o' of @
               o = @_o
-              @_p =
-                if isFinite o
-                  (o - ruler.range.base) / (ruler.range.u - ruler.range.l)
-                else
-                  o
+              @_p = (o - ruler.range.base) / (ruler.range.u - ruler.range.l)
             else if '_x' of @
               x = @_x
               tlr = tl.getBoundingClientRect()
@@ -80,7 +71,15 @@
             delete @_p
             return

-      defined: () ->
+      clip: ->
+        p = @p
+        if p > 1
+          @p = Infinity
+        if p < 0
+          @p = -Infinity
+        this
+
+      defined: ->
         isFinite(@o)

       minus: (t) ->
@@ -427,6 +426,7 @@

       startPos = down.position ?= new TimePoint(down.clientX, 'x')
       endPos = new TimePoint(up.clientX, 'x')
+      endPos.clip()
       ruler.selection =
         if startPos.x < endPos.x
           new TimeSegment(startPos, endPos)
@@ -888,7 +888,14 @@
             return

       drag: (event, which) ->
-        p = this[if which then 'ut' else 'lt'] = @snapping(x = new TimePoint(event.clientX, 'x'))
+        x = new TimePoint(event.clientX, 'x')
+        p = this[if which then 'ut' else 'lt'] =
+          if which && x.p > 1
+            new TimePoint(Infinity)
+          else if !which && x.p < 0
+            new TimePoint(-Infinity)
+          else
+            @snapping(x)
         if event.type != 'mousemove'
           $scope.form.position.$setDirty()
         return


Show notes whitout numbers

Show notes whitout numbers

--- lib/caniuse-view.coffee
+++ lib/caniuse-view.coffee
@@ -148,9 +148,14 @@

       break unless i++ < 10

-    notes = Object.keys(item.notes_by_num)
+    notes = []
+    notes.push(markdown.toHTML(item.notes)) if item.notes
+
+    notesByNumber = Object.keys(item.notes_by_num)
       .filter (i) => needNotes.indexOf(i) isnt -1
-      .map (i) => markdown.toHTML(repeat('\\*', i) + ' ' + item.notes_by_num[i])
+      .map (i) => markdown.toHTML("#{i} #{item.notes_by_num[i]}")
+
+    notes = notes.concat(notesByNumber)

     @notes
       .html('')


support 'use strict'

support 'use strict'

--- app/js/jasmine-stealth.coffee
+++ app/js/jasmine-stealth.coffee
@@ -1,5 +1,5 @@
 ( ->
-  root = this
+  root = `(1, eval)('this')`

   #private helpers


restored Wallet.favorites

restored Wallet.favorites

--- app/js/services/wallet.coffee
+++ app/js/services/wallet.coffee
@@ -1,6 +1,7 @@
 class Wallet

     accounts: {}
+    favorites: {}

     balances: {}
     bonuses: {}
@@ -141,12 +142,14 @@
         acct.registered = val.registration_date and val.registration_date != "1970-01-01T00:00:00"
         #console.log "populate_account",acct.name
         @accounts[acct.name] = acct
+        @favorites[acct.name] = acct if acct.is_favorite
         return acct

     refresh_account: (name) ->
         @wallet_api.get_account(name).then (result) => # TODO no such acct?
             @populate_account(result)
             @refresh_balances()
+            return @accounts[name]

     refresh_accounts: () ->
         @refresh_accounts_request = on
@@ -301,7 +304,7 @@

     refresh_transactions: () ->
         return @transactions_loading_promise if @transactions_loading_promise
-        #console.log "------ refresh_transactions ------>"
+        console.log "------ refresh_transactions ------>", getStackTrace()
         deffered = @q.defer()

         @transactions_loading_promise = deffered.promise
@@ -321,7 +324,7 @@

             account_transaction_history_promise.then (result) =>
                 for val in result
-                    #console.log "------ account_transaction ------>", val
+                    console.log "------ account_transaction ------>", val
                     @transactions_last_block = val.block_num
                     @process_transaction(val) if val.is_confirmed


refactor - destructuring

refactor - destructuring

--- src/constructor.coffee
+++ src/constructor.coffee
@@ -1,16 +1,17 @@
 tools = require './tools'
+{pairs, keys, values, object, resolveAll, isPrimitive, isArray, objectCreate, proc} = tools

 resolveCompletely = (unresolved) ->
-  tools.resolveAll([unresolved]).then ([resolved]) ->
+  resolveAll([unresolved]).then ([resolved]) ->

-    return resolved if !resolved? || tools.isPrimitive(resolved)
-    return tools.resolveAll(resolved.map(resolveCompletely)) if tools.isArray(resolved)
+    return resolved if !resolved? || isPrimitive(resolved)
+    return resolveAll(resolved.map(resolveCompletely)) if isArray(resolved)

-    unresolvedKeys = tools.resolveAll(tools.keys(resolved))
-    unresolvedValues = tools.resolveAll(tools.values(resolved).map(resolveCompletely))
+    unresolvedKeys = resolveAll(keys(resolved))
+    unresolvedValues = resolveAll(values(resolved).map(resolveCompletely))

-    tools.resolveAll([unresolvedKeys, unresolvedValues]).then ([resolvedKeys, resolvedValues]) ->
-      tools.object(resolvedKeys, resolvedValues)
+    resolveAll([unresolvedKeys, unresolvedValues]).then ([resolvedKeys, resolvedValues]) ->
+      object(resolvedKeys, resolvedValues)

 overrides = ['get']
@@ -22,22 +23,22 @@

   Z = (obj) ->
     resolvedObject = resolveCompletely(obj)
-    overrideLayer = tools.objectCreate(resolvedObject)
-    resultingPromise = tools.objectCreate(overrideLayer)
+    overrideLayer = objectCreate(resolvedObject)
+    resultingPromise = objectCreate(overrideLayer)

     overrides.forEach (name) ->
       overrideLayer[name] = (args...) ->
         Z resolvedObject[name].apply(this, args)

-    tools.pairs(mixedIn).forEach ([name, func]) ->
+    pairs(mixedIn).forEach ([name, func]) ->
       resultingPromise[name] = (args...) ->
         Z resultingPromise.then (resolved) ->
           func.apply({ value: resolved }, args)

     resultingPromise

-  Z.mixin = tools.proc (hash) ->
-    tools.pairs(hash).forEach ([name, func]) ->
+  Z.mixin = proc (hash) ->
+    pairs(hash).forEach ([name, func]) ->
       mixedIn[name] = func

   Z


Removed .coffee extension to let Travis pass

--- src/mixins/validatable-http-message.coffee
+++ src/mixins/validatable-http-message.coffee
@@ -1,7 +1,7 @@
 async = require 'async'

 errors = require '../errors'
-{extendable} = require '../utils/extendable.coffee'
+{extendable} = require '../utils/extendable'

 validators = require '../validators'


require config

require config

--- services/engine.coffee
+++ services/engine.coffee
@@ -1,6 +1,7 @@
 _ = require 'underscore'
 somata = require 'somata'
 announce = require 'nexus-announce'
+config = require '../config'

 somata_client = new somata.Client
 DataService = somata_client.bindRemote 'maia:data'


trackJs custom errors

trackJs custom errors

--- jsapp/xlform_model_view/view.surveyApp.coffee
+++ jsapp/xlform_model_view/view.surveyApp.coffee
@@ -40,9 +40,9 @@

       _s = (i)-> JSON.stringify(i)
       if _s(rIds) isnt _s(elIds)
-        console?.error "Order do not match"
-        console?.error _s(rIds)
-        console?.error _s(elIds)
+        trackJs?.console.log _s(rIds)
+        trackJs?.console.log _s(elIds)
+        trackJs?.console.error("Row model does not match view")
         false
       else
         true
@@ -579,7 +579,7 @@
           if parent.constructor.kls == "Group" && parent.rows.length == 0
             parent_view = @__rowViews.get(parent.cid)
             if !parent_view
-              trackJs?.console.warn("parent view is not defined", matchingRow.get('name').get('value'))
+              trackJs?.console.error("parent view is not defined", matchingRow.get('name').get('value'))
             parent_view._deleteGroup()
         @set_multioptions_label()


use all ref_dates in each article

use all ref_dates in each article

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -4,9 +4,9 @@
     .controller 'MainCtrl', ($scope, $http, $filter, Restangular) ->
         today = $scope.today = new Date()
         month = $filter('date')(today, "MMMM")
-        monthDigit = $filter('date')(today, "MM")
-        year  = $filter('date')(today, "yyyy")
-        day = $filter('date')(today, "dd")
+        monthDigit = parseInt $filter('date')(today, "MM")
+        year  = parseInt $filter('date')(today, "yyyy")
+        day = parseInt $filter('date')(today, "dd")
         $scope.month = []
         $scope.year  = []
         # Date information
@@ -21,18 +21,19 @@
           _.map data._items, (article) =>
             if not article.ref_dates[0]?
               return
-            a_year = article.ref_dates[0].date[0]
-            if a_year is year
-              a_month = article.ref_dates[0].date[1]
-              a_day = article.ref_dates[0].date[2]
-              article['reference_date'] = new Date a_year, a_month, a_day
-              if a_month is monthDigit
-                if a_day is day
-                  $scope.days.push article
-                else if not a_day?
-                  $scope.month.push article
-              else if not a_month?
-                $scope.year.push article
+            _.map article.ref_dates, (ref_date, index) =>
+              a_year = ref_date.date[0]
+              if a_year is year
+                a_month = ref_date.date[1]
+                a_day = ref_date.date[2]
+                article['reference_date'] = new Date a_year, a_month, a_day
+                if a_month is monthDigit
+                  if a_day is day
+                    $scope.days.push article
+                  else if not a_day?
+                    $scope.month.push article
+                else if not a_month?
+                  $scope.year.push article
         $scope.active  = -1
         $scope.article = null
         $scope.previewStyle = ->


Fix: デフォルトリポジトリの修正

Fix: デフォルトリポジトリの修正

--- src/core/config.coffee
+++ src/core/config.coffee
@@ -29,7 +29,7 @@
  * @const
  ###
 DEFAULT_DATA =
-  repos: ["subdiox/BMRepository/PC"]
+  repos: ["BlitzModder/BMRepository/master"]
   localRepos: []
   debugRepo: ""
   appliedMods: []


remove extra conditional

remove extra conditional

--- src/core/format.coffee
+++ src/core/format.coffee
@@ -89,7 +89,7 @@
       else
         dom(node).wrap(formatNode)
         node = formatNode
-    if _.isString(@config.style) or _.isString(@config.attribute) or _.isString(@config.class) or _.isString(@config.className)
+    if _.isString(@config.style) or _.isString(@config.attribute) or _.isString(@config.class)
       if _.isString(@config.class)
         node = this.remove(node)
       if dom(node).isTextNode()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -163,7 +163,7 @@
               I.includedModules.push moduleName
               self.extend Module(I, self)
           else
-            warn "Unable to discover name for module: ", Module, "\n"
+            warn "Unable to discover name for module: ", Module, "\nSerialization issues may occur"
             self.extend Module(I, self)

         return self


Rotating turtle

Rotating turtle

--- src/draw_turtle.coffee
+++ src/draw_turtle.coffee
@@ -28,7 +28,7 @@
     stroke: "green"
     transform: (world, args) ->
       scale = world.get('scale')
-      x = world.get('i') + 0.4 * scale
+      x = world.get('i') + 0.5 * scale
       y = world.get('j') + 0.5 * scale
       # TODO: perform real triginometry
       angle = 90*(1-world.get('v_i')) #0, 90, 180, 90, 0
@@ -43,10 +43,10 @@
       ]
     children: {
       ME: {
-        i: 3
-        j: 3
-        v_i: 1
-        v_j: 0
+        i: 4
+        j: 4
+        v_i: 0
+        v_j: -1
         fill: "#88ff88"
       }
     }


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/emitterable.coffee
+++ src/emitterable.coffee
@@ -18,7 +18,7 @@
       duration: 30
       includedModules: ["Movable"]
       height: 2
-      maxSpeed: 60
+      maxSpeed:
       offset: Point(0, 0)
       sprite: false
       spriteName: false


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_game_state.coffee
+++ src/engine_game_state.coffee
@@ -51,7 +51,7 @@
       else
         I.currentState.cameras()
     fadeIn: (options={}) ->
-      self.cameras.invoke('fadeIn', options)
+      self.cameras().invoke('fadeIn', options)
     fadeOut: (options={}) ->
       self.cameras.invoke('fadeOut', options)
     flash: (options={}) ->


webapp/project log: fix sorting of log entries, especially when searching

--- src/smc-webapp/project_log.cjsx
+++ src/smc-webapp/project_log.cjsx
@@ -23,6 +23,7 @@
 misc_page = require('./misc_page')
 underscore = require('underscore')
 immutable  = require('immutable')
+lodash = require('lodash')

 {React, ReactDOM, Actions, Store, Table, rtypes, rclass, Redux}  = require('./app-framework')
 {Col, Row, Button, ButtonGroup, ButtonToolbar, FormControl, FormGroup, InputGroup, Panel, Well} = require('react-bootstrap')
@@ -366,7 +367,7 @@
         cursor     : rtypes.string    # id of the cursor

     render_entries: ->
-        for x, i in @props.log
+        for x, i in lodash.sortBy(@props.log, (el) -> -el.time)
             <LogEntry
                 key             = {x.id}
                 cursor          = {@props.cursor==x.id}
@@ -489,7 +490,7 @@
                             @process_log_entry(x)
                             break
             if new_log.length > 1
-                new_log.sort((a,b) -> misc.cmp(b.time, a.time))
+                new_log = lodash.sortBy(new_log, (el) -> -el.time)
                 # combine redundant subsequent events that differ only by time
                 v = []
                 for i in [1...new_log.length]


rename async => useAsyncApi

rename async => useAsyncApi

--- src/processors/job/notification.api.coffee
+++ src/processors/job/notification.api.coffee
@@ -18,7 +18,7 @@
   success: (response, options) =>
     { statusCode } = response;
     __makeRequest = () => @_makeRequest { statusCode, success: yes }, options
-    __retryRequest = () => @success(response, { async: true })
+    __retryRequest = () => @success(response, { useAsyncApi: true })

     @_retryViaAsyncOrIgnore(__makeRequest, __retryRequest, options)

@@ -26,7 +26,7 @@
     { statusCode, error, request } = response
     message = _.get error, "message"
     __makeRequest = () => @_makeRequest { statusCode, success: no, message, request }, options
-    __retryRequest = () => @fail(response, { async: true })
+    __retryRequest = () => @fail(response, { useAsyncApi: true })

     @_retryViaAsyncOrIgnore(__makeRequest, __retryRequest, options)

@@ -59,14 +59,13 @@
   _retryViaAsyncOrIgnore: (makeRequest, retryRequest, options = {}) =>
     retry(makeRequest, { throw_original: true, max_tries: 3 })
     .catch (e) =>
-      throw e if options.async
+      throw e if options.useAsyncApi
       console.log("Error sending status to notifications-api. Retrying via notifications-api-async")
-      asyncOptions = _.assign({}, options, { async: true })
       retryRequest()
     .catchReturn()

-  _makeRequest: (body, { async } = {}) =>
-    url = if async then @notificationApiAsyncUrl else @notificationApiUrl
+  _makeRequest: (body, { useAsyncApi } = {}) =>
+    url = if useAsyncApi then @notificationApiAsyncUrl else @notificationApiUrl
     requestPromise {
       url: "#{ url }/jobs/#{ @jobId }/operations"
       method: "POST"


update validation

update validation

--- src/flux/period.coffee
+++ src/flux/period.coffee
@@ -33,7 +33,7 @@
         error = 'Period name is required.'
       if name?.length > maxLength
         error = "Period name must be #{maxLength} characters or less."
-      if not error and name?.match(/[^A-Z0-9-.]+/ig)
+      if not error and name?.match(/[^A-Z0-9-. ]+/ig)
         error = "Period name must contain only letters, numbers, periods or hyphens."
       {error}


count numStudentsTask if included in avgs

--- src/flux/scores.coffee
+++ src/flux/scores.coffee
@@ -54,11 +54,16 @@
       ( task.score / numTasksStudent )

   # Assignment averages
-  studentCount = course.students.length
+  numStudentsTask = 0
+  for student in course.students
+    for studentTask, i in student.data
+      if i is columnIndex
+        numStudentsTask += 1 if studentTask.is_included_in_averages
+
   heading = course.data_headings[columnIndex]
   heading.average_score =
-    ( heading.average_score - ( oldScore / studentCount ) ) +
-      ( task.score / studentCount )
+    ( heading.average_score - ( oldScore / numStudentsTask ) ) +
+      ( task.score / numStudentsTask )

   # Overall course averages
   taskCount = 0


linting

linting

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -148,7 +148,7 @@
           # If the main body's MathJax is still processing,
           # queueing up additional elements freezes the main body's
           # MathJax and prevents the coach's MathJax from ever processing.
-          #
+          #
           # This will que up the coach's MathJax-ing if the main jaxing is
           # in progress, and will run the coach's MathJax-ing immediately
           # otherwise.


switched TERP from herkou demo to live OST, added anchor for quick navigation to the TERP iframe

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -52,8 +52,9 @@
         # Converts a TERP link to an OST-hosted iframe
         $temp.find('a[href*="#terp-"]').each () ->
           terpCode = $(this).attr('href').match(/#terp\-(.*)/)[1]
-          $(this).replaceWith("<iframe class='terp'
-                                       src='https://stormy-wave-8747.herokuapp.com/terp/#{terpCode}/quiz_start'
+          $(this).replaceWith("
+
                                ")


Find hash fragment content after content loads

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -139,6 +139,14 @@
       # MathJax rendering must be done after the HTML has been added to the DOM
       MathJax?.Hub.Queue(['Typeset', MathJax.Hub], @$el.get(0))

+      # Update the hash fragment after the content has loaded
+      # to force the browser window to find the intended content
+      if not @fragmentReloaded and window.location.hash
+        @fragmentReloaded = true
+        hash = window.location.hash
+        window.location.hash = ''
+        window.location.hash = hash
+
     changePage: (e) ->
       $el = $(e.currentTarget)
       href = $el.attr('href')


fix logic for determining draft

fix logic for determining draft

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -26,7 +26,7 @@
           version = @model.get('currentPage.version')
         else
           version = @model.get('version')
-        return 'draft' == version or true # No version means it is a draft
+        return version is 'draft' or !version # No version means it is a draft

     editable:
       '.media-body':


Updated constants naming

Updated constants naming

--- lib/flash-loader.coffee
+++ lib/flash-loader.coffee
@@ -1,17 +1,17 @@
 {app} = require 'electron'
 {join} = require 'path-extra'
 fs = require 'fs-extra'
-{warn, error} = require './utils'
+{error} = require './utils'

-platform = process.platform
-filename = switch platform
+PLATFORM = process.platform
+FILENAME = switch PLATFORM
   when 'darwin' then 'PepperFlashPlayer.plugin'
   when 'linux'  then 'libpepflashplayer.so'
   when 'win32'  then 'pepflashplayer.dll'
-innerPath = join 'PepperFlash', platform, filename
+INNER_PATH = join 'PepperFlash', PLATFORM, FILENAME

 validatePath = (path) ->
-  return false if typeof path isnt 'string' or not path.endsWith filename
+  return false if typeof path isnt 'string' or not path.endsWith FILENAME
   try
     fs.accessSync path
   catch
@@ -19,8 +19,8 @@
   true

 getBuiltInFlashPath = ->
-  builtInPath = join EXECROOT, innerPath if EXECROOT?
-  builtInPath = join ROOT, innerPath if not validatePath builtInPath
+  builtInPath = join EXECROOT, INNER_PATH if EXECROOT?
+  builtInPath = join ROOT, INNER_PATH if not validatePath builtInPath
   if validatePath builtInPath then builtInPath else null

 reVerNum = /(\d+)\.(\d+)\.(\d+)\.(\d+)/
@@ -35,7 +35,7 @@

 findChromeFlashPath = ->
   # Refer to: https://helpx.adobe.com/flash-player/kb/flash-player-google-chrome.html
-  switch platform
+  switch PLATFORM
     when 'darwin'
       try
         # Usually there are 2 directories under the 'Versions' directory,
@@ -57,7 +57,7 @@

 findSystemFlashPath = ->
   # Download/install from: https://get.adobe.com/flashplayer/otherversions/
-  switch platform
+  switch PLATFORM
     when 'darwin'
       try
         systemFlashPath = '/Library/Internet Plug-Ins/PepperFlashPlayer/PepperFlashPlayer.plugin'
@@ -70,7 +70,7 @@

 getFlashVersion = (path) ->
   return '' if not validatePath path
-  switch platform
+  switch PLATFORM
     when 'darwin'
       # The version info is in the Info.plist inside PepperFlashPlayer.plugin
       plistPath = join path, 'Contents', 'Info.plist'
@@ -121,7 +121,7 @@
       errMsg = 'Could not load system Pepper Flash Player plug-in'
     when 'cli'
       flashPath = CLIFlashPath if validatePath CLIFlashPath
-      errMsg = "Invalid path to '#{filename}': \n#{CLIFlashPath}"
+      errMsg = "Invalid path to '#{FILENAME}': \n#{CLIFlashPath}"
     else
       errMsg = "Invalid Pepper Flash Player location: '@#{loc}'."
   if not flashPath?


renamed getValidChildrenOf to getChildrenOf

--- src/contextHelp.coffee
+++ src/contextHelp.coffee
@@ -17,4 +17,5 @@

   getDescOf: (e) -> @getODDfor(e)?.desc

-  getValidChildrenOf: (e) -> @getODDfor(e)?.desc+  getChildrenOf: (e) ->
+    @getODDfor(e)?.children


Move socket listener for 'error' events out of the main state handling code.

--- lib/connection/connection-statemachine.coffee
+++ lib/connection/connection-statemachine.coffee
@@ -19,9 +19,6 @@
       entry: ->
         defaultConfig()
         connect()
-        socket.on('error', (error) ->
-          # Need this handler, or else the error action is not fired. Weird.
-        )
         connectTimer = setTimeout(fire.$cb('connectTimeout'), config.options.connectTimeout);

         fire.$regEmitter('socket', socket, true);
@@ -114,6 +111,10 @@
     socket.setKeepAlive(true, KEEP_ALIVE_INITIAL_DELAY)
     socket.connect(config.options.port, config.server)

+    socket.on('error', (error) ->
+      # Need this listener, or else the error actions are not fired. Weird.
+    )
+
   connectTimeout = ->
     client.emit('connection', "timeout : failed to connect in #{config.options.connectTimeout}ms")
     'Final'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -151,5 +151,5 @@
     @param {Module} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
     ###
     include: (modules...) ->
-      for Module
-      self.extend Module(I, self)
+      for Module in modules
+        self.extend Module(I, self)


respect linter warnings

respect linter warnings

--- lib/julia-client.coffee
+++ lib/julia-client.coffee
@@ -25,9 +25,9 @@
     frontend.activate()
     proc.activate()
     client.activate()
-    client.onConnected =>
+    client.onConnected ->
       notifications.show("Client Connected")
-    @withInk =>
+    @withInk ->
       cons.activate()

     try
@@ -44,42 +44,45 @@
   commands: (subs) ->
     subs.add atom.commands.add '.item-views > atom-text-editor',
       'julia-client:evaluate': (event) =>
-        @withInk =>
+        @withInk ->
           client.start()
           evaluation.eval()
       'julia-client:evaluate-all': (event) =>
-        @withInk =>
+        @withInk ->
           client.start()
           evaluation.evalAll()
       'julia-client:toggle-documentation': =>
-        @withInk =>
+        @withInk ->
           client.start()
           evaluation.toggleMeta 'docs'
       'julia-client:toggle-methods': =>
-        @withInk =>
+        @withInk ->
           client.start()
           evaluation.toggleMeta 'methods'

     subs.add atom.commands.add 'atom-workspace',
-      'julia-client:open-a-repl': => terminal.repl()
-      'julia-client:start-repl-client': =>
-        client.disrequire =>
-          tcp.listen (port) => terminal.client port
-      'julia-client:start-julia': =>
-        client.disrequire =>
-          tcp.listen (port) => proc.start port, cons
-      'julia-client:toggle-console': => @withInk => cons.toggle()
-      'julia-client:reset-loading-indicator': => client.reset()
-      'julia-client:settings': -> atom.workspace.open('atom://config/packages/julia-client')
+      'julia-client:open-a-repl': -> terminal.repl()
+      'julia-client:start-repl-client': ->
+        client.disrequire ->
+          tcp.listen (port) -> terminal.client port
+      'julia-client:start-julia': ->
+        client.disrequire ->
+          tcp.listen (port) -> proc.start port, cons
+      'julia-client:toggle-console': => @withInk -> cons.toggle()
+      'julia-client:reset-loading-indicator': -> client.reset()
+      'julia-client:settings': ->
+        atom.workspace.open('atom://config/packages/julia-client')

-    subs.add atom.commands.add '.item-views > atom-text-editor[data-grammar="source julia"]',
-      'julia-client:set-working-module': => modules.chooseModule()
-      'julia-client:reset-working-module': => modules.resetModule()
+    subs.add atom.commands.add '.item-views >
+                                atom-text-editor[data-grammar="source julia"]',
+      'julia-client:set-working-module': -> modules.chooseModule()
+      'julia-client:reset-working-module': -> modules.resetModule()

     utils.commands subs

     if atom.config.get("julia-client.launchOnStartup")
-      atom.commands.dispatch atom.views.getView(atom.workspace), 'julia-client:start-julia'
+      atom.commands.dispatch atom.views.getView(atom.workspace),
+        'julia-client:start-julia'

   consumeInk: (ink) ->
     @ink = ink
@@ -90,7 +93,7 @@
     client.loading = @loading
     cons.loading = @loading
     @spinner = new ink.Spinner client.loading
-    client.handle 'show-block', ({start, end}) =>
+    client.handle 'show-block', ({start, end}) ->
       if ed = atom.workspace.getActiveTextEditor()
         ink.highlight ed, start-1, end-1


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -139,7 +139,7 @@

     @name include
     @methodOf Core#
-    @param {Module} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
+    @param {St} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
     ###
     include: (modules...) ->
       for Module in modules


include Bindable by default in Core

include Bindable by default in Core

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -171,7 +171,8 @@
       send: (name, args...) ->
         self[name](args...)

-    self.include "Bi"
+    # Include B
+    self.include "Bindable"

     # Initial module inclue, for reconstructing objects from JSON
     for moduleName in I.includedModules


:green_heart: Fix broken left scrolling test in the model

--- spec/minimap-spec.coffee
+++ spec/minimap-spec.coffee
@@ -82,6 +82,10 @@
     scrollSpy = jasmine.createSpy('didScroll')
     minimap.onDidChangeScrollLeft(scrollSpy)

+    # Seems like text without a view aren't able to scroll horizontally
+    # even when its width was set.
+    spyOn(editor.displayBuffer, 'getScrollWidth').andReturn(10000)
+
     editor.setScrollLeft(100)

     expect(scrollSpy).toHaveBeenCalled()
@@ -143,6 +147,11 @@
     [largeLineCount, editorHeight, editorScrollRatio] = []

     beforeEach ->
+      # Same here, without a view, the getScrollWidth method always returns 1
+      # and the test fails because the capped scroll left value always end up
+      # to be 0, inducing errors in computations.
+      spyOn(editor.displayBuffer, 'getScrollWidth').andReturn(10000)
+
       editor.setText(largeSample)
       editor.setScrollTop(1000)
       editor.setScrollLeft(200)


console.log debugging

console.log debugging

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -583,12 +583,14 @@
       dirTwo = temp.mkdirSync('atom-spec-project-two')
       fileThree = path.join(dirTwo, 'file-three.txt')

+      console.log "Setting project paths to #{dirOne}"
       atom.project.setPaths([dirOne])

       waitsForPromise -> atom.project.watchersByPath[dirOne].getStartPromise()

       runs ->
         expect(atom.project.watchersByPath[dirTwo]).toEqual undefined
+        console.log "Watching #{dirOne} but not #{dirTwo}"

         fs.writeFileSync fileThree, "three\n"
         fs.writeFileSync fileTwo, "two\n"
@@ -597,6 +599,7 @@
       waitsForPromise -> waitForEvents [fileOne, fileTwo]

       runs ->
+        console.log "Events seen:\n#{require('util').inspect events}"
         expect(events.some (event) -> event.path is fileThree).toBeFalsy()

   describe ".onDidAddBuffer()", ->


beaglebone.coffee: Set initial versioning and change to 1
the partition holding config.json

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@resin.io>

--- beaglebone.coffee
+++ beaglebone.coffee
@@ -11,6 +11,7 @@
 ]

 module.exports =
+	version: 1
 	slug: 'beaglebone-black'
 	aliases: [ 'beaglebone' ]
 	name: 'BeagleBone Black'
@@ -47,8 +48,7 @@
 	configuration:
 		config:
 			partition:
-				primary: 4
-				logical: 1
+				primary: 1
 			path: '/config.json'

 	initialization: commonImg.initialization


FSHelper: fix .getParentPath from provided path, it now take cares of uid embeded paths too

--- client/Finder/fs/fshelper.coffee
+++ client/Finder/fs/fshelper.coffee
@@ -91,6 +91,8 @@
   @plainPath:(path)-> path.replace /^\[.*\]/, ''
   @getVMNameFromPath:(path)-> (/^\[([^\]]+)\]/g.exec path)?[1]

+  @getUidFromPath:(path)-> (/^\[([^\]]+)\]/g.exec path)?[1]
+
   @handleStdErr = ->
     (result) ->
       { stdout, stderr, exitStatus } = result
@@ -155,10 +157,16 @@
     return name.split('.').shift()

   @getParentPath = (path)->
-    path = path.substr(0, path.length-1) if path.substr(-1) is "/"
-    parentPath = path.split('/')
-    parentPath.pop()
-    return parentPath.join('/')
+
+    uid = @getUidFromPath path
+    if uid? then path = @plainPath path
+
+    path   = path.replace /\/$/, ''
+    parent = path.split '/'; parent.pop(); parent = parent.join '/'
+    parent = "/"  if parent is ""
+    parent = "[#{uid}]#{parent}"  if uid
+
+    return parent

   @createRecursiveFolder = ({ path, vmName }, callback = noop) ->
     return warn "Pass a path to create folders recursively"  unless path


Add author info to profile feeds

Add author info to profile feeds

--- assets/javascripts/routers/profile.js.coffee
+++ assets/javascripts/routers/profile.js.coffee
@@ -9,11 +9,16 @@
     "profile" : "Profile"
   }

+  _initAuthorInfoView: (options = {}) =>
+    new Marbles.Views.AuthorInfo _.extend options,
+      el: document.getElementById('author-info')
+
   currentProfile: (params) =>
     if TentStatus.Helpers.isAppSubdomain()
       return @navigate('/global', {trigger:true, replace: true})

     new Marbles.Views.Profile entity: TentStatus.config.domain_entity.toString()
+    @_initAuthorInfoView()
     TentStatus.setPageTitle page: @actions_titles.currentProfile

   profile: (params) =>
@@ -21,6 +26,7 @@
       return @navigate('/global', {trigger:true, replace: true})

     new Marbles.Views.Profile entity: params.entity
+    @_initAuthorInfoView()

     title = @actions_titles.profile
     title = "#{TentStatus.Helpers.formatUrlWithPath(params.entity)} - #{title}" if params.entity


Fix a little bug in 9703d3f3fee9c269b0a75555fdb6b3122611e8a5

--- src/octonode/client.coffee
+++ src/octonode/client.coffee
@@ -61,7 +61,7 @@
     uri+= if typeof @token == 'object' and @token.username then "#{@token.username}:#{@token.password}@" else ''
     uri+= "api.github.com#{path}"
     uri+= if typeof @token == 'string' then "?access_token=#{@token}" else ''
-    uri+= if typeof @token == 'object' and @token.id then "?client_id=#{@token.id}&client_secret=#{@token.secret}" else ''
+    uri+= if typeof @token == 'object' and @token.id then "?client_id=#{@token.id}&client_secret=#{@token.secret}" else '?'
     uri+= "&page=#{page}" if page?
     uri+= "&per_page=#{per_page}" if per_page?
     uri


so pedantic... good.

so pedantic... good.

--- resource/translators/reference.coffee
+++ resource/translators/reference.coffee
@@ -12,7 +12,7 @@
 #
 # The Bib(La)TeX references are generated by the `Reference` class. Before being comitted to the cache, you can add
 # postscript code that can manipulated the `fields` or the `referencetype`
-#
+#
 # @param {Array} @fields Array of reference fields
 # @param {String} @referencetype referencetype
 # @param {Object} @item the current Zotero item being converted


Add atom prefix to global

Add atom prefix to global

--- lib/package-view.coffee
+++ lib/package-view.coffee
@@ -2,7 +2,7 @@
 semver = require 'semver'
 packageManager = require './package-manager'
 {_, $, $$, View} = require 'atom'
-requireWithGlobals 'bootstrap/js/dropdown', jQuery: $
+atom.requireWithGlobals 'bootstrap/js/dropdown', jQuery: $

 ### Internal ###
 module.exports =


Fixed: missing alias

Fixed: missing alias

--- client/app/utils/XHRUtils.coffee
+++ client/app/utils/XHRUtils.coffee
@@ -1,3 +1,5 @@
+request = superagent
+
 MessageActionCreator = require '../actions/MessageActionCreator'
 MailboxActionCreator = require '../actions/MailboxActionCreator'


Add test case that verifies log parsing end-to-end

--- spec/latex-spec.coffee
+++ spec/latex-spec.coffee
@@ -69,3 +69,16 @@

       waitsFor -> latex.showResult.callCount == 1
       runs -> expect(latex.showResult).toHaveBeenCalled()
+
+    it "invokes `showResult` after a successful build, with expect log parsing result", ->
+      editor = atom.workspace.openSync("file.tex")
+
+      spyOn(latex, "showResult").andCallThrough()
+      latex.build()
+
+      waitsFor -> latex.showResult.callCount == 1
+      runs -> expect(latex.showResult).toHaveBeenCalledWith {
+        outputFilePath: path.join(tempPath, "file.pdf")
+        errors: []
+        warnings: []
+      }


temporary secure relay to fix case count locations

--- client/controllers/counts.coffee
+++ client/controllers/counts.coffee
@@ -21,7 +21,7 @@
       placeholder: "Search for a location..."
       minimumInputLength: 1
       ajax: {
-        url: "http://api.geonames.org/searchJSON"
+        url: "https://crossorigin.me/http://api.geonames.org/searchJSON"
         data: (params) ->
           return {
             username: "eha_eidr"


FSHelper: add method to get file path with VM address

--- client/finder/fs/fshelper.coffee
+++ client/finder/fs/fshelper.coffee
@@ -255,4 +255,8 @@
         data = data.substr chunkSize
     return chunks

+  @getFullPath = (file)->
+    plainPath = @plainPath file.path
+    return "[#{file.vmName}]#{plainPath}"
+
 KD.classes.FSHelper = FSHelper


:fire: Point::sanitizeNegatives spec

:fire: Point::sanitizeNegatives spec

--- spec/point-spec.coffee
+++ spec/point-spec.coffee
@@ -142,16 +142,6 @@
       expect(Point.max([3, 4], [1, 1])).toEqual [3, 4]
       expect(Point.max([1, 2], [5, 6])).toEqual [5, 6]

-  describe "::sanitizeNegatives()", ->
-    it "returns the point so that it has valid buffer coordinates", ->
-      expect(Point(-1, -1).sanitizeNegatives()).toEqual Point(0, 0)
-      expect(Point(-1, 0).sanitizeNegatives()).toEqual Point(0, 0)
-      expect(Point(-1, Infinity).sanitizeNegatives()).toEqual Point(0, 0)
-
-      expect(Point(5, -1).sanitizeNegatives()).toEqual Point(5, 0)
-      expect(Point(5, -Infinity).sanitizeNegatives()).toEqual Point(5, 0)
-      expect(Point(5, 5).sanitizeNegatives()).toEqual Point(5, 5)
-
   describe "::translate(delta)", ->
     it "returns a new point by adding corresponding coordinates", ->
       expect(Point(1, 1).translate(Point(2, 3))).toEqual Point(3, 4)


Plural.

Plural.

--- lib/price-import.coffee
+++ lib/price-import.coffee
@@ -50,12 +50,12 @@

     _.map products, (p) =>
       product = _.deepClone p
-      @_wrapPriceIntoVariant product.masterVariant, prices, sku2index
+      @_wrapPricesIntoVariant product.masterVariant, prices, sku2index
       _.each product.variants, (v) =>
-        @_wrapPriceIntoVariant v, prices, sku2index
+        @_wrapPricesIntoVariant v, prices, sku2index
       product

-  _wrapPriceIntoVariant: (variant, prices, sku2index) ->
+  _wrapPricesIntoVariant: (variant, prices, sku2index) ->
     if _.has(sku2index, variant.sku)
       if not _.contains(@skuOfPerformedPrices, variant.sku)
         variant.prices = []


namespaced Movable

namespaced Movable

--- src/emitterable.coffee
+++ src/emitterable.coffee
@@ -30,7 +30,7 @@
   self.bind 'draw', (canvas) ->
     I.particles.invoke "draw", canvas

-  self.bind 'update', ->
+  self.bind 'update', (dt) ->
     I.batchSize.times ->
       if n < I.particleCount && rand() < I.emissionRate
         particleProperties = Object.extend {}, I.particleData


Adding new children does not cause Drag and Drop to stop working

Because child tree nodes were detached Drag-and-Drop would stop working
on child nodes

--- scripts/views/workspace/sidebar/toc-branch.coffee
+++ scripts/views/workspace/sidebar/toc-branch.coffee
@@ -1,9 +1,10 @@
 define [
+  'jquery'
   'marionette'
   'cs!controllers/routing'
   'cs!helpers/enable-dnd'
   'hbs!templates/workspace/sidebar/toc-branch'
-], (Marionette, controller, EnableDnD, tocBranchTemplate) ->
+], ($, arionette, controller, EnableDnD, tocBranchTemplate) ->

   return class TocBranchView extends Marionette.CompositeView
     tagName: 'li'
@@ -29,20 +30,29 @@
     itemViewOptions: () -> {container: @collection}

     renderModelOnly: () ->
+      # **DO NOT** just detach children. They (and descendants) will
+      # lose their draggable events.
+      #
+      # Instead, reconstruct around the DOM tree
+
       # Detach the children
       $children = @$el.find(@itemViewContainer).children()

       @triggerBeforeRender()
-      html = @renderModel()
-      @$el.html(html)
+
+      $html = $('

').append(@renderModel())
+      # Reattach everything but the children
+      # Remove the itemViewContainer
+      $html.find(@itemViewContainer).remove()
+      $modelNodes = $html.children()
+      @$el.children().not(@$el.find(@itemViewContainer)).remove()
+      @$el.prepend($modelNodes)
+
       # the ui bindings is done here and not at the end of render since they
       # will not be available until after the model is rendered, but should be
       # available before the collection is rendered.
       @bindUIElements()
       @triggerMethod('composite:model:rendered')
-
-      # Reattach the children
-      @$el.find(@itemViewContainer).append($children)

       @triggerMethod('composite:rendered')
       @triggerRendered()


More specific filter for atom commands

More specific filter for atom commands

--- lib/python-tools.coffee
+++ lib/python-tools.coffee
@@ -28,15 +28,15 @@
     # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
     @subscriptions = new CompositeDisposable
     @subscriptions.add(
-      atom.commands.add 'atom-text-editor',
+      atom.commands.add 'atom-text-editor[data-grammar="source python"]',
       'python-tools:show-usages': => @jediToolsRequest('usages')
     )
     @subscriptions.add(
-      atom.commands.add 'atom-text-editor',
+      atom.commands.add 'atom-text-editor[data-grammar="source python"]',
       'python-tools:goto-definition': => @jediToolsRequest('gotoDef')
     )
     @subscriptions.add(
-      atom.commands.add 'atom-text-editor',
+      atom.commands.add 'atom-text-editor[data-grammar="source python"]',
       'python-tools:select-all-string': => @selectAllString()
     )


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -48,7 +48,7 @@
   @param {Number} [frequency=3] The number of frames in between opacity changes
   @param {Number} [alpha=0.5] The alpha value to flicker to
   ###
-  flicker: ({duration, frequency, alpha}={}) ->
+  flicker: ({duration, frequency, alpha}) ->
     Object.reverseMerge I,
       flickerAlpha: 0.5
       flickerDuration: 30