Update tr.coffee

Update tr.coffee

--- app/locale/tr.coffee
+++ app/locale/tr.coffee
@@ -148,7 +148,7 @@
     unwatch: "İzlemeyi Bırak"
     submit_patch: "Yama Gönder"
     submit_changes: "Değişiklikleri Gönder"
-#    save_changes: "Save Changes"
+    save_changes: "Değişiklikleri Kaydet"

   general:
     and: "ve"
@@ -245,19 +245,19 @@
     victory_rate_the_level: "Seviyeyi oyla:" # Only in old-style levels.
     victory_return_to_ladder: "Merdivene Dön"
     victory_play_continue: "Devam Et"
-    victory_saving_progress: "İlerlemeyi Kaydediliyor"
+    victory_saving_progress: "İlerleme Kaydediliyor"
     victory_go_home: "Anasayfaya Git"
     victory_review: "Daha fazla söyleyin!"
-#    victory_review_placeholder: "How was the level?"
+    victory_review_placeholder: "Seviye nasıldı?"
     victory_hour_of_code_done: "Bitirdiniz mi?"
     victory_hour_of_code_done_yes: "Evet, Kod Saatimi (Hour of Code) bitirdim!"
     victory_experience_gained: "Kazanılan XP"
     victory_gems_gained: "Kazanılan Taş"
 #    victory_new_item: "New Item"
 #    victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks."
-#    victory_become_a_viking: "Become a Viking"
-#    victory_bloc: "Great work! Your skills are improving, and someone's taking notice. If you've considered becoming a software developer, this may be your lucky day. Bloc is an online bootcamp that pairs you 1-on-1 with an expert mentor who will help train you into a professional developer! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES"
-#    victory_bloc_cta: "Meet your mentor – learn about Bloc"
+    victory_become_a_viking: "Viking Ol"
+    victory_bloc: "Harika iş çıkardın! Yeteneklerin gelişiyor ve dikkat çekiyor. Yazılım geliştirici olmayı düşünüyorsan şanslı günündesin. Bloc, profesyonel bir program geliştirici olman için seni deneyimli bir danışmanla buluşturan cevrimiçi bir eğitim ağıdır. Mayhem Munchkins'i yenerek $500 indirim almaya hak kazandiniz. Kod: CCRULES"
+    victory_bloc_cta: "Danışmanınla Tanış – Bloc'u Öğren"
     guide_title: "Rehber"
     tome_minion_spells: "Minyonlarınızın Büyüleri" # Only in old-style levels.
     tome_read_only_spells: "Salt Okunur Büyüler" # Only in old-style levels.
@@ -285,63 +285,63 @@
     time_current: "Şimdi:"
     time_total: "Azami:"
     time_goto: "Git:"
-#    non_user_code_problem_title: "Unable to Load Level"
-#    infinite_loop_title: "Infinite Loop Detected"
+    non_user_code_problem_title: "Seviye yüklenemiyor"
+    infinite_loop_title: "Sonsuz döngü tespit edildi"
 #    infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know."
-#    check_dev_console: "You can also open the developer console to see what might be going wrong."
-#    check_dev_console_link: "(instructions)"
+    check_dev_console: "Neyin hatali olduğunu görmek icin geliştirici konsolunu da açabilirsin."
+    check_dev_console_link: "(yönergeler)"
     infinite_loop_try_again: "Yeniden Dene"
     infinite_loop_reset_level: "Bölümü Sıfırla"
     infinite_loop_comment_out: "Kodumu Yorum Yap"
     tip_toggle_play: "Ctrl+P ile oynat/beklet."
-#    tip_scrub_shortcut: "Use Ctrl+[ and Ctrl+] to rewind and fast-forward."
-#    tip_guide_exists: "Click the guide, inside game menu (at the top of the page), for useful info."
+    tip_scrub_shortcut: "Ctrl+[ and Ctrl+] kullanarak geriye veya ileriye sar."
+    tip_guide_exists: "Gerekli bilgiler için oyun menüsünün içindeki (sayfanın başında) rehbere tıkla."
     tip_open_source: "CodeCombat %100 açık kaynaktır!"
-#    tip_tell_friends: "Enjoying CodeCombat? Tell your friends about us!"
+    tip_tell_friends: "CodeCombat'ı sevdin mi? Arkadaşlarına bizden bahset!"
     tip_beta_launch: "CodeCombat ilk beta sürümünü Ekim 2013'te başlattı."
-#    tip_think_solution: "Think of the solution, not the problem."
-#    tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra"
-#    tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis"
-#    tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra"
-#    tip_forums: "Head over to the forums and tell us what you think!"
-#    tip_baby_coders: "In the future, even babies will be Archmages."
+    tip_think_solution: "Sorunu değil, çözümü düşün."
+    tip_theory_practice: "Teoride, teori ve pratik arasında fark yoktur. Ama pratikte vardır. - Yogi Berra"
+    tip_error_free: "Hatasız program yazmanın 2 yolu vardır; sadece üçüncüsü işe yarar. - Alan Perlis"
+    tip_debugging_program: "Eğer hataları ayıklamak programı düzeltmekse, o zaman programlama da hataları bir araya getirmektir. - Edsger W. Dijkstra"
+    tip_forums: "Forumları dolaş ve bize ne düşündüğünü söyle!"
+    tip_baby_coders: "Gelecekte bebekler bile Archmage olacak."
 #    tip_morale_improves: "Loading will continue until morale improves."
 #    tip_all_species: "We believe in equal opportunities to learn programming for all species."
 #    tip_reticulating: "Reticulating spines."
 #    tip_harry: "Yer a Wizard, "
 #    tip_great_responsibility: "With great coding skill comes great debug responsibility."
 #    tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep."
-#    tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't."
+    tip_binary: "Dünyada sadece 10 çeşit insan vardır: Ikilileri anlayabilenler ve anlayamayanlar."
 #    tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda"
-#    tip_no_try: "Do. Or do not. There is no try. - Yoda"
-#    tip_patience: "Patience you must have, young Padawan. - Yoda"
-#    tip_documented_bug: "A documented bug is not a bug; it is a feature."
-#    tip_impossible: "It always seems impossible until it's done. - Nelson Mandela"
-#    tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds"
-#    tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay"
+    tip_no_try: "Yap ya da yapma. Denemek diye bir şey yok. - Yoda"
+    tip_patience: "Sabırlı olmalısın genç Padawan. - Yoda"
+    tip_documented_bug: "Dökümante edilmiş bir hata, hata değildir; olsa olsa yeni bir özelliktir."
+    tip_impossible: "Her şey tamamlanana kadar imkansız görünür. - Nelson Mandela"
+    tip_talk_is_cheap: "Konuşmak kolaydır. Kodunu göster. - Linus Torvalds"
+    tip_first_language: "Hayatta öğrenebilecegin en feci şey ilk programlama dilindir. - Alan Kay"
 #    tip_hardware_problem: "Q: How many programmers does it take to change a light bulb?  A: None, it's a hardware problem."
 #    tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
 #    tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
-#    tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
+#    tip_brute_force: "Süpheye düstüğünde kaba kuvveti kullan. - Ken Thompson"
 #    tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
-#    tip_superpower: "Coding is the closest thing we have to a superpower."
-#    tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds"
+    tip_superpower: "Kodlama, süper güç sahibi olmaya en çok benzeyen şeydir."
+    tip_control_destiny: "Gerçekten açık kaynaklı olan bir kodda, kendi kaderinizi kontrol edebilirsiniz. - Linus Torvalds"
 #    tip_no_code: "No code is faster than no code."
-#    tip_code_never_lies: "Code never lies, comments sometimes do. — Ron Jeffries"
-#    tip_reusable_software: "Before software can be reusable it first has to be usable."
-#    tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’"
+    tip_code_never_lies: "Kod yalan söylemez, yorumlar bazen söyler. — Ron Jeffries"
+    tip_reusable_software: "Yazılımın tekrar kullanılabilir olması için öncelikle kullanılabilir olması gerekmektedir."
+    tip_optimization_operator: "Her dilin optimizasyon operatörü vardır. Çogu dilde bu operator ‘//’ şeklindedir"
 #    tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates"
-#    tip_source_code: "I want to change the world but they would not give me the source code."
-#    tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann"
-#    tip_move_forward: "Whatever you do, keep moving forward. - Martin Luther King Jr."
-#    tip_google: "Have a problem you can't solve? Google it!"
-#    tip_adding_evil: "Adding a pinch of evil."
+    tip_source_code: "Dünyayı değiştirmek istiyorum ancak kaynak kodunu vermiyorlar."
+    tip_javascript_java: "Java ile JavaScript arasındaki ilişki, kafes ile kavanoz arasındaki kadardır. - Chris Heilmann"
+    tip_move_forward: "Ne yaparsan yap, ilerlemeye devam et. - Martin Luther King Jr."
+    tip_google: "Çözemediğin bir problemin mi var? Google'a sor!"
+    tip_adding_evil: "Bir tutam kötülük ekle."
 #    tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven"
-#    tip_open_source_contribute: "You can help CodeCombat improve!"
+    tip_open_source_contribute: "CodeCombat'ı geliştirmeye yardım edebilirsin!"
 #    tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch"
-#    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."
+    tip_free_your_mind: "Her şeyi bırakmalısın, Neo. Korku, şüphe, ve güvensizlik. Zihnini özgürleştir. - Morpheus"
+    tip_strong_opponents: "En güçlü rakiplerin bile zayıf noktası vardır. - Itachi Uchiha"
+    tip_paper_and_pen: "Kodlamaya başlamadan önce çalışmaya her zaman kağıt ve kalemle başlamalısın."

   game_menu:
     inventory_tab: "Envanter"
@@ -391,9 +391,9 @@
     few_gems: "Bir avuç taş"
     pile_gems: "Bir Torba"
     chest_gems: "Sandık dolusu taş"
-#    purchasing: "Purchasing..."
-#    declined: "Your card was declined"
-#    retrying: "Server error, retrying."
+    purchasing: "Ödeniyor..."
+    declined: "Kartınız reddedildi"
+    retrying: "Sunucu hatası, tekrar deneniyor."
 #    prompt_title: "Not Enough Gems"
 #    prompt_body: "Do you want to get more?"
 #    prompt_button: "Enter Shop"
@@ -1127,36 +1127,36 @@
   account:
     recently_played: "En Son Oynananlar"
     no_recent_games: "Son iki hafta içerisinde hiç oyun oynanmadı."
-#    payments: "Payments"
-#    purchased: "Purchased"
-#    sale: "Sale"
-#    subscription: "Subscription"
-#    invoices: "Invoices"
-#    service_apple: "Apple"
-#    service_web: "Web"
+    payments: "Ödemeler"
+    purchased: "Ödendi"
+    sale: "İndirim"
+    subscription: "Üyelik"
+    invoices: "Faturalar"
+    service_apple: "Apple"
+    service_web: "Web"
 #    paid_on: "Paid On"
-#    service: "Service"
-#    price: "Price"
-#    gems: "Gems"
-#    active: "Active"
-#    subscribed: "Subscribed"
-#    unsubscribed: "Unsubscribed"
+    service: "Servis"
+    price: "Fiyat"
+    gems: "Değerli Taş"
+    active: "Aktif"
+    subscribed: "Üye olundu"
+    unsubscribed: "Üyelikten ayrılındı"
 #    active_until: "Active Until"
-#    cost: "Cost"
-#    next_payment: "Next Payment"
-#    card: "Card"
+    cost: "Tutar"
+    next_payment: "Bir sonraki ödeme"
+    card: "Kart"
 #    status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now."
 #    status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!"

-#  account_invoices:
-#    amount: "Amount in US dollars"
-#    declined: "Your card was declined"
-#    invalid_amount: "Please enter a US dollar amount."
-#    not_logged_in: "Log in or create an account to access invoices."
-#    pay: "Pay Invoice"
-#    purchasing: "Purchasing..."
-#    retrying: "Server error, retrying."
-#    success: "Successfully paid. Thanks!"
+  account_invoices:
+    amount: "US doları cinsinden tutarı"
+    declined: "Kartınız reddedildi"
+    invalid_amount: "Lütfen US doları cinsinden tutar giriniz."
+    not_logged_in: "Faturalara erisim için lütfen giriş yapınız ya da hesap açınız"
+    pay: "Fatura Ödeme"
+    purchasing: "Ödeniyor..."
+    retrying: "Sunucu hatası, tekrar deneniyor."
+    success: "Başarıyla ödendi. Tesekkürler!"

   loading_error:
     could_not_load: "Yüklenemiyor"
@@ -1182,53 +1182,53 @@
     gplus_friends: "G+ Arkadaşları"
     gplus_friend_sessions: "G+ Arkadaş Oturumları"
     leaderboard: "Sıralama"
-#    user_schema: "User Schema"
-#    user_profile: "User Profile"
-#    patch: "Patch"
-#    patches: "Patches"
-#    patched_model: "Source Document"
-#    model: "Model"
-#    system: "System"
-#    systems: "Systems"
-#    component: "Component"
-#    components: "Components"
-#    thang: "Thang"
-#    thangs: "Thangs"
-#    level_session: "Your Session"
-#    opponent_session: "Opponent Session"
-#    article: "Article"
-#    user_names: "User Names"
-#    thang_names: "Thang Names"
-#    files: "Files"
-#    top_simulators: "Top Simulators"
-#    source_document: "Source Document"
-#    document: "Document"
-#    sprite_sheet: "Sprite Sheet"
-#    employers: "Employers"
-#    candidates: "Candidates"
-#    candidate_sessions: "Candidate Sessions"
+    user_schema: "Kullanıcı Şeması"
+    user_profile: "Kullanıcı Profili"
+    patch: "Yama"
+    patches: "Yamalar"
+    patched_model: "Kaynak Dökümanı"
+    model: "Model"
+    system: "Sistem"
+    systems: "Sistemler"
+    component: "Bileşen"
+    components: "Bileşenler"
+    thang: "Madde"
+    thangs: "Maddeler"
+    level_session: "Oturumunuz"
+    opponent_session: "Rakip Oturumu"
+    article: "Makale"
+    user_names: "Kullanıcı Adları"
+    thang_names: "Madde Adları"
+    files: "Dosyalar"
+    top_simulators: "En iyi simulatörler"
+    source_document: "Kaynak Dökümanı"
+    document: "Dökümanlar"
+    sprite_sheet: "Sprite Sheet"
+    employers: "İş verenler"
+    candidates: "Adaylar"
+    candidate_sessions: "Aday Oturumları"
 #    user_remark: "User Remark"
 #    user_remarks: "User Remarks"
-#    versions: "Versions"
+    versions: "Versiyonlar"
 #    items: "Items"
-#    hero: "Hero"
-#    heroes: "Heroes"
-#    achievement: "Achievement"
-#    clas: "CLAs"
-#    play_counts: "Play Counts"
-#    feedback: "Feedback"
-#    payment_info: "Payment Info"
-#    campaigns: "Campaigns"
+    hero: "Kahraman"
+    heroes: "Kahramanlar"
+    achievement: "Başarı"
+    clas: "CLA'ler"
+    play_counts: "Oyun Sayısı"
+    feedback: "Geri bildirim"
+    payment_info: "Ödeme Bilgisi"
+    campaigns: "Kampanyalar"
 #    poll: "Poll"
 #    user_polls_record: "Poll Voting History"

 #  concepts:
 #    advanced_strings: "Advanced Strings"
-#    algorithms: "Algorithms"
-#    arguments: "Arguments"
-#    arithmetic: "Arithmetic"
-#    arrays: "Arrays"
-#    basic_syntax: "Basic Syntax"
+    algorithms: "Algoritmalar"
+    arguments: "Argümanlar"
+    arithmetic: "Aritmetik"
+    arrays: "Diziler"
+    basic_syntax: "Temel Söz Dizimi (Syntax)"
 #    boolean_logic: "Boolean Logic"
 #    break_statements: "Break Statements"
 #    classes: "Classes"


Applied fix for issue #198 on vi.coffee + added a couple of translations done by An as a test (succesful)

--- app/locale/vi.coffee
+++ app/locale/vi.coffee
@@ -1,20 +1,20 @@
 module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietnamese", translation:
   common:
-    loading: "Loading..."
-#    saving: "Saving..."
-#    sending: "Sending..."
-
-#  modal:
-#    close: "Close"
-#    okay: "Okay"
-
-#  not_found:
-#    page_not_found: "Page not found"
-
-#  nav:
-#    sign_up: "Create Account"
-#    log_in: "Log In"
-#    log_out: "Log Out"
+    loading: "Tải..."
+    saving: "Lưu..."
+    sending: "Gởi..."
+
+  modal:
+    close: "Đóng"
+    okay: "Được rồi"
+
+  not_found:
+    page_not_found: "không tìm thấy trang"
+
+  nav:
+    sign_up: "Tạo tài khoản"
+    log_in: "Đăng nhập"
+    log_out: "Đăng xuất"
 #    play: "Levels"
 #    editor: "Editor"
 #    blog: "Blog"
@@ -99,11 +99,11 @@
 #    sending: "Sending..."
 #    send: "Send Feedback"

-  diplomat_suggestion:
+#  diplomat_suggestion:
 #    title: "Help translate CodeCombat!"
 #    sub_heading: "We need your language skills."
-    pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Vietnamese but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Vietnamese."
-    missing_translations: "Until we can translate everything into Vietnamese, you'll see English when Vietnamese isn't available."
+#    pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Vietnamese but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Vietnamese."
+#    missing_translations: "Until we can translate everything into Vietnamese, you'll see English when Vietnamese isn't available."
 #    learn_more: "Learn more about being a Diplomat"
 #    subscribe_as_diplomat: "Subscribe as a Diplomat"

@@ -261,23 +261,19 @@
 #    new_component_btn_cancel: "Cancel"
 #    new_component_btn_create: "Create"

-#  level:
-#    index_table_results: "Results"
-#    index_table_name: "Name"
-#    index_table_description: "Description"
-#    index_table_version: "Version"
-
 #  article:
-#    index_table_results: "Results"
-#    index_table_name: "Name"
-#    index_table_body: "Body"
-#    index_table_version: "Version"
 #    edit_btn_preview: "Preview"
 #    edit_btn_save: "Save"
 #    edit_article_title: "Edit Article"

 #  general:
 #    and: "and"
+#    name: "Name"
+#    version: "Version"
+#    commit_msg: "Commit Message"
+#    version_history_for: "Version History for: "
+#    results: "Results"
+#    description: "Description"

 #  about:
 #    who_is_codecombat: "Who is CodeCombat?"


draw-turtle-path variant

draw-turtle-path variant

--- src/draw_grid.coffee
+++ src/draw_grid.coffee
@@ -17,13 +17,6 @@
     head = x + length
     half_width = width / 2
     S.g {class: "turtle"}, [
-      S.ellipse {
-        class: "body"
-        cx: x, cy: y
-        rx: length, ry: width
-        fill: "#00ff00"
-        stroke: "#88ff88"
-      }
       S.circle {
         class: "head"
         cx: head, cy: y
@@ -36,7 +29,20 @@
         draw_leg(x+half_width, y-3*half_width)
         draw_leg(x-width, y-3*half_width)
       ]
+      S.ellipse {
+        class: "body"
+        cx: x, cy: y
+        rx: length, ry: width
+        fill: "#00ff00"
+        stroke: "#88ff88"
+      }
     ]
+  draw_turtle_path = (i, j) ->
+    x = i*cell_size
+    y = j*cell_size
+    path = "M#{x},#{y} H100 V100"
+    S.path {class: "path-turtle", d: path , stroke: "#00ff00"}
+
   grid_path = ""
   for n in [0..grid_size] by cell_size
     grid_path += "M#{n},1 V#{grid_size-1} M1,#{n} H#{grid_size-1} "
@@ -50,4 +56,5 @@
     }
     S.path {class: "grid-lines", d: grid_path, stroke: "#ffffff"}
     draw_turtle(2,4)
+    draw_turtle_path(1,2)
   ]


warn when trying to write tag where parent is already written

--- src/dt-stream.coffee
+++ src/dt-stream.coffee
@@ -86,6 +86,9 @@
     # eventlisteners

     onadd: (parent, el) ->
+        unless parent.writable
+            console.warn "creating #{el.toString()} in closed #{parent.toString()} omitted"
+            return
         el._stream = new Entry el, parent
         @opened_tags++
         el._stream.write =>


Display content browser or content page depending on options

--- src/scripts/pages/content/content.coffee
+++ src/scripts/pages/content/content.coffee
@@ -3,6 +3,7 @@
   HeaderView = require('cs!modules/header/header')
   FooterView = require('cs!modules/footer/footer')
   FindContentView = require('cs!modules/find-content/find-content')
+  BrowseContentView = require('cs!modules/browse-content/browse-content')
   MediaView = require('cs!modules/media/media')
   template = require('hbs!./content-template')
   require('less!./content')
@@ -21,5 +22,9 @@
     onRender: () ->
       @parent.regions.header.show(new HeaderView({page: 'content'}))
       @parent.regions.footer.show(new FooterView({page: 'content'}))
-      @regions.content.append(new FindContentView())
-      @regions.content.append(new MediaView({uuid: @uuid, page: @page}))
+      @regions.content.show(new FindContentView())
+
+      if @uuid
+        @regions.content.append(new MediaView({uuid: @uuid, page: @page}))
+      else
+        @regions.content.append(new BrowseContentView())


Only show content views that have content

--- src/scripts/pages/content/content.coffee
+++ src/scripts/pages/content/content.coffee
@@ -21,7 +21,5 @@
     onRender: () ->
       @parent?.regions.header.show(new HeaderView({page: 'content'}))
       @parent?.regions.footer.show(new FooterView({page: 'content'}))
-
-      if @uuid
-        @regions.content.append(new FindContentView())
-        @regions.content.append(new MediaView({uuid: @uuid, page: @page}))
+      @regions.content.append(new FindContentView())
+      @regions.content.append(new MediaView({uuid: @uuid, page: @page}))


misc cleanup

misc cleanup

--- src/scripts/services/dapiServices.coffee
+++ src/scripts/services/dapiServices.coffee
@@ -38,7 +38,7 @@
       useXDomain: true # needed to send cookies in POST
       # FIXME - better to include in an auth header per RFC2617 Authorization: DroneApi apikey="blah.blah"
       #params:
-      #  api_key: "eb34bd67.megadroneshare"
+      #  api_key: "userkey.appkey"
       headers:
         Authorization: 'DroneApi apikey="' + apiKey + '"'


Origin header is meaningless in SockJS context

Special attention required - just like with cookies.

--- src/transport.coffee
+++ src/transport.coffee
@@ -115,7 +115,7 @@
         @connection.pathname = req.pathname

         headers = {}
-        for key in ['origin', 'referer', 'x-client-ip', 'x-forwarded-for', \
+        for key in ['referer', 'x-client-ip', 'x-forwarded-for', \
                     'x-cluster-client-ip']
             headers[key] = req.headers[key] if req.headers[key]
         if headers


Testing change immediacy again

Testing change immediacy again

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -208,10 +208,10 @@
   robot.hear /^cah help$/i, (msg) ->
     msg.send helpSummary

-  robot.respond /cah black$/i, (msg) ->
+  robot.hear /cah black$/i, (msg) ->
     msg.send random_black_card()

-  robot.respond /cah white$/i, (msg) ->
+  robot.hear /cah white$/i, (msg) ->
     msg.send random_white_card()

   robot.respond /cah play$/i, (msg) ->


Fixes #14

Fixes #14

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -260,7 +260,7 @@
     responseString = "Your white CAH cards:"
     if cards?
       for i in [0...cards.length] by 1
-        responseString += "\n#{i}: #{cards[i]}"
+        responseString += "\n#{i+1}: #{cards[i]}"
     responseString += "\nCurrent black card: *#{db.blackCard}*"
     robot.messageRoom sender(msg), responseString

@@ -300,7 +300,7 @@
       responseString = "White card submissions thus far:"
       for i in [0...answers.length] by 1
         cards = answers[i][1]
-        responseString += "\n#{i}: #{generate_phrase(db.blackCard, cards)}"
+        responseString += "\n#{i+1}: #{generate_phrase(db.blackCard, cards)}"
       robot.messageRoom sender(msg), responseString

   robot.hear /^cah (choose|pick) (\d+)$/i, (msg) ->


Use unique text editor title in window and tab titles

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -155,7 +155,7 @@
     projectPaths = @project.getPaths() ? []
     if item = @getActivePaneItem()
       itemPath = item.getPath?()
-      itemTitle = item.getTitle?()
+      itemTitle = item.getLongTitle?() ? item.getTitle?()
       projectPath = _.find projectPaths, (projectPath) ->
         itemPath is projectPath or itemPath?.startsWith(projectPath + path.sep)
     itemTitle ?= "untitled"


hotfix

hotfix

--- src/js/player.coffee
+++ src/js/player.coffee
@@ -104,7 +104,7 @@
             ).on(EVENTS.PROGRESS, (progress) =>
                 @trigger('progress', progress)
             ).on(EVENTS.ERROR, (e) =>
-                @trigger(EVENTS.ERROR, e)
+                @trigger('error', e)
             )

         ###*


reordered code to highlight renderInitialView, attachLocationChangeListener and attachClickListener

--- src/ki-router.coffee
+++ src/ki-router.coffee
@@ -138,6 +138,49 @@
     @transparentRouting()
     return

+  # Renders page based on current url
+  renderInitialView: =>
+    initialUrl = window.location.pathname
+    if @pushStateSupport
+      if window.location.hash.substring(0, 2) == "#!" && @find(window.location.hash.substring(2))
+        initialUrl = window.location.hash.substring(2)
+    else
+      if @hashchangeSupport
+        if window.location.hash.substring(0, 2) == "#!"
+          initialUrl = window.location.hash.substring(2)
+    @log("Rendering initial page")
+    @renderUrl(initialUrl)
+    return
+
+  # Notices when browser goes back or forward in history
+  attachLocationChangeListener: =>
+    if @pushStateSupport
+      @addListener window, "popstate", (event) =>
+        if @updateCount > 0 # Chrome sends popstate event when rendering page, don't render unless really needed
+          href = window.location.pathname
+          @log("Rendering popstate", href)
+          @renderUrl(href)
+        return
+    else
+      if @hashchangeSupport
+        @addListener window, "hashchange", (event) =>
+          if window.location.hash.substring(0, 2) == "#!"
+            href = window.location.hash.substring(2)
+            @log("Rendering hashchange", href)
+            @renderUrl(href)
+          return
+    return
+
+  renderUrl: (url) =>
+    if ret = @exec(url)
+      return ret
+    else
+      if @fallbackRoute
+        return @fallbackRoute(url)
+      else
+        @log("Could not resolve route for", url)
+
+  # Click listener catches clicks to A tag and processes the url if it matches known routes
   attachClickListener: =>
     if @pushStateSupport || @hashchangeSupport
       @addListener document, "click", (event) =>
@@ -166,6 +209,16 @@
       @log("Letting browser render url because no matching route", href)
     return

+  updateUrl: (href) =>
+    @updateCount += 1
+    if !@disableUrlUpdate
+      if @pushStateSupport
+        history.pushState({ }, document.title, href)
+      else
+        if @hashchangeSupport
+          window.location.hash = "!" + href
+    return
+
   checkIfHashBaseUrlRedirectNeeded: () =>
     !@pushStateSupport && @hashchangeSupport && @hashBaseUrl && @hashBaseUrl != window.location.pathname

@@ -235,56 +288,6 @@
       ""
     else
       port
-
-  attachLocationChangeListener: =>
-    if @pushStateSupport
-      @addListener window, "popstate", (event) =>
-        if @updateCount > 0 # Chrome sends popstate event when rendering page, don't render unless really needed
-          href = window.location.pathname
-          @log("Rendering popstate", href)
-          @renderUrl(href)
-        return
-    else
-      if @hashchangeSupport
-        @addListener window, "hashchange", (event) =>
-          if window.location.hash.substring(0, 2) == "#!"
-            href = window.location.hash.substring(2)
-            @log("Rendering hashchange", href)
-            @renderUrl(href)
-          return
-    return
-
-  renderInitialView: =>
-    initialUrl = window.location.pathname
-    if @pushStateSupport
-      if window.location.hash.substring(0, 2) == "#!" && @find(window.location.hash.substring(2))
-        initialUrl = window.location.hash.substring(2)
-    else
-      if @hashchangeSupport
-        if window.location.hash.substring(0, 2) == "#!"
-          initialUrl = window.location.hash.substring(2)
-    @log("Rendering initial page")
-    @renderUrl(initialUrl)
-    return
-
-  renderUrl: (url) =>
-    if ret = @exec(url)
-      return ret
-    else
-      if @fallbackRoute
-        return @fallbackRoute(url)
-      else
-        @log("Could not resolve route for", url)
-
-  updateUrl: (href) =>
-    @updateCount += 1
-    if !@disableUrlUpdate
-      if @pushStateSupport
-        history.pushState({ }, document.title, href)
-      else
-        if @hashchangeSupport
-          window.location.hash = "!" + href
-    return

   addListener: (element, event, fn) =>
     if element.addEventListener  # W3C DOM


Buffer outgoing subgraph ports as well

Buffer outgoing subgraph ports as well

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -22,6 +22,7 @@
     connections: []
     graph: null
     startupDate: null
+    portBuffer: {}

     constructor: (graph) ->
         @processes = {}
@@ -145,6 +146,33 @@
         socket.on "data", (data) ->
             logSocket "DATA"

+    # Release the IPs buffered because of an un-ready component
+    flushPortBuffer: (id) ->
+        buffer = @portBuffer[id]
+        inports = buffer.ins
+        outports = buffer.outs
+
+        # Notify outports FIRST so that connections are attached because the floodgate is opened
+        for port in outports
+            port.emit("ready")
+
+        for port in inports
+            port.emit("ready")
+
+        delete @portBuffer[id]
+
+    # Set up a buffer for an un-ready component's port
+    setupPortBuffer: (id) ->
+        if @portBuffer[id]?
+            @portBuffer[id].count++
+        else
+            @portBuffer[id] =
+                ins: [] # Inports of upstream components of an un-ready component that need to buffer
+                outs: [] # Outports of an un-ready component
+                count: 1 # A stack count to make sure all IPs are flushed at the same time with the right order of setup (i.e. outports of an un-ready component are set first)
+
+        @portBuffer[id]
+
     addEdge: (edge) ->
         return @addInitial(edge) unless edge.from.node
         socket = internalSocket.createSocket()
@@ -156,9 +184,25 @@
         unless from.component
             throw new Error "No component defined for outbound node #{edge.from.node}"
         unless from.component.isReady()
+            buffer = @setupPortBuffer(from.id)
+
             from.component.once "ready", =>
                 @addEdge edge
-            return
+
+                # When the "from" component isn't ready, it's an outgoing port
+                fromPort = from.component.outPorts[edge.from.port]
+                buffer.outs.push(fromPort)
+
+                # Decrement the count and flush the buffer on empty stack ON NEXT CYCLE
+                next = () =>
+                  buffer.count--
+                  if buffer.count is 0
+                    @flushPortBuffer(from.id)
+
+                setTimeout(next, 0)
+
+            return
+
         to = @getNode edge.to.node
         unless to
             throw new Error "No process defined for inbound node #{edge.to.node}"
@@ -167,10 +211,23 @@
         unless to.component.isReady()
             fromPort = from.component.outPorts[edge.from.port]
             fromPort.downstreamIsGettingReady = true
+            buffer = @setupPortBuffer(from.id)

             to.component.once "ready", =>
                 @addEdge edge
-                fromPort.emit("ready")
+
+                # When the "to" component isn't ready, it's an incoming port
+                fromPort = from.component.outPorts[edge.from.port]
+                buffer.ins.push(fromPort)
+
+                # Decrement the count and flush the buffer on empty stack ON NEXT CYCLE
+                next = () =>
+                  buffer.count--
+                  if buffer.count is 0
+                    @flushPortBuffer(from.id)
+
+                setTimeout(next, 0)
+
             return

         @connectPort socket, to, edge.to.port, true


cache: Export clearCache

cache: Export clearCache

--- src/lib/cache.coffee
+++ src/lib/cache.coffee
@@ -82,11 +82,14 @@

 # Removes the whole cache directory and recreates it from scratch.
 #
-# @param cacheRoot [String] The path to the cache.
 # @param callback [(err)] A function to receive the error (if any).
 #
 clearCache = (cacheRoot, callback) ->
-  log.debug 'Clearing cache'
+  unless callback?
+    callback = cacheRoot
+    cacheRoot = tilde config.get 'cache:root'
+
+  log.debug "Clearing #{cacheRoot}"
   rimraf cacheRoot, (err) ->
     return callback "Failed to clear the cache (#{err})" if err?

@@ -95,6 +98,4 @@
       callback err

-module.exports =
-  getCacheDir: getCacheDir
-  initCache: initCache
+module.exports = {getCacheDir, initCache, clearCache}


moved all of the quad code inside the main require so that it is easier to move it into amd modules

--- src/main/quad.coffee
+++ src/main/quad.coffee
@@ -1,51 +1,37 @@

 define('quad', {})

-digitalWrite(LED1, false);
-digitalWrite(LED2, false);
-digitalWrite(LED3, false);
+require ['quad', 'intensity'], (intensity) ->
+  digitalWrite(LED1, false);
+  digitalWrite(LED2, false);
+  digitalWrite(LED3, false);

-Quad = ->
-  throttle = 56
+  Quad = ->
+    throttle = 56

-  startProps: ->
-#    setInterval (=>
+    startProps: ->
+  #    setInterval (=>
       digitalPulse(C9, 1, 1 + E.clip(throttle / 100, 0, 1))
       digitalPulse(C8, 1, 1 + E.clip(throttle / 100, 0, 1))
       digitalPulse(C7, 1, 1 + E.clip(throttle / 100, 0, 1))
       digitalPulse(C6, 1, 1 + E.clip(throttle / 100, 0, 1))
-#      50
+  #      50

-  throttle: (newThrottle) -> throttle = newThrottle
+    throttle: (newThrottle) -> throttle = newThrottle

-newIntensity = (sinceLastPulse) ->
-  intensity = sinceLastPulse
+  Receiver = (pin, quad) ->

-  if isNaN(intensity)
-    intensity = 0
+    listenForEvents: =>
+      updateThrottle = (event) ->
+        sinceLastPulse = (event.time - event.lastTime) * 100000
+        quad.throttle(intensity(sinceLastPulse))

-  if intensity > 100
-    intensity = 100
+      setWatch(updateThrottle, pin, repeat: true, edge: 'falling')

-  if intensity < 0
-    intensity = 0
+  quad = Quad()
+  quad.startProps()

-  intensity
-
-Receiver = (pin, quad) ->
-
-  listenForEvents: =>
-    updateThrottle = (event) ->
-      sinceLastPulse = (event.time - event.lastTime) * 100000
-      quad.throttle(newIntensity(sinceLastPulse))
-
-    setWatch(updateThrottle, pin, repeat: true, edge: 'falling')
-
-quad = Quad()
-quad.startProps()
-
-receiver = Receiver(A13, quad)
-receiver.listenForEvents()
-
-require ['quad'], -> console.log 'quadcopter started'
+  receiver = Receiver(A13, quad)
+  receiver.listenForEvents()
+  console.log 'quadcopter started'


do not call out XNCRs as spurious ampersands


Rename variable for clarity

Rename variable for clarity

--- bokehjs/src/coffee/core/ui_events.coffee
+++ bokehjs/src/coffee/core/ui_events.coffee
@@ -123,20 +123,20 @@
     switch base_type

       when "move"
-        active_inspectors = any(@toolbar.inspectors, (t) -> t.active)
+        has_active_inspectors = any(@toolbar.inspectors, (t) -> t.active)
         cursor = "default"

         # the event happened on a renderer
         if view?
           if view.model.cursor?
             cursor = view.model.cursor()
-          if active_inspectors
+          if has_active_inspectors
             # override event_type to cause inspectors to clear overlays
             event_type = "move:exit"

         # the event happened on the plot frame but off a renderer
         else if @_hit_test_frame(e.bokeh.sx, e.bokeh.sy)
-          if active_inspectors
+          if has_active_inspectors
             cursor = "crosshair"

         @plot_view.set_cursor(cursor)


update cn to tw for zh_tw

update cn to tw for zh_tw

--- tests/translate.coffee
+++ tests/translate.coffee
@@ -27,7 +27,7 @@
   tr: 'tr_TR'
   vi: 'vn_VN'
   'zh-CN': 'zh_CN'
-  'zh-TW': 'zh_CN'
+  'zh-TW': 'zh_TW'
 }

 flags = {
@@ -55,7 +55,7 @@
   tr: 'tr'
   vi: 'vn'
   'zh-CN': 'cn'
-  'zh-TW': 'cn'
+  'zh-TW': 'tw'
 }

 count = null


Fix for checking of child element

Fix for checking of child element

--- src/normalize.coffee
+++ src/normalize.coffee
@@ -101,6 +101,7 @@
         "node=#{encodeURI req.node};" +
         "item=#{encodeURI req.item.id}"
     alreadyPresent = req.item.el.children.some (child) ->
+        typeof child != 'string' and
         child.is('link') and
         child.attrs.rel is 'self' and
         child.attrs.href is link


Caching and optimizations that feel a bit silly after updating to latest stable node (soo much faster)

--- server/packages/nodemailer/index.coffee
+++ server/packages/nodemailer/index.coffee
@@ -13,8 +13,13 @@
 readFile = Promise.promisify fs.readFile, fs

 sandbox = null
+templateCache = {}
 transport = null

+exports.$clearCaches = ->
+
+	templateCache = {}
+
 exports.$httpListening = (http) ->

 	settings = nconf.get 'packageSettings:nodemailer'
@@ -87,14 +92,20 @@

 	).then((html) ->

-		if html?
-
+		return unless html?
+
+		html = if templateCache[type]?
+
+			templateCache[type]
+
+		else
+
 			# Prepare the HTML to be sent as email.
-			html = sandbox.prepareHtmlForEmail html.toString()
-
-			# Compile it with Handlebars.
-			mail.tokens ?= {}
-			html = handlebars.compile(html) mail.tokens
+			templateCache[type] = sandbox.prepareHtmlForEmail html.toString()
+
+		# Compile it with Handlebars.
+		mail.tokens ?= {}
+		html = handlebars.compile(html) mail.tokens

 	).then((html) ->

@@ -172,19 +183,44 @@

 						true

-					# Rejoin the selectors, and cache the associated rules.
+					# Rejoin the selectors.
 					selector = parts.join ','

+					# Normalize the rule(s).
 					selectors[selector] ?= ''
-					selectors[selector] += rule.style.cssText
-
+					selectors[selector] += rule.style.cssText.split(
+
+						';'
+
+					).filter((rule) ->
+
+						rule isnt ''
+
+					).map((rule) ->
+
+						rule.trim()
+
+					).sort().join '; '
+					selectors[selector] += ';'
+
+			# Merge as many rules as we can, so we'll have less work to do for
+			# each application.
+			cssTextCache = {}
+			for selector, cssText of selectors
+				(cssTextCache[cssText] ?= []).push selector
+
+			selectors = {}
+			for cssText, selectors_ of cssTextCache
+				selectors[selectors_.join ','] = cssText
+
 			# CREDIT: http://devintorr.es/blog/2010/05/26/turn-css-rules-into-inline-style-attributes-using-jquery/
 			# With some improvements, of course.
 			sandbox.inlineCss = (html) ->
+
 				for selector, cssText of selectors
-					$(selector, $(html)).each (i, elm) ->
-						elm.style.cssText += cssText
-
+					for element in $(selector, $(html))
+						element.style.cssText += cssText
+
 			# Prepare HTML for email; inject all CSS inline and insert niceties
 			# like a nav on top.
 			sandbox.prepareHtmlForEmail = (html) ->


Don't return early in case where last arg is not a function

--- timeAsyncMethod.coffee
+++ timeAsyncMethod.coffee
@@ -14,7 +14,6 @@

 		if !callback? || typeof callback != 'function'
 			logger.log "[Metrics] expected wrapped method '#{methodName}' to be invoked with a callback"
-			return

 		timer = new metrics.Timer(key)


Add Reveal context menu to pane item views

Closes #70

--- menus/tree-view.cson
+++ menus/tree-view.cson
@@ -28,3 +28,6 @@
     'Copy Full Path': 'tree-view:copy-full-path'
     'Copy Project Path': 'tree-view:copy-project-path'
     'Show in Finder': 'tree-view:show-in-file-manager'
+
+  '.pane .item-views':
+    'Reveal in Tree View': 'tree-view:reveal-active-file'


Provide tooltip for postponing contestant heat participation

--- app/assets/javascripts/announcer.js.coffee
+++ app/assets/javascripts/announcer.js.coffee
@@ -69,7 +69,7 @@
       for contestant in heat.contestants
         slot = heat_container.find(".lane#{contestant.lane}")
         if contestant.postponable
-          contestant_link = "#{contestant.name}"
+          contestant_link = "#{contestant.name}"
           slot.html contestant_link
         else
           slot.html contestant.name


refactored getScores method of BarChart class to use a score count array and array methods

--- app/assets/javascripts/bar_chart.js.coffee
+++ app/assets/javascripts/bar_chart.js.coffee
@@ -4,24 +4,20 @@
     @getScores()
     @buildChart()
   getScores: ->
-    zeroCount = 0
-    oneCount = 0
-    twoCount = 0
+    count = [0, 0, 0]

-    for key, rating of @ratingsData
-      if rating.score == 0
-        zeroCount += 1
-      else if rating.score == 1
-        oneCount += 1
-      else if rating.score == 2
-        twoCount += 1
+    for rating in @ratingsData
+      count[rating.score] += 1

-    @scores.zeroPercent = (zeroCount * 100 / @ratingsData.length) + "%"
-    @scores.onePercent = (oneCount * 100 / @ratingsData.length) + "%"
-    @scores.twoPercent = (twoCount * 100 / @ratingsData.length) + "%"
-    @scores.zeroCount = zeroCount
-    @scores.oneCount = oneCount
-    @scores.twoCount = twoCount
+    numberOfRatings = @ratingsData.length
+    percents = count.map (c) -> (c * 100 / numberOfRatings) + "%"
+
+    @scores.zeroPercent = percents[0]
+    @scores.onePercent = percents[1]
+    @scores.twoPercent = percents[2]
+    @scores.zeroCount = count[0]
+    @scores.oneCount = count[1]
+    @scores.twoCount = count[2]
   buildChart: ->
     @element.find('.count-number').html(@ratingsData.length)
     @element.find('.progress-bar-danger').css('width', @scores.zeroPercent).html(@scores.zeroCount)


OX-120 debug

OX-120 debug

--- app/assets/javascripts/model/binary.coffee
+++ app/assets/javascripts/model/binary.coffee
@@ -162,13 +162,12 @@
       positionBucket = [position[0] >> 5, position[1] >> 5, position[2] >> 5]
       zoomedPositionBucket = [positionBucket[0] >> zoomSteps[0], positionBucket[1] >> zoomSteps[0], positionBucket[2] >> zoomSteps[0]]

-
       resizeRadius = 4

       buckets  = @getBucketArray(zoomedPositionBucket, resizeRadius, resizeRadius, 0).concat(
                   @getBucketArray(zoomedPositionBucket, resizeRadius, 0, resizeRadius),
                   @getBucketArray(zoomedPositionBucket, 0, resizeRadius, resizeRadius))
-
+      console.log buckets

       Cube.extendByBucketAddressExtent6(
         (zoomedPositionBucket[0] - resizeRadius) << zoomSteps[0], (zoomedPositionBucket[1] - resizeRadius) << zoomSteps[0], (zoomedPositionBucket[2] - resizeRadius) << zoomSteps[0],


debug in prod

debug in prod

--- app/assets/javascripts/workflows.js.coffee
+++ app/assets/javascripts/workflows.js.coffee
@@ -30,6 +30,9 @@
     next_button = button.next('li')
     button.removeClass('is-selected')
     next_button.addClass('is-selected')
+    console.log(button)
+    console.log(next_button)
+
     window.change_level()

     false


Restored setEcAction method

Restored setEcAction method

--- app/coffee/modules/common/analytics.coffee
+++ app/coffee/modules/common/analytics.coffee
@@ -155,4 +155,10 @@
         })
         @win.ga('send', 'event', 'checkout', 'Plan checkout')

+    setEcAction: (action, page) ->
+        @win.ga('ec:setAction', action, {
+            'list': page
+        })
+        @.trackEvent("ecommerce", "set-action", (action+page), 0)
+
 module.service("$tgAnalytics", AnalyticsService)


Fix syntax error in fs.base

Fix syntax error in fs.base

--- src/stdlib/fs.coffee
+++ src/stdlib/fs.coffee
@@ -15,7 +15,7 @@
   # remove a trailing extension.
   base: (path, ext) ->
     base = path.replace(/\/$/, '').split("/").pop()
-    if ext then base.replace(RegEx(ext + "$"), "") else base
+    if ext then base.replace(RegExp(ext + "$"), "") else base

   # Returns the path of a file's containing directory, albeit the
   # parent directory if the file is a directory. A terminal directory


reload only loads 1 buffer into invisible scroller

--- src/ui-scroll.coffee
+++ src/ui-scroll.coffee
@@ -281,7 +281,7 @@
 							when 'append' then buffer.push wrapper
 							when 'prepend' then buffer.unshift wrapper

-				adjustBuffer = (rid, finalize) ->
+				adjustBuffer = (rid, adjustAfterFetch) ->

 					promises = []
 					toBePrepended = []
@@ -300,6 +300,7 @@
 									else
 										builder.insertElement wrapper.element, buffer[i-1].element
 									wrapper.op = 'none'
+									keepFetching = keepFetching || wrapper.element.outerHeight(true)
 								when 'insert'
 									if (i == 0)
 										promises = promises.concat (builder.insertElementAnimated wrapper.element)
@@ -319,6 +320,7 @@
 							for wrapper in toBePrepended
 								builder.insertElement wrapper.element
 								wrapper.op = 'none'
+								keepFetching = keepFetching || wrapper.element.outerHeight(true)
 							heightIncrement = builder.bottomDataPos() - bottomPos
 							# adjust padding to prevent it from visually pushing everything down
 							if builder.topPadding() >= heightIncrement
@@ -332,11 +334,18 @@
 						item.scope.$index = first + i for item,i in buffer

 						#log "top {actual=#{builder.topDataPos()} visible from=#{topVisiblePos()} bottom {visible through=#{bottomVisiblePos()} actual=#{builder.bottomDataPos()}}"
+
 						if shouldLoadBottom()
-							enqueueFetch(rid, true)
-						else
-							enqueueFetch(rid, false) if shouldLoadTop()
-						finalize(rid) if finalize
+							# keepFetching = true means that at least one item app/prepended in the last batch had height > 0
+							# !adjustAfterFetch = false means that this is chained fetch (a fetch after another fetch)
+							enqueueFetch(rid, true) if !adjustAfterFetch || keepFetching
+						else
+							if shouldLoadTop()
+								# pending[0] = true means that previous fetch was at the bottom we need to force at least one at the top
+								# BTW there will always be at least 1 element in the pending array because bottom is fetched first
+								enqueueFetch(rid, false) if !adjustAfterFetch || keepFetching || pending[0]
+
+						adjustAfterFetch(rid) if adjustAfterFetch

 						#topVisible is not necessarily the first item in the buffer
 						if pending.length == 0


removed dom utils - replaced with dom-helper

Former-commit-id: 1a97eb33b7cf3f9edca706cfb96eac6accffebf5 [formerly bbc0883d7e056f9220e1e750cdf13918d860a2f2]
Former-commit-id: 9168afb11d72b46200fed5cfe68ca2d6f31cb4d3

--- src/utils/dom.coffee
+++ src/utils/dom.coffee
@@ -1,33 +0,0 @@
-Utils = {}
-
-###
-Remove an element and provide a function that inserts it into its original position
-https://developers.google.com/speed/articles/javascript-dom
-@param element {Element} The element to be temporarily removed
-@return {Function} A function that inserts the element into its original position
-###
-Utils.removeToInsertLater = (element) ->
-  parentNode = element.parentNode
-  nextSibling = element.nextSibling
-  parentNode.removeChild element
-  ->
-    if nextSibling
-      parentNode.insertBefore element, nextSibling
-    else
-      parentNode.appendChild element
-    return
-
-
-###
-fastest possible way to destroy all sub nodes (aka childs)
-http://jsperf.com/innerhtml-vs-removechild/15
-@param element {Element} The element for which all childs should be removed
-###
-Utils.removeAllChilds = (element) ->
-  count = 0
-  while element.firstChild
-    count++
-    element.removeChild element.firstChild
-  return
-
-module.exports = Utils


Add `activateItem` config to `Workspace::open`

Now that we have deleted `Project::open(...)`, all the methods that previously
used it need to switch to `Workspace::open(...)` instead. These two methods,
however, are not idempotent because the latter performs additional operations.

Since some specs relied on not activating an item after opening it, this commit
adds a parameter to prevent such activation.

Refs: https://github.com/atom/tabs/commit/1ebcf166c73a9d55932a78b1246dbd5a50077ec5

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -336,6 +336,8 @@
   #     item will be opened in the rightmost pane of the current active pane's row.
   #   * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
   #     containing pane. Defaults to `true`.
+  #   * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem}
+  #     on containing pane. Defaults to `true`.
   #   * `searchAllPanes` A {Boolean}. If `true`, the workspace will attempt to
   #     activate an existing item for the given URI on any pane.
   #     If `false`, only the active pane will be searched for
@@ -374,9 +376,12 @@
   #     initially. Defaults to `0`.
   #   * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
   #     the containing pane. Defaults to `true`.
+  #   * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem}
+  #     on containing pane. Defaults to `true`.
   openSync: (uri='', options={}) ->
     {initialLine, initialColumn} = options
     activatePane = options.activatePane ? true
+    activateItem = options.activateItem ? true

     uri = @project.resolvePath(uri)
     item = @getActivePane().itemForURI(uri)
@@ -384,13 +389,14 @@
       item ?= opener(uri, options) for opener in @getOpeners() when not item
     item ?= @project.openSync(uri, {initialLine, initialColumn})

-    @getActivePane().activateItem(item)
+    @getActivePane().activateItem(item) if activateItem
     @itemOpened(item)
     @getActivePane().activate() if activatePane
     item

   openURIInPane: (uri, pane, options={}) ->
     activatePane = options.activatePane ? true
+    activateItem = options.activateItem ? true

     if uri?
       item = pane.itemForURI(uri)
@@ -417,7 +423,7 @@
           pane = new Pane({items: [item], @config, @confirm})
           @paneContainer.root = pane
         @itemOpened(item)
-        pane.activateItem(item)
+        pane.activateItem(item) if activateItem
         pane.activate() if activatePane

         initialLine = initialColumn = 0


Raising events for mousover/mousout on a single record.

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -72,6 +72,8 @@
         @options.debounce ||= 50
         @options.ticksize ||= 3
         @options.datasets ||= []
+
+        @recordFilter = @options.recordFilter

         # object to hold individual data points / data ranges
         @datasets = {}
@@ -305,15 +307,28 @@

     # Convenience method to hook up a single record elements events
-    setupRecord: (recordElement, color) ->
+    setupRecord: (recordElement, dataset) ->
         recordElement.attr('fill', (record) =>
-            if not @recordFilter or @recordFilter(record)
-                color
+            if not @recordFilter or @recordFilter(record, dataset)
+                dataset.color
             else
                 "transparent"
         )
         .on("mouseover", (record) =>
             params = record[2]
+            @element.dispatchEvent(
+                new CustomEvent('recordMouseover', {
+                    detail: {
+                        dataset: dataset.id,
+                        start: record[0],
+                        end: record[1],
+                        params: params
+                    }
+                    bubbles: true,
+                    cancelable: true
+                })
+            )
+
             if params and (params.id or params.name)
                 @tooltip.transition()
                     .duration(200)
@@ -323,6 +338,18 @@
                     .style("top", (d3.event.pageY - 28) + "px")
         )
         .on("mouseout", (record) =>
+            @element.dispatchEvent(
+                new CustomEvent('recordMouseout', {
+                    detail: {
+                        dataset: dataset.id,
+                        start: record[0],
+                        end: record[1],
+                        params: record[2]
+                    }
+                    bubbles: true,
+                    cancelable: true
+                })
+            )
             @tooltip.transition()
                 .duration(500)
                 .style("opacity", 0)
@@ -331,6 +358,7 @@
             @element.dispatchEvent(
                 new CustomEvent('recordClicked', {
                     detail: {
+                        dataset: dataset.id,
                         start: record[0],
                         end: record[1],
                         params: record[2]
@@ -341,7 +369,7 @@
             )
         )

-    drawRanges: (datasetElement, records, index, color) ->
+    drawRanges: (datasetElement, dataset, records) ->
         datasetElement.selectAll('rect').remove()

         r = datasetElement.selectAll('rect')
@@ -349,16 +377,16 @@

         r.enter().append('rect')
             .attr('x', (record) => @scales.x(new Date(record[0])) )
-            .attr('y', - (@options.ticksize + 3) * index + -(@options.ticksize-2) )
+            .attr('y', - (@options.ticksize + 3) * dataset.index + -(@options.ticksize-2) )
             .attr('width', (record) => @scales.x(new Date(record[1])) - @scales.x(new Date(record[0])) )
             .attr('height', (@options.ticksize-2))
-            .attr('stroke', d3.rgb(color).darker())
+            .attr('stroke', d3.rgb(dataset.color).darker())
             .attr('stroke-width', 1)
-            .call((recordElement) => @setupRecord(recordElement, color))
+            .call((recordElement) => @setupRecord(recordElement, dataset))

         r.exit().remove()

-    drawPoints: (datasetElement, records, index, color) ->
+    drawPoints: (datasetElement, dataset, records) ->
         datasetElement.selectAll('circle').remove()
         p = datasetElement.selectAll('circle')
             .data(records)
@@ -370,15 +398,15 @@
                 else
                     return @scales.x(new Date(a))
             )
-            .attr('cy', - (@options.ticksize + 3) * index - (@options.ticksize - 2) / 2)
-            .attr('stroke', d3.rgb(color).darker())
+            .attr('cy', - (@options.ticksize + 3) * dataset.index - (@options.ticksize - 2) / 2)
+            .attr('stroke', d3.rgb(dataset.color).darker())
             .attr('stroke-width', 1)
             .attr('r', @options.ticksize / 2)
-            .call((recordElement) => @setupRecord(recordElement, color))
+            .call((recordElement) => @setupRecord(recordElement, dataset))

         p.exit().remove()

-    drawPaths: (datasetElement, data, index, color) ->
+    drawPaths: (datasetElement, dataset, data) ->
         @scales.y.domain(d3.extent(data, (d) -> d[1]))

         datasetElement.selectAll('path').remove()
@@ -419,25 +447,25 @@

         datasetElement.append("g")
             .attr("class", "y axis")
-            .attr('fill', color)
+            .attr('fill', dataset.color)
             .call(@axis.y)
-            .attr("transform", "translate("+((index+1)*30)+","+ (-@options.height+29)+")")
+            .attr("transform", "translate("+((dataset.index+1)*30)+","+ (-@options.height+29)+")")

         datasetElement.selectAll('.axis .domain')
             .attr("stroke-width", "1")
-            .attr("stroke", color)
+            .attr("stroke", dataset.color)
             .attr("shape-rendering", "crispEdges")
             .attr("fill", "none")

         datasetElement.selectAll('.axis line')
             .attr("stroke-width", "1")
             .attr("shape-rendering", "crispEdges")
-            .attr("stroke", color)
+            .attr("stroke", dataset.color)

         datasetElement.selectAll('.axis path')
             .attr("stroke-width", "1")
             .attr("shape-rendering", "crispEdges")
-            .attr("stroke", color)
+            .attr("stroke", dataset.color)

     # this function acually draws a dataset

@@ -451,7 +479,7 @@
         color = dataset.color

         if paths and paths.length
-            @drawPaths(datasetElement, paths, index, color)
+            @drawPaths(datasetElement, dataset, paths)
         else if records and records.length
             points = dataset.getRecords().filter((record) =>
                 (@scales.x(new Date(record[1])) - @scales.x(new Date(record[0]))) < 5
@@ -459,8 +487,8 @@
             ranges = dataset.getRecords().filter((record) =>
                 (@scales.x(new Date(record[1])) - @scales.x(new Date(record[0]))) >= 5
             )
-            @drawRanges(datasetElement, ranges, index, color)
-            @drawPoints(datasetElement, points, index, color)
+            @drawRanges(datasetElement, dataset, ranges)
+            @drawPoints(datasetElement, dataset, points)

     # this function triggers the reloading of a dataset (sync)

@@ -594,6 +622,7 @@
                     .attr('id', "dataset-#{id}")

         @datasets[id] = new Dataset({
+            id: id,
             index: index,
             color: definition.color,
             source: definition.source,
@@ -627,6 +656,9 @@
         @redraw()
         true

+    hasDataset: (id) ->
+        return false unless @datasets[id]?
+
     # TODO redraws.
     center: (params...) ->
         start = new Date(params[0])
@@ -682,7 +714,7 @@
 # Dataset utility class for internal use only
 class Dataset
     constructor: (options) ->
-        { @color, @source, @sourceParams, @index, @records, @paths, @lineplot } = options
+        { @id,  @color, @source, @sourceParams, @index, @records, @paths, @lineplot } = options
         @syncDebounced = debounce(@sync, options.debounceTime)

     getSource: ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -32,7 +32,7 @@
   engine.nextLevel()
   </pre></code>

-  @name ne
+  @name nextLevel
   @methodOf Engine#
   @param {Number} steps The number of steps to wait before executing the callback
   @param {Function} callback The callback to be executed.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -2,6 +2,7 @@
   Object.reverseMerge I,
     levels: []
     currentLevel: -1
+    currentLevelName: null

   I.transitioning = false

@@ -29,4 +30,4 @@
     loadLevel level

   reloadLevel: ->
-    loadLevel currentLev
+    loadLevel I.currentLevelName


remove one coffelint warning

remove one coffelint warning

--- src/front/content.coffee
+++ src/front/content.coffee
@@ -39,7 +39,8 @@
       mas.push(elem.nodeName,elem.nodeValue)
     return mas

-  # Function returns iframe url, content,html-atributes,iframe-selectors and iframe-path
+  # Function returns iframe url, content,html-atributes,
+  # iframe-selectors and iframe-path
   [ document.URL,
     document.documentElement.innerHTML,
     getAttribute(document.documentElement.attributes),
@@ -59,4 +60,4 @@
 @_port = chrome.runtime.connect {name: "skeleton"}
 send @_port,getSource()

-  +


testing whoops :trollface:

testing whoops :trollface:

--- resources/assets/coffee/react/_components/bbcode-editor.coffee
+++ resources/assets/coffee/react/_components/bbcode-editor.coffee
@@ -77,7 +77,6 @@
         value: @state.rawValue
         onChange: @onInput # binds to oninput, not onchange
         onKeyDown: @onKeyDown
-        placeholder: 'blah'
         ref: 'body'

       el 'div', className: 'post-editor__footer',


Stop whole-line selection on gutter clicks.

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -106,6 +106,7 @@
     @toggleControls null, @writable
     @aceSession.selection.on 'changeCursor', @onCursorActivity
     $(@ace.container).find('.ace_gutter').on 'click mouseenter', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
+    $(@ace.container).find('.ace_gutter').on 'click', @onGutterClick
     @initAutocomplete aceConfig.liveCompletion ? true

   createACEShortcuts: ->
@@ -796,6 +797,9 @@
     # @ is the gutter element
     Backbone.Mediator.publish 'tome:jiggle-problem-alert', {}

+  onGutterClick: =>
+    @ace.clearSelection()
+
   onDisableControls: (e) -> @toggleControls e, false
   onEnableControls: (e) -> @toggleControls e, @writable
   toggleControls: (e, enabled) ->
@@ -882,6 +886,7 @@

   destroy: ->
     $(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
+    $(@ace?.container).find('.ace_gutter').off 'click', @onGutterClick
     @firepad?.dispose()
     @ace?.commands.removeCommand command for command in @aceCommands
     @ace?.destroy()


removed console.log

removed console.log

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -130,10 +130,8 @@
       # Maybe we could temporarily set ourselves to read-only if we somehow know that a script is active?
       exec: =>
         if @scriptRunning
-          console.log '>>>> BOOM <<<<'
           Backbone.Mediator.publish 'level:shift-space-pressed'
         else
-          console.log '>>>> BAEM <<<<'
           @ace.insert ' '

     addCommand


workers: remove emailer

workers: remove emailer

--- workers/social/lib/social/emailer.coffee
+++ workers/social/lib/social/emailer.coffee
@@ -1,39 +0,0 @@
-module.exports = class Emailer
-  nodemailer = require 'nodemailer'
-
-  email = require './config.email'
-
-  @smtpTransport = nodemailer.createTransport 'SMTP',
-    service: 'SendGrid'
-    auth:
-      user: 'koding'
-      pass: 'DEQl7_Dr'
-
-  @send : (options, callback) ->
-    { From, To, Subject, HtmlBody, TextBody, ReplyTo, Bcc } = options
-    mailOptions =
-      from    : From or email.defaultFromMail
-      to      : To
-      subject : Subject
-
-    mailOptions.text    = TextBody  if TextBody
-    mailOptions.html    = HtmlBody  if HtmlBody
-    mailOptions.replyTo = ReplyTo   if ReplyTo
-    mailOptions.bcc     = Bcc       if Bcc
-
-    # console.log mailOptions
-    setTimeout ->
-      Emailer.smtpTransport.sendMail mailOptions, (error, response) ->
-        # console.log 'got a response', arguments
-        if error
-          # console.log error
-          callback error
-        else
-          callback null, response
-    , 1000 / 20
-
-  @simulate : (options, callback) ->
-
-    setTimeout ->
-      console.log "[SIMULATION] EMAIL SENT TO #{options.To}"
-    , 250


remove last blank line

remove last blank line

--- client/app/views/calendar_popover.coffee
+++ client/app/views/calendar_popover.coffee
@@ -201,4 +201,4 @@
                 @selfclose()

         unless noError
-            console.log @model.validationError+            console.log @model.validationError


Make NS for components on extensions

Make NS for components on extensions

--- client/lanes/extension/Extensions.coffee
+++ client/lanes/extension/Extensions.coffee
@@ -17,7 +17,7 @@
             instance?.setBootstrapData?(data)

     makeNamespace: (identifier)->
-        for ns in ['Models','Views','Controllers','Screens']
+        for ns in ['Models','Views','Controllers','Screens','Components']
             Lanes.namespace("#{identifier}.#{ns}")

     controlling: ->


Fix display of fullscreen device when embedded in an in iframe.

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -161,17 +161,17 @@

 	_shouldRenderFullScreen: ->

+		if not @_device
+			return true
+
+		if @fullScreen is true
+			return true
+
+		if @deviceType is "fullscreen"
+			return true
+
 		if Utils.isInsideIframe()
 			return false
-
-		if not @_device
-			return true
-
-		if @fullScreen is true
-			return true
-
-		if @deviceType is "fullscreen"
-			return true

 		if Utils.deviceType() is "phone" and Utils.deviceType() is @_device.deviceType
 			return true


fix the scrollcomponent to not jump to the top when its content gets updated

--- framer/Components/ScrollComponent.coffee
+++ framer/Components/ScrollComponent.coffee
@@ -143,9 +143,8 @@
 		return unless @content

 		contentFrame = @calculateContentFrame()
-		contentFrame.x = contentFrame.x + @_contentInset.left
-		contentFrame.y = contentFrame.y + @_contentInset.top
-		@content.frame = contentFrame
+		@content.width = contentFrame.width
+		@content.height = contentFrame.height

 		constraintsFrame = @calculateContentFrame()
 		constraintsFrame =


Restore Slider Defaults

Restore Slider Defaults

--- framer/Components/SliderComponent.coffee
+++ framer/Components/SliderComponent.coffee
@@ -36,7 +36,7 @@
 			borderRadius: 50
 			clip: false
 			width: 300
-			height: 6
+			height: 10
 			value: 0
 			knobSize: 30


Minor refactoring

Minor refactoring

--- scripts/steam.coffee
+++ scripts/steam.coffee
@@ -162,26 +162,22 @@
                 msg.reply "The custom URL you have entered (\"#{customURL}\") does not exist."
                 return

-            if steamData()[object.response.steamid]?
-                steamData()[object.response.steamid].url = customURL
-            else
-                steamData()[object.response.steamid] = url: customURL
+            steamData()[object.response.steamid] or= {}
+            steamData()[object.response.steamid].url = customURL

             handler object.response.steamid

     getCommunityID = (steamID) ->
-        if steamData()[steamID]?.communityID?
-            return steamData()[steamID].communityID
+        if steamData()[steamID]?.cID?
+            return steamData()[steamID].cID

         # 64 -> 32
         buffer = new Buffer 8
         buffer.writeUInt64LE steamID, 0
         communityID = buffer.readUInt32LE 0

-        if steamData()[steamID]?
-            steamData()[steamID].communityID = communityID
-        else
-            steamData()[steamID] = communityID: communityID
+        steamData()[steamID] or= {}
+        steamData()[steamID].cID = communityID

         communityID


Update i18n-zh-tw.coffee

Update i18n-zh-tw.coffee

added Chinese translations to the item log.

--- addon/initializers/i18n-zh-tw.coffee
+++ addon/initializers/i18n-zh-tw.coffee
@@ -167,13 +167,13 @@
       "description_placeholder" : "物品的描述、大小及數量等。如要增加或修改這項物品的相片，請點擊圖像。"

   "item_log":
-    "added": "added item"
-    "edited": "edited item"
-    "accepted": "accepted item"
-    "rejected": "rejected item"
-    "submitted": "submitted item"
-    "received": "received item"
-    "missing": "marked item missing"
+    "added": "加入此物"
+    "edited": "修改此物"
+    "accepted": "接受此物"
+    "rejected": "拒絕此物"
+    "submitted": "捐出此物"
+    "received": "收到此物"
+    "missing": "將此物的狀況改為'失蹤'"

   "edit_images":
     "add_photo" : "加入圖片"


add aggregate type to repository commit changes

--- source/infrastructure/repository.coffee
+++ source/infrastructure/repository.coffee
@@ -27,6 +27,7 @@

     # Save the changes into the commit store
     changes =
+      aggregateType: aggregate.toString()
       events: aggregate.getEvents?() ? []
       commands: aggregate.getCommands?() ? []
     expectedVersion ?= aggregate.getVersion()


Use 'parse' function to set necessary properties on the page

--- core/app/backbone/models/evidence.coffee
+++ core/app/backbone/models/evidence.coffee
@@ -7,11 +7,16 @@
   initialize: (attributes, options) ->
     @_fact_id = options.fact_id ? @collection.fact.id

-    @on 'change:users', =>
-      @opinionaters().reset @get('users')
+    @on 'change', =>
+      @updateOpinionators()
+
+    @updateOpinionators()

   opinionaters: ->
-    @_opinionaters ?= new InteractorsPage @get('users') ? [],
+    @_opinionaters ?= new InteractorsPage null,
       fact_id: @_fact_id
       type: @get('type')
       perPage: 7
+
+  updateOpinionators: ->
+    @opinionaters().reset @opinionaters().parse(@attributes)


Removed unused class name

Removed unused class name

--- core/app/backbone/views/user_view.coffee
+++ core/app/backbone/views/user_view.coffee
@@ -1,4 +1,3 @@
 class window.UserView extends Backbone.Marionette.ItemView
   tagName: "article"
-  className: "channel-listing-owner-block"
   template: "users/user_large"


Function call bugfix.

Function call bugfix.

--- server/manage.coffee
+++ server/manage.coffee
@@ -46,7 +46,7 @@

     s3 = new AWS.S3()

-    list = blocking s3, s3.listObjects
+    list = blocking(s3, s3.listObjects)
       Bucket: 'arxiv'
       Prefix: 'pdf/'
       RequestPayer: 'requester'


Router: consistent terminology 'definition'

--- server/router.coffee
+++ server/router.coffee
@@ -35,12 +35,12 @@

   # Given an object with 'controller' and 'action' properties,
   # return the corresponding action function.
-  getAction: (spec) ->
-    controller = @getController(spec.controller)
-    controller[spec.action]
+  getAction: (definition) ->
+    controller = @getController(definition.controller)
+    controller[definition.action]

   # This is the method that renders the request.
-  getHandler: (action, routeInfo) ->
+  getHandler: (action, definition) ->
     router = @

     (req, res, next) ->
@@ -66,7 +66,7 @@
           return router.handleErr(err, req, res) if err

           # Set any headers based on route.
-          res.set(router.getHeadersForRoute(routeInfo))
+          res.set(router.getHeadersForRoute(definition))

           res.type('html').end(html)
           router.stashPerf(req, "render", new Date - start)
@@ -91,10 +91,10 @@
       res.type('text').send(text)

-  getHeadersForRoute: (routeInfo) ->
+  getHeadersForRoute: (definition) ->
     headers = {}
-    if routeInfo.maxAge?
-      headers['Cache-Control'] = "public, max-age=#{routeInfo.maxAge}"
+    if definition.maxAge?
+      headers['Cache-Control'] = "public, max-age=#{definition.maxAge}"
     headers

   # stash performance metrics, if handler available


report render error

report render error

--- server/router.coffee
+++ server/router.coffee
@@ -36,6 +36,9 @@
       start = new Date;
       res.render template, locals: data, app: req.appContext, req: req, (err, html) ->
         # TODO! consider setting pragma-no-cache headers based on route!
+        if (err)
+          console.log("RENDER ERROR", err)
+        return handleErr(err, req, res) if err
         res.end(html)
         stashPerf(req, "render", new Date - start)
         next()


Fix typo in previous test update

Fix typo in previous test update

--- spec/server/functional/user.spec.coffee
+++ spec/server/functional/user.spec.coffee
@@ -1628,7 +1628,7 @@
     url = utils.getUrl("/db/user/#{user.id}/avatar")
     [res] = yield request.getAsync({url, followRedirect: false})
     expect(res.statusCode).toBe(302)
-    expect(res.headers.location).toBe('default=http://localhost:3001/file/db/thang.type/1234/portrait.png')
+    expect(res.headers.location).toBe('http://localhost:3001/file/db/thang.type/1234/portrait.png')

   it 'allows overriding the fallback value', utils.wrap ->
     user = yield utils.initUser()


toogles map visibility

toogles map visibility

--- app/assets/javascripts/map.js.coffee
+++ app/assets/javascripts/map.js.coffee
@@ -6,6 +6,10 @@
     if maps.length > 0
       $.each maps, (index, map) ->
         App.Map.initializeMap map
+
+    $('.js-toggle-map').on
+        click: ->
+          App.Map.toogleMap()

   initializeMap: (element) ->

@@ -76,3 +80,7 @@
       $(removeMarkerSelector).on 'click', removeMarker
       map.on    'zoomend', updateFormfields
       map.on    'click',   moveOrPlaceMarker
+
+  toogleMap: ->
+      $('.map').toggle()
+      $('.location-map-remove-marker-button').toggle()


* wating befor we send the offer

* wating befor we send the offer

--- app/assets/javascripts/webrtc.coffee
+++ app/assets/javascripts/webrtc.coffee
@@ -287,7 +287,9 @@
       @doGetUserMedia( =>
         peer = @addPeer({uuid:peer_id, sendOffer:false})
         # now we have the peer so lets try this again.
-        @sessionDescription(config)
+        setTimeout(=>
+          @sessionDescription(config)
+        ,3500)
       )
     else
       remote_description = config.session_description
@@ -296,34 +298,34 @@
       desc = new RTCSessionDescription(remote_description)
       console.log("Description Object: ", desc)

-      setTimeout(=>
-        peer.setRemoteDescription(desc, =>
-          console.log("setRemoteDescription succeeded")
-          if (remote_description.type == "offer")
-            console.log("Creating answer");
-            peer.createAnswer((local_description) =>
-              console.log("Answer description is: ", local_description);
-              peer.setLocalDescription(local_description
-              , =>
-                @send('relay',
-                  {
-                    'type':'sessionDescription'
-                    'actors': [peer_id],
-                    'session_description': local_description
-                  }
-                )
-                console.log("Answer setLocalDescription succeeded")
-              , ->
-                alert("Answer setLocalDescription failed!")
+
+      peer.setRemoteDescription(desc, =>
+        console.log("setRemoteDescription succeeded")
+        if (remote_description.type == "offer")
+          console.log("Creating answer");
+          peer.createAnswer((local_description) =>
+            console.log("Answer description is: ", local_description);
+            peer.setLocalDescription(local_description
+            , =>
+              @send('relay',
+                {
+                  'type':'sessionDescription'
+                  'actors': [peer_id],
+                  'session_description': local_description
+                }
               )
-            , (error) ->
-              console.log("Error creating answer: ", error)
-              console.log(peer)
-            ,sdpConstraints)
-        ,(error) ->
-          console.log("setRemoteDescription error: ", error)
-        )
-      ,1000)
+              console.log("Answer setLocalDescription succeeded")
+            , ->
+              alert("Answer setLocalDescription failed!")
+            )
+          , (error) ->
+            console.log("Error creating answer: ", error)
+            console.log(peer)
+          ,sdpConstraints)
+      ,(error) ->
+        console.log("setRemoteDescription error: ", error)
+      )
+


small check before adding a local stream

--- app/assets/javascripts/webrtc.coffee
+++ app/assets/javascripts/webrtc.coffee
@@ -232,8 +232,11 @@
         $('body').append(remote_media)

     # Add our local stream
-    console.log('adding local stream')
-    peer_connection.addStream(@localStream)
+    if( @localStream? )
+      console.log('adding local stream')
+      peer_connection.addStream(@localStream)
+    else
+      console.log('[WARN] - localStream could not be added to peer_connection, localStream',@localStream)

     # Only one side of the peer connection should create the
     # offer, the signaling server picks one to be the offerer.


use api.json vs api.body

use api.json vs api.body

--- shared/syncer.coffee
+++ shared/syncer.coffee
@@ -26,7 +26,7 @@
   # Put the data as form data if POST or PUT,
   # otherwise query string.
   if verb is 'POST' or verb is 'PUT'
-    req.body = options.data
+    req.json = options.data
   else
     _.extend req.query, options.data


add import http

add import http

--- snippets/meteor.cson
+++ snippets/meteor.cson
@@ -1,22 +1,7 @@
 '.source.js':
-  'Create with data':
-      'prefix': 'withData'
-      'body': """
-        @withData(({$1}) => {
-          const handler = Weeshing.subscribe('$2')
-          const isLoading = !handler.ready()
-          return {isLoading}
-        })
-      """
-  'Create a container':
-    'prefix': 'createCon'
-    'body': """
-      export default createContainer(({$1}) => {
-        const handler = Weeshing.subscribe('')
-        const isLoading = !handler.ready()
-        return {isLoading}
-      }, ${2:Component})
-    """
   'Import apollo helpers':
     'prefix': 'impapollohelpers'
     'body': """import {createModifier, validate, FieldError} from 'meteor/orionsoft:apollo-helpers'"""
+  'Import meteor http':
+    'prefix': 'imphttp'
+    'body': """import {HTTP} from 'meteor/http'"""


Add auth stats to librato

Add auth stats to librato

--- workers/auth/lib/auth/main.coffee
+++ workers/auth/lib/auth/main.coffee
@@ -5,7 +5,13 @@

 AuthWorker = require './authworker'

-{authWorker} = require argv.c
+{authWorker,librato} = require argv.c
+
+processMonitor = (require 'processes-monitor').start
+  name : "Auth Worker #{process.pid}"
+  stats_id: "worker.auth." + process.pid
+  interval : 30000
+  librato: librato

 authWorker = new AuthWorker koding, authWorker.authResourceName
 authWorker.connect()


feed controller: use groupController.ready instead of .changeGroup

--- client/Social/Feeder/FeedController.coffee
+++ client/Social/Feeder/FeedController.coffee
@@ -175,7 +175,7 @@
     then group = KD.config.entryPoint.slug
     else group = 'koding'

-    groupsController.changeGroup group, =>
+    groupsController.ready =>

       currentGroup = group
       feedId = "#{currentGroup}-#{feedId}"


activity/app: imitate pm styling on bot channels

--- client/activity/lib/activityappview.coffee
+++ client/activity/lib/activityappview.coffee
@@ -57,6 +57,7 @@

     @tabs.on 'PaneDidShow', (pane) =>
       if type = pane.getData()?.typeConstant
+        type = 'privatemessage'  if type is 'bot'
         @tabs.setAttribute 'class', kd.utils.curry 'kdview kdtabview', type

     { router } = kd.singletons


Simplify NotificationService

Simplify NotificationService

--- client/services/notificationservice.coffee
+++ client/services/notificationservice.coffee
@@ -12,23 +12,18 @@
       {
         title: title,
         message: message,
-      }, [
-        {
-          event: 'yes',
-          callback: yesCallback,
-        },
-        {
-          event: 'no',
-          callback: noCallback,
-        },
-      ])
+      }, {
+        "yes": yesCallback
+        "no": noCallback
+      }
+    )

   # Show a Bootstrap modal based on a template that gets rendered on-the-fly
   _showModal: (templateName, data, callbacks) ->
     html = Blaze.toHTMLWithData(Template[templateName], data)
     $modal = $(html)
     $modal.modal({
-      callbacks: callbacks,
+      callbacks: R.map((([key, value]) -> {"event": key, callback: value}), R.toPairs(callbacks))
     })
     $(".modal-default").focus()
     # After the modal is hidden, remove the DOM node


Relax code bank section names character restrictions

--- app/schemas/models/level.coco.coffee
+++ app/schemas/models/level.coco.coffee
@@ -406,8 +406,8 @@
   methodsBankList: c.array {title: 'Methods Bank List'}, c.object {
     properties: {
       name: c.shortString(title: 'Name'),
-      section: c.shortString(title: 'Methods Bank Section', pattern: '^[a-z]+$'),
-      subSection: c.shortString(title: 'Methods Bank Sub-Section', pattern: '^[a-z]+$'),
+      section: c.shortString(title: 'Methods Bank Section', pattern: /^\w[\w ]*$/),
+      subSection: c.shortString(title: 'Methods Bank Sub-Section', pattern: /^\w[\w ]*$/),
       componentName: c.shortString(title: 'Level Component Name', description: 'Level Component to use for documentation in case there are multiple components with same property\'s documentation'),
     }
   }


bind => on

bind => on

--- test/bindable.coffee
+++ test/bindable.coffee
@@ -1,7 +1,7 @@
 module "Bindable"

 test "#bind and #trigger", 1, ->
-  o = Object.extend({}, Bindable())
+  o = Cor.extend({}, Bindable())

   o.bind("test", -> ok true)


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -100,6 +100,6 @@

 # TODO: tests to make sure value isn't overwritten if the instance variable already exists
 # TODO: tests to make sure value is defaulted to 0, but only if the instance variable doesn't exist
-test "ini"
+test "initialize"

 module()


Increase setup timeout

Increase setup timeout

--- test/coverage.coffee
+++ test/coverage.coffee
@@ -3,6 +3,9 @@

 suite('coverage', () ->
   setup(() ->
+    # give the code coverage tool time to work
+    @timeout(10000)
+
     # Use compiled javascript for debugging
     if typeof v8debug != 'object'
       global.ko = require('../dist/ko-typed.apply.coffee')(require('knockout'))


Always close please wait overlay on confirmation

Do not rely on a potential redirect to remove the waiting animation.

--- app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee
@@ -78,8 +78,9 @@
         type: "DELETE"
         error: (xhr, status, error) ->
           type = if xhr.status == 403 then 'warning' else 'error'
+          Alchemy.growl(xhr.responseText || error, type)
+        complete: () ->
           Alchemy.pleaseWaitOverlay(false)
-          Alchemy.growl(xhr.responseText || error, type)

   $.extend(options, opts)
   Alchemy.openConfirmDialog options.message, options


Needed to use png for logo because of transparency.

--- app/assets/javascripts/app/lib/app_post/image_service.js.coffee
+++ app/assets/javascripts/app/lib/app_post/image_service.js.coffee
@@ -41,6 +41,10 @@
     #@resize()
     @canvas.toDataURL( type, quallity )

+  toDataURLForAvatar: ( x, y ) =>
+    @resize( x * 2, y * 2 )
+    @toDataURL( 'image/jpeg', 0.7 )
+
   toDataURLForApp: ( x, y ) =>
     @resize( x * 2, y * 2 )
-    @toDataURL( 'image/jpeg', 0.7 )
+    @toDataURL( 'image/png', 0.7 )


reset form after inviting member

reset form after inviting member

--- app/assets/javascripts/controllers/projects/show_project.coffee
+++ app/assets/javascripts/controllers/projects/show_project.coffee
@@ -51,9 +51,11 @@

     $scope.addMember = (form) ->
       Resources.Membership.save { project_id: $scope.project.id }, $scope.newMembership, (response) ->
+        $scope.newMembership = { role: "viewer" }
         form.$setPristine()
         flash.success = "@#{response.user.username} has been added to this project"
         $scope.refreshProject()
       , (failure) ->
-        flash.error = failure.data.errors[0] || failure.data.error
+        flash.error = failure.data.errors?[0] || failure.data.error
+
 ])


While in Quick Reading mode, reduce the time after user has finished scrolling from 500ms to 250ms.

--- app/assets/javascripts/ng-services/ng-QuickReadingSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-QuickReadingSvc.js.coffee
@@ -25,5 +25,5 @@
             id = $(this).attr 'data-entry-id'
             entry = findSvc.find_entry id
             entrySvc.read_entry entry
-      , 500
+      , 250
 ]


this router should return a new instance of venta to work with

--- app/assets/javascripts/routes/ventas/ventas_new_route.js.coffee
+++ app/assets/javascripts/routes/ventas/ventas_new_route.js.coffee
@@ -1,3 +1,6 @@
 Bandango.VentasNewRoute = Ember.Route.extend
   beforeModel: ->
     Bandango.comprobantesHelper.loadComprobantes(@get("store"))
+
+  model: ->
+    @get("store").createRecord("venta")


ignoring problematic test

ignoring problematic test

--- spec/e2e.spec.coffee
+++ spec/e2e.spec.coffee
@@ -145,7 +145,7 @@
          createRequest context
          waitsFor ( -> context.passed ), 'request to finish', 1000

-      it 'should be able to edit an endpoint through PUT', ->
+      xit 'should be able to edit an endpoint through PUT', ->
          id = 2
          endpoint = ce.clone endpointData[id-1]
          context.url = "/#{id}"


Fixed really dumb bug with multiple '+add's

--- bot.iced
+++ bot.iced
@@ -85,7 +85,7 @@

 			if user
 				for transaction in user.history
-					if transaction.status is "pending" and transaction.amount is "add"
+					if transaction.status is "pending" and transaction.type is "add"
 						bot.sendMessage chatterID, "You already have an +add request pending"
 						return bot.sendMessage chatterID, "#{transaction.amount} DOGE are pending to #{transaction.address}. If the request isn't completed in 30 minutes, it will automatically be cancelled."


Prevent recursive calls in same invoke.

Prevent recursive calls in same invoke.

--- src/invoke/invoke.coffee
+++ src/invoke/invoke.coffee
@@ -9,9 +9,14 @@

 {isFunction, isGeneratorFn} = require '../utils'

+invoked = {}
+
 # Invoke delegates to one of the above
 invoke = (name, opts, cb) ->
   log.debug 'invoke'
+
+  return if invoked[name]
+  invoked[name] = true

   # Calling cake's invoke ensures that options are passed to us correctly as
   # well as ensuring the normal missing task error is shown.
@@ -24,6 +29,7 @@
   opts = Object.assign options, opts

   done = (err) ->
+    invoked = {}
     (cb err) if isFunction cb

   invokeAction = (err) ->


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -100,6 +100,6 @@

 # TODO: tests to make sure value isn't overwritten if the instance variable already exists
 # TODO: tests to make sure value is defaulted to 0, but only if the instance variable doesn't exist
-test "initialize"
+test "initialize pro"

 module()


adding in .off to unhook onbeforeunload, and adding in save if route is changed

--- src/components/confirm-leave-mixin.cjsx
+++ src/components/confirm-leave-mixin.cjsx
@@ -5,6 +5,13 @@
   # getId: -> id
   componentDidMount: ->
     $(window).on("beforeunload", @confirmLeave)
+  componentWillUnmount: ->
+    $(window).off("beforeunload", @confirmLeave)
+
+    {store, actions} = @getFlux()
+    if store.isChanged(@getId())
+      if confirm('You have unsaved changes.  Would you like to save before you leave?')
+        actions.save(@getId())

   confirmLeave: ->
     {store} = @getFlux()


Add booksplash to Readings component

Add booksplash to Readings component

Not sure why it was on Interactives.

--- src/components/task-step/all-steps.cjsx
+++ src/components/task-step/all-steps.cjsx
@@ -17,11 +17,14 @@

 Reading = React.createClass
   displayName: "Reading"
-  mixins: [StepMixin, StepFooterMixin]
+  mixins: [StepMixin, StepFooterMixin, BookContentMixin]
   isContinueEnabled: -> true
   onContinue: ->
     @props.onStepCompleted()
     @props.onNextStep()
+
+  getSplashTitle: ->
+    TaskStepStore.get(@props.id)?.title or ''

   renderBody: ->
     {id} = @props
@@ -31,14 +34,11 @@

 Interactive = React.createClass
   displayName: "Interactive"
-  mixins: [StepMixin, StepFooterMixin, BookContentMixin]
+  mixins: [StepMixin, StepFooterMixin]
   isContinueEnabled: -> true
   onContinue: ->
     @props.onStepCompleted()
     @props.onNextStep()
-
-  getSplashTitle: ->
-    TaskStepStore.get(@props.id)?.title or ''

   renderBody: ->
     {id} = @props


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/tweening.coffee
+++ test/tweening.coffee
@@ -8,7 +8,7 @@
     x: 10

   11.times (i) ->
-    o.update()
+    o.update(1)

     equals o.I.x, i


add natalia to feedback notifications if feedback type is wrong

--- noddy/service/oabutton/api.coffee
+++ noddy/service/oabutton/api.coffee
@@ -286,10 +286,14 @@

 API.add 'service/oab/bug',
   post: () ->
+    whoto = ['help@openaccessbutton.org']
+    try
+      if this.request.body?.form is 'wrong'
+        whoto.push 'requests@openaccessbutton.org'
     API.mail.send {
       service: 'openaccessbutton',
       from: 'help@openaccessbutton.org',
-      to: ['help@openaccessbutton.org'],
+      to: whoto,
       subject: 'Feedback form submission',
       text: JSON.stringify(this.request.body,undefined,2)
     }


removed unused parameter

removed unused parameter

--- core/app/backbone/models/discussion.coffee
+++ core/app/backbone/models/discussion.coffee
@@ -1,7 +1,6 @@
 class window.Discussion extends Backbone.Model
   initialize: (opts) ->
     @_fact = opts.fact
-    @_relations = opts.relations
     @_type = opts.type

   relations: -> @_relations ?= @getFactRelations()


Add approve button to AppView for super-admins to approve app

--- client/app/Applications/Apps.kdapplication/Views/appview.coffee
+++ client/app/Applications/Apps.kdapplication/Views/appview.coffee
@@ -31,6 +31,23 @@
             callback? err
       ]
     , app
+
+    if KD.checkFlag 'super-admin'
+      @approveButton = new KDToggleButton
+        style           : "kdwhitebtn"
+        dataPath        : "approved"
+        defaultState    : if app.approved then "Disapprove" else "Approve"
+        states          : [
+          "Approve", (callback)->
+            app.approve yes, (err)->
+              callback? err
+          "Disapprove", (callback)->
+            app.approve no, (err)->
+              callback? err
+        ]
+      , app
+    else
+      @approveButton = new KDView

     app.checkIfLikedBefore (err, likedBefore)=>
       if likedBefore
@@ -52,7 +69,7 @@
           parentId : null
           callback : (item)=>
             appsController.installApp app, app.versions[item.data.id], (err)=>
-              @hideLoader()
+              if err then warn err

       @installButton = new KDButtonViewWithMenu
         title     : "Install Now"
@@ -115,6 +132,7 @@

         {{ @putThumb #(manifest)}}

+      {{> @approveButton}}

{{#(title)}}by {{#(manifest.author)}}


graph: reduce profile feed entry bandaid to 3

--- workers/social/lib/social/models/graph/activity.coffee
+++ workers/social/lib/social/models/graph/activity.coffee
@@ -122,7 +122,7 @@
         # we have maximum call stack size error from bongo,
         # while sending the result back to client
         # this is a bandaid for it
-        limitCount : 5 #requestOptions.limit
+        limitCount : 3 #requestOptions.limit
         skipCount  : requestOptions.skip

       query = QueryRegistry.activity.profilePage {facetQuery, orderBy}


Shorter version

Shorter version

--- client/app/Applications/Chat.kdapplication/AppController.coffee
+++ client/app/Applications/Chat.kdapplication/AppController.coffee
@@ -36,7 +36,7 @@
   handleChatRequest:(request, callback)->
     {invitee, publicName} = request

-    if invitee isnt KD.whoami().profile.nickname
+    if invitee isnt KD.nick()
       throw new Error 'Red alert!  Security breach detected!'

     chatPanel = KD.getSingleton 'chatPanel'


Get share approved state from options

Get share approved state from options

--- client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinesharepopup.coffee
@@ -24,9 +24,8 @@

     super options, data

-    if @getData().isApproved()
-      @isApproved = yes
-      @setClass 'approved'
+    {@isApproved} = options
+    @setClass 'approved'  if @isApproved

     @createArrow()
     @createElements()


show minimize and expand version of stack template preview

--- client/stacks/lib/views/stacks/stacktemplatepreviewmodal.coffee
+++ client/stacks/lib/views/stacks/stacktemplatepreviewmodal.coffee
@@ -9,7 +9,6 @@
   constructor: (options = {}, data) ->

     options.title           = 'Template Preview'
-    options.content         = '
Generated from your account data
'
     options.cssClass        = kd.utils.curry 'stack-template-preview content-modal', options.cssClass
     options.overlay         = yes
     options.overlayOptions  = { cssClass : 'second-overlay' }
@@ -25,6 +24,36 @@
     @addSubView @main = new kd.CustomHTMLView
       tagName : 'main'

+    @main.addSubView expandButton = new kd.ButtonView
+      cssClass : 'solid compact light-gray expand-button'
+      title : 'Expand'
+      callback : =>
+        @unsetClass 'stack-template-preview'
+        @setClass 'expanded-stack-template-preview'
+        @setModalHeight '98%'
+        @setModalWidth '99%'
+        @setOption 'position', { top:10, left:10 }
+        @setPositions()
+        @minimizeButton.show()
+        @resizeOpenPane()
+
+
+    @main.addSubView @minimizeButton = new kd.ButtonView
+      cssClass : 'solid compact light-gray minimize-button hidden'
+      title : 'Minimize'
+      callback : =>
+        @setClass 'stack-template-preview'
+        @unsetClass 'expanded-stack-template-preview'
+        @setOption 'position', 'absolute'
+        @setModalHeight defaultHeight
+        @setModalWidth defaultWidth
+        @setPositions()
+        @minimizeButton.hide()
+
+
+    @main.addSubView new kd.CustomHTMLView
+      partial : "
This Preview is generated from your account data.
"
+
     @main.addSubView new kd.CustomHTMLView
       cssClass : 'has-markdown'
       partial  : applyMarkdown """
@@ -38,6 +67,14 @@
     @createJSONView()

     @tabView.showPaneByIndex 0
+    @tabView.on 'PaneDidShow', => @resizeOpenPane()
+
+    defaultHeight = @getHeight()
+    defaultWidth = @getWidth()
+
+  resizeOpenPane: ->
+
+    return window.dispatchEvent new Event 'resize'

   createYamlView: ->


Refactoring: coffee style syntax.

Refactoring: coffee style syntax.

--- src/lib/voicetext.coffee
+++ src/lib/voicetext.coffee
@@ -65,8 +65,7 @@
     @

   emotion_level: (lvl)->
-    if this._speaker == this.SPEAKER.SHOW
-      return this
+    return @ if @_speaker is @SPEAKER.SHOW
     for k,v of @EMOTION_LEVEL
       if lvl is v
         @_emotion_level = v


Allow passing One Time Password to login

Include the X-GitHub-OTP header if otp option is passed in order to
support two factor authentication

--- src/octonode/auth.coffee
+++ src/octonode/auth.coffee
@@ -55,6 +55,10 @@
           'Content-Type': 'application/json'
           'User-Agent': 'octonode/0.3 (https://github.com/pksunkara/octonode) terminal/0.0'
       options.url.auth = "#{@options.username}:#{@options.password}"
+
+      # Check for a one time password (for two factor authentication)
+      if @options.otp then options.headers['X-GitHub-OTP'] = @options.otp
+
       request options, (err, res, body) ->
         if err?
           callback err


fix bug where after pasting in firefox, we type backwards

--- src/paste-manager.coffee
+++ src/paste-manager.coffee
@@ -34,6 +34,7 @@
         delta.endLength += (@editor.getLength() - (range.end.index - range.start.index))
         delta.startLength = oldDocLength
         @editor.applyDelta(delta, { source: 'user' })
+        @editor.root.focus()
         @editor.setSelection(new Scribe.Range(@editor, range.start.index + lengthAdded, range.start.index + lengthAdded))
       )
     )