add hide views of models

add hide views of models

--- lib/client/javascripts/nali/collection.js.coffee
+++ lib/client/javascripts/nali/collection.js.coffee
@@ -161,6 +161,10 @@
         model.hide viewName
     @

+  hide: ( viewName, delay ) ->
+    model.hide viewName, delay for model in @
+    @
+
   prepareViewToShow: ( view ) ->
     unless view in @::toShowViews
       @::toShowViews.push view


use message not data

use message not data


Don't convert absolute paths

Don't convert absolute paths

--- src/bin/noflo.coffee
+++ src/bin/noflo.coffee
@@ -24,7 +24,9 @@
         if arg.indexOf(".json") is -1 and arg.indexOf(".fbp") is -1
             console.error "#{arg} is not a NoFlo graph file, skipping"
             continue
-        noflo.loadFile "#{nofloRoot}/#{arg}", (network) ->
+        if arg.substr(0, 1) isnt "/"
+            arg = "#{nofloRoot}/#{arg}"
+        noflo.loadFile arg, (network) ->
             return unless options.interactive

             shell.app.network = network


Make Od work

Make Od work

--- prototypes/using-sirius/coffeescripts/extensions/Od.coffee
+++ prototypes/using-sirius/coffeescripts/extensions/Od.coffee
@@ -4,12 +4,30 @@

 window.sirius.Od::resolve_references = (deferred, object_with_id) ->
   deferred.push =>
-    [begin, end] = [@get('begin'), @get('end')]
-    [begin_node, end_node] = [object_with_id.node[begin], object_with_id.node[end]]
-    @set('begin_node', begin_node)
-    @set('end_node', end_node)
-    throw "Od instance can't find node for id == #{begin}" unless begin_node
-    throw "Od instance can't find node for id == #{end}" unless end_node
+    [n_origin_id, n_destination_id, l_origin_id, l_destination_id] = [
+      @get('network_id_origin'),
+      @get('network_id_destination'),
+      @get('link_id_origin'),
+      @get('link_id_destination')
+    ]
+#    [begin_node, end_node] = [object_with_id.node[begin], object_with_id.node[end]]
+    [n_origin, n_destination, l_origin, l_destination] = [
+      object_with_id.network[n_origin_id],
+      object_with_id.network[n_destination_id],
+      object_with_id.link[l_origin_id],
+      object_with_id.link[l_destination_id]
+    ]
+#    @set('begin_node', begin_node)
+#    @set('end_node', end_node)
+    @set('network_origin', n_origin)
+    @set('network_destination', n_destination)
+    @set('link_origin', l_origin)
+    @set('link_destination', l_destination)
+
+    unless l_origin and l_destination
+      throw "Od instance can't find link for id == #{begin}"
+    unless n_origin and n_destination
+      throw "Od instance can't find network for id == #{end}"

 window.sirius.Od::encode_references = ->
   @set('begin', @get('begin_node').id)


Ensure we have the deviceId by GETting it from the Resin API

--- src/bootstrap.coffee
+++ src/bootstrap.coffee
@@ -34,10 +34,18 @@
 			return userConfig
 		deviceRegister.register(resinApi, userConfig)
 		.catch DuplicateUuidError, ->
-			return {}
+			resinApi.get
+				resource: 'device'
+				options:
+					filter:
+						uuid: userConfig.uuid
+				customOptions:
+					apikey: userConfig.apiKey
+			.then ([ device ]) ->
+				return device
 		.then (device) ->
 			userConfig.registered_at = Date.now()
-			userConfig.deviceId = device.id if device.id?
+			userConfig.deviceId = device.id
 			fs.writeFileAsync(configPath, JSON.stringify(userConfig))
 		.return(userConfig)
 	.then (userConfig) ->


FPS limiting implemented.

FPS limiting implemented.

--- hyperbolic_tessellation_test_app.coffee
+++ hyperbolic_tessellation_test_app.coffee
@@ -256,11 +256,17 @@
   context.translate 1, 1
   observer.draw cells, tfm, context
   context.restore()
-
+
+lastTime = Date.now()
+fpsMax = 10
+dtMax = 1000.0/fpsMax
 redrawLoop = ->
   if dirty
-    drawEverything()
-    dirty = false
+    t = Date.now()
+    if t - lastTime > dtMax
+      drawEverything()
+      dirty = false
+      lastTime = t
   requestAnimationFrame redrawLoop


clampable

clampable

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -5,6 +5,6 @@

   # Add events and methods here
   self.bind "afterUpdate", ->
-
+
   clamp: ->


Update de-DEcoffee

Update de-DEcoffee

new translations.
please add me to the diplomatpages if you like my work, that would be a great honour.
Thanks^^

--- app/locale/de-DE.coffee
+++ app/locale/de-DE.coffee
@@ -26,20 +26,20 @@
     learn_more: "Mehr lernen"
     classroom_in_a_box: "Ein Klassenraum in-einer-Box um Informatik zu lehren."
     codecombat_is: "CodeCombat ist eine Plattform für Schüler um Informatik zu lernen während sie ein echtes Spiel spielen."
-#    our_courses: "Our courses have been specifically playtested to excel in a classroom setting, even by teachers with little to no prior programming experience."
-#    designed_with: "Designed with teachers in mind"
+    our_courses: "Unsere Kurse wurden genau getestet um im Klassenraum zu bestehen, auch bei Lehrkräften mit wenig bis keiner Programmiererfahrung."
+    designed_with: "Designed um Lehrkräften zu helfen"
     real_code: "Real, getipper Code"
     from_the_first_level: "vom ersten Level an"
-#    getting_students: "Getting students to typed code as quickly as possible is critical to learning programming syntax and proper structure."
-#    educator_resources: "Educator resources"
-#    course_guides: "and course guides"
-#    teaching_computer_science: "Teaching computer science does not require a costly degree, because we provide tools to support educators of all backgrounds."
-#    accessible_to: "Accessible to"
-    everyone: "jedem"
+    getting_students: "Schüler zum getippten Code so schnell wie möglich zu bringen ist entscheidend um Syntax und richtige Struktur zu erlernen."
+    educator_resources: "Lehrresourcen"
+    course_guides: "und Kursanleitungen"
+    teaching_computer_science: "Informatik zu unterrichten benötigt keinen teuren Abschluss, weil wir Tools bereitstellen, die Lehrkräften im Hintergrund helfen."
+    accessible_to: "Zugänglich für "
+    everyone: "jeden"
 #    democratizing: "Democratizing the process of learning coding is at the core of our philosophy. Everyone should be able to learn to code."
-#    forgot_learning: "I think they actually forgot that they were actually learning something."
-#    wanted_to_do: " Coding is something I've always wanted to do, and I never thought I would be able to learn it in school."
-#    why_games: "Why is learning through games important?"
+    forgot_learning: "Ich glaube sie vergessen sogar, dass sie gerade etwas lernen."
+    wanted_to_do: " Programmieren ist etwas was ich immer wollte und ich hätte nie gedacht, dass ich es in der Schule lernen könnte."
+    why_games: "Warum ist lernen durch Spiele wichtig?"
 #    games_reward: "Games reward the productive struggle."
 #    encourage: "Gaming is a medium that encourages interaction, discovery, and trial-and-error. A good game challenges the player to master skills over time, which is the same critical process students go through as they learn."
 #    excel: "Games excel at rewarding"
@@ -47,7 +47,7 @@
 #    kind_of_struggle: "the kind of struggle that results in learning that’s engaging and"
 #    motivating: "motivating"
 #    not_tedious: "not tedious."
-#    gaming_is_good: "Studies suggest gaming is good for children’s brains. (it’s true!)"
+    gaming_is_good: "Studien legen nahe, dass spielen gut für das kindliche Gehirn ist. (Es ist wahr!)"
 #    game_based: "When game-based learning systems are"
 #    compared: "compared"
 #    conventional: "against conventional assessment methods, the difference is clear: games are better at helping students retain knowledge, concentrate and"
@@ -59,17 +59,17 @@
 #    curious: "Curious? Request a demo and we'll show you the ropes"
 #    create_class: "Or create a class and see it for yourself!"
 #    request_demo: "Request a Demo"
-#    create_a_class: "Create a Class"
-#    have_an_account: "Already have an account?"
-#    logged_in_as: "You are currently logged in as"
-#    view_my_classes: "View my classes"
+    create_a_class: "Erstellen Sie eine Klasse"
+    have_an_account: "Haben Sie bereits einen Account?"
+    logged_in_as: "Sie sind zurzeit eingeloggt als"
+    view_my_classes: "Zeige meine Klassen"
 #    computer_science: "Computer science courses for all ages"
 #    show_me_lesson_time: "Show me lesson time estimates for:"
 #    curriculum: "Total curriculum hours:"
-#    ffa: "Free for all students"
-#    lesson_time: "Lesson time:"
+    ffa: "Kostenlos für alle Schüler"
+    lesson_time: "Lektionszeit:"
     coming_soon: "Coming soon!"
-#    courses_available_in: "Courses are available in JavaScript, Python, and Java (coming soon!)"
+    courses_available_in: "Kurse sind verfügbar in JavaScript, Python, und Java (coming soon!)"
 #    boast: "Boasts riddles that are complex enough to fascinate gamers and coders alike."
 #    winning: "A winning combination of RPG gameplay and programming homework that pulls off making kid-friendly education legitimately enjoyable."
 #    run_class: "Everything you need to run a computer science class in your school today, no CS background required."


Update de-DE.coffee

Update de-DE.coffee

--- app/locale/de-DE.coffee
+++ app/locale/de-DE.coffee
@@ -137,9 +137,9 @@
     buy_gems: "Edelsteine kaufen"
     subscription_required: "Abonnement benötigt"
     subscribers_only: "Nur Abonnenten!"
-#    subscribe_unlock: "Subscribe to Unlock!"
-#    subscriber_heroes: "Subscribe today to immediately unlock Amara, Hushbaum, and Hattori!"
-#    subscriber_gems: "Subscribe today to purchase this hero with gems!"
+    subscribe_unlock: "Freischalten mit Abo!"
+    subscriber_heroes: "Mit einem Abonnement direkt Amara, Hushbaum und Hattori freischalten!"
+    subscriber_gems: "Als Abonnent kannst Du diesen Held mit Edelsteinen freischalten!"
     anonymous: "Anonymer Spieler"
     level_difficulty: "Schwierigkeit: "
     play_classroom_version: "Spiele die Klassenraum-Version" # Choose a level in campaign version that you also can play in one of your courses
@@ -150,7 +150,7 @@
     adjust_volume: "Lautstärke anpassen"
     campaign_multiplayer: "Mehrspieler-Abenteuer"
     campaign_multiplayer_description: "... in der Du Kopf-an-Kopf gegen andere Spieler programmierst."
-#    brain_pop_done: "You’ve defeated the Ogres with code! You win!"
+    brain_pop_done: "Programmierter Sieg über die Oger!"

   code:
     if: "wenn" # Keywords--these translations show up on hover, so please translate them all, even if it's kind of long. (In the code editor, they will still be in English.)
@@ -610,9 +610,9 @@
     prompt_button: "Laden betreten"
     recovered: "Vorangegangener Edelsteinkauf rückgängig gemacht. Aktualisiere bitte die Seite."
     price: "x{{gems}} / Monat"
-#    buy_premium: "Buy Premium"
-#    purchase: "Purchase"
-#    purchased: "Purchased"
+    buy_premium: "Kaufe Premium"
+    purchase: "Kauf"
+    purchased: "Gekauft"

   subscribe:
     subscribe_modal_title: "CodeCombat Premium"
@@ -669,22 +669,22 @@
     unsubscribing: "Abonnement wird gekündigt..."
     subscribe_prepaid: "Klicke auf abonnieren, um einen Prepaidcode zu benutzen"
     using_prepaid: "Benutze einen Prepaidcode für ein Monatsabo"
-#    feature_levels: "Access __premiumLevelsCount__ levels available"
-#    feature_gems: "Receive __gems__ gems per month"
-#    feature_heroes: "Unlock exclusive heroes"
-#    feature_games: "Make games for your friends"
-#    feature_websites: "Build websites and apps"
-#    feature_items: "Equip more powerful items"
-#    month_price: "$__price__/mo"
-#    lifetime: "Lifetime Subscription"
-#    lifetime_price: "$__price__"
-#    year_subscription: "Yearly Subscription"
-#    year_price: "$__price__/year"
-#    kids_message_1: "Kids! We'll send an email to your parents so they can purchase a subscription for you."
-#    kids_message_2: "Ask Your Parent"
-#    support_part1: "Need help with payment options? Email"
+    feature_levels: "Zugang zu __premiumLevelsCount__ Leveln"
+    feature_gems: "Du erhälst __gems__ Edelsteine pro Monat"
+    feature_heroes: "Schalte exklusive Helden frei"
+    feature_games: "Kreire Spiele für Deine Freunde"
+    feature_websites: "Programmiere Webseiten und Apps"
+    feature_items: "Nutze machtvollere Gegenstände"
+    month_price: "$__price__/Mon"
+    lifetime: "Lebenslanges Abonnement"
+    lifetime_price: "$__price__"
+    year_subscription: "Jahresabonnement"
+    year_price: "$__price__/Jahr"
+    kids_message_1: "Kids! Wir senden Euren Eltern eine E-Mail, damit Sie das Abonnement für Euch bestellen können"
+    kids_message_2: "Frag Deine Eltern"
+    support_part1: "Brauchst Du Hilfe bei den Zahlungsoptionen? Schicke eine E-Mail an"
 #    support_part2: "support@codecombat.com"
-#    support_part3: "if you have any questions."
+    support_part3: "wenn Du Fragen hast."

 #  announcement:
 #    cuddly_companions: "Cuddly Companions!"
@@ -990,8 +990,8 @@
     please_explain: "(bitte erklären)"
     middle_school: "Mittelschule"
     college_plus: "Hochschule oder höher"
-#    referrer: "How did you hear about us?"
-#    referrer_help: "For example: from another teacher, a conference, your students, Code.org, etc."
+    referrer: "Wie hast du von uns erfahren?"
+    referrer_help: "zum Beispiel: von einem anderen Lehere, auf einer Konferenz, von deinen Schülern, Code.org, etc."
     anything_else: "Weitere Dinge die wir wissen sollten?"
     thanks_header: "Danke dass Sie eine Demo beantragen!" # {change}
     thanks_sub_header: "Vielen Dank für dein Interesse an CodeCombat für deine Schule."
@@ -1075,7 +1075,7 @@
     saved: "Änderungen gespeichert"
     password_mismatch: "Passwörter stimmen nicht überein."
     password_repeat: "Bitte wiederhole dein Passwort."
-#    file_size_limit: "(max file size: 1MB)"
+    file_size_limit: "(max. Dateigröße: 1MB)"

   keyboard_shortcuts:
     keyboard_shortcuts: "Tastaturkürzel"
@@ -1317,8 +1317,8 @@
     current_hero: "Aktueller Held:"
     change_hero: "Held wechseln"
     web_dev_language_transition: "Alle Klassen für diesen Kurs programmieren in HTML / JavaScript.  Klassen, die Python verwendet haben, fangen mit gesonderten Einführungsleveln für JavaScript an, um den Übergang zu erleichtern.  Klassen, die bereits JavaScript verwenden, überspringen die Einführungslevel."
-#    course_membership_required_to_play: "You'll need to join a course to play this level."
-#    license_required_to_play: "Ask your teacher to assign a license to you so you can continue to play CodeCombat!"
+    course_membership_required_to_play: "Du musst einen Kurs belegen, um diese Level zu spielen."
+    license_required_to_play: "Bitte deinen Lehrer Dir eine Lizenz zuzuweisen, damit Du weiter CodeCombat spielen kannst!"

   teacher:
     assigning_course: "Kurs zuweisen"
@@ -1361,7 +1361,7 @@
     latest_complete: "Letztes abgeschlossenes Level"
     enroll_student: "Schüler einschreiben"
     apply_license: "Lizenz anwenden"
-#    revoke_license: "Revoke License"
+    revoke_license: "Lizenz zurücknehmen"
     course_progress: "Kursfortschritt"
     not_applicable: "n. v."
     edit: "bearbeiten"
@@ -1471,8 +1471,8 @@
     cs4_curriculum_desc: "Umfang und Reihenfolge, Unterrichtspläne und mehr für Kurs 4."
     cs1_pairprogramming: "Paar-Programmieraktivität"
     cs1_pairprogramming_desc: "Zeige deinen Schülern eine paarweise Programmierübung. So lernen sie, besser zuzuhören und zu kommunizieren."
-#    gd1: "Game Development 1"
-#    gd1_guide: "Game Development 1 - Project Guide"
+    gd1: "Game-Entwicklung 1"
+    gd1_guide: "Game-Entwicklung 1 - Project Guide"
 #    gd1_guide_desc: "Use this to guide your students as they create their first shareable game project in 5 days."
     wd1: "Web Entwicklung 1"
     wd1_headlines: "Schlagzeilen & Kopfzeilen Aktivität"


Fixing localization issues. (#4380)

Fixing localization issues. (#4380)

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Update pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Update pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Create pt-BR.coffee

* Update pt-BR.coffee

--- app/locale/pt-BR.coffee
+++ app/locale/pt-BR.coffee
@@ -231,7 +231,7 @@
     create_student_header: "Criar conta de Aluno"
     create_teacher_header: "Criar conta de Professor"
     create_individual_header: "Criar conta Individual"
-    email_announcements: "Receber notícias por email." # {change}
+    email_announcements: " Receber notícias por email sobre novas fases e características do CodeCombat." # {change}
     creating: "Criando uma nova conta..."
     sign_up: "Criar conta"
     log_in: "Entre com a senha"
@@ -262,16 +262,16 @@
     teacher_type_2: "Organize sua sala"
     teacher_type_3: "Acessar seus guias de curso"
     teacher_type_4: "Ver progresso do estudante"
-    signup_as_teacher: "Cadastrar como professor"
+    signup_as_teacher: "Cadastrar Professor"
     student_type_1: "Aprenda programação enquanto se diverte jogando!"
     student_type_2: "Jogue com sua classe"
     student_type_3: "Competir em arenas"
     student_type_4: "Escolha seu herói!"
     student_type_5: "Tenha o seu codigo de classe pronto!"
-    signup_as_student: "Cadastrar como estudante"
+    signup_as_student: "Cadastrar Estudante"
     individuals_or_parents: "Individuais e resposáveis"
     individual_type: "Para aprender a programar fora da classe.Os responsáveis devem requerer uma conta aqui."
-    signup_as_individual: "Registrar como individual"
+    signup_as_individual: "Cadastrar Usuário Comum"
     enter_class_code: "Coloque seu código de classe"
     enter_birthdate: "Coloque sua data de aniversário:"
     parent_use_birthdate: "Pais ou responsável, use sua data de nascimento."
@@ -458,7 +458,7 @@
     victory: "Vitória"
     victory_title_prefix: " Vitória "
     victory_title_suffix: " Completado!"
-    victory_sign_up: "Assine para receber atualizações"
+    victory_sign_up: "Assine para salvar progresso"
     victory_sign_up_poke: "Quer receber as últimas novidades por email? Crie uma conta grátis e nós o manteremos informado!"
     victory_rate_the_level: "Avalie o estágio: " # {change}
     victory_return_to_ladder: "Retornar para a progressão"
@@ -676,7 +676,7 @@
     sale_button_title: "Economize ${{discount}} quando você adquirir a assinatura de 1 ano" # {change}
     stripe_description: "Inscrição Mensal"
     stripe_description_year_sale: "Assinatura de 1 Ano (${{discount}} de desconto" # {change}
-#    buy_now: "Buy Now"
+    buy_now: "Compre Agora"
     subscription_required_to_play: "Você precisará se inscrever para jogar este nível."
     unlock_help_videos: "Inscreva-se para desbloquear todos os vídeos tutoriais."
     personal_sub: "Inscrição individual" # Accounts Subscription View below
@@ -752,10 +752,10 @@
 #    wallOfDarkness_name: "Wall of Darkness"
 #    wallOfDarkness_description: "Hide behind a wall of shadows to prevent the gaze of prying eyes."

-#  premium_features:
+  premium_features:
 #    get_premium: "Get
CodeCombat
Premium" # Fit into the banner on the /features page
 #    master_coder: "Become a Master Coder by subscribing today!"
-#    subscribe_now: "Subscribe Now"
+    subscribe_now: "Assine Agora"
 #    hero_blurb_1: "Get access to __premiumHeroesCount__ super-charged subscriber-only heroes! Harness the unstoppable power of Okar Stompfoot, the deadly precision of Naria of the Leaf, or summon \"adorable\" skeletons with Nalfar Cryptor."
 #    hero_blurb_2: "Premium Warriors unlock stunning martial skills like Warcry, Stomp, and Hurl Enemy. Or, play as a Ranger, using stealth and bows, throwing knives, traps! Try your skill as a true coding Wizard, and unleash a powerful array of Primordial, Necromantic or Elemental magic!"
 #    hero_caption: "Exciting new heroes!"
@@ -875,7 +875,7 @@
     matt_title: "Cofundador, CTO" # {change}
     cat_title: "Designer de Jogos" # {change}
     cat_blurb: "Airbender"
-    scott_title: "Cofundado, Engenheiro de Software" # {change}
+    scott_title: "Cofundado, Eng. de Software" # {change}
     scott_blurb: "O Sensato"
     maka_title: "Defensor dos Clientes"
     maka_blurb: "Contador de Histórias"
@@ -906,7 +906,7 @@
     community_description_1: ", com centenas de jogadores que se voluntariam para criar níveis, contribuem para o nosso código para adicionar recursos, corrigir bugs, testam o jogo e até mesmo traduzir o jogo em até 50 idiomas até agora. Funcionários, colaboradores e o site ganham compartilhando ideias e compartilhando esforços, assim como a comunidade open source em geral. O site é construído em vários projetos open source, e nós somos open source para dar a comunidade e fornecer aos jogadores curiosos um projeto familiar para explorar e experimentar. Qualquer um pode se juntar à comunidade CodeCombat! Confira nossa "
     community_description_link: "página de contribuições"
     community_description_2: "para mais informação."
-    number_contributors: "Over 450 contributors have lent their support and time to this project. = Mais de 450 colaboradores deram o seu apoio e tempo a este projeto."
+    number_contributors: "Mais de 450 colaboradores deram o seu apoio e tempo a este projeto."
     story_title: "Nosso história até agora"
     story_subtitle: "Desde 2013, o CodeCombat cresceu de um mero conjunto de esboços para um jogo vivo e próspero."
 #    story_statistic_1a: "5,000,000+"
@@ -922,7 +922,7 @@
     story_long_way_2: "ainda temos muito o que fazer antes de completar a nossa missão, então ..."
     jobs_title: "Venha trabalhar conosco e ajudar a escrever a história do CodeCombat!"
     jobs_subtitle: "Não se encaixa em nenhum, mas está interessado em manter contato? Veja a listagem “Crie o seu”."
-    jobs_benefits: "Benefícios do empregado"
+    jobs_benefits: "Benefícios do Funcionário"
     jobs_benefit_4: "Férias ilimitadas"
     jobs_benefit_5: "Desenvolvimento profissional e apoio à educação continuada - livros e jogos gratuitos!"
     jobs_benefit_6: "Apoio médico"
@@ -1251,10 +1251,10 @@
     teacher: "Professor"
 #    arena: "Arena"
     available_levels: "Fases disponíveis"
-#    started: "started"
-#    complete: "complete"
-#    practice: "practice"
-#    required: "required"
+    started: "iniciado"
+    complete: "completo"
+    practice: "prático"
+    required: "obrigatório"
     welcome_to_courses: "Aventureiros, bem vindos aos Cursos!"
     ready_to_play: "Pronto para jogar?"
     start_new_game: "Começar um novo jogo"


no text on join, move to quit

no text on join, move to quit

--- scripts/eslogger_watcher.coffee
+++ scripts/eslogger_watcher.coffee
@@ -55,7 +55,7 @@
       log = {
         room: res.message.room
         nick: ''
-        message: "#{res.message.user.name} has joined #{res.message.room} (#{res.message.text})"
+        message: "#{res.message.user.name} has joined #{res.message.room}"
       }
       eslogger.logMessageES log, res.message.room, res

@@ -63,6 +63,6 @@
       log = {
         room: res.message.room
         nick: ''
-        message: "#{res.message.user.name} has quit #{res.message.room}"
+        message: "#{res.message.user.name} has quit #{res.message.room} (#{res.message.text})"
       }
       eslogger.logMessageES log, res.message.room, res


XY: Placeholders for abilities

XY: Placeholders for abilities

--- server/xy/data/abilities.coffee
+++ server/xy/data/abilities.coffee
@@ -43,6 +43,7 @@
 # New unique abilities

 # TODO: Aroma Veil
+makeAbility "Aroma Veil"

 # Implemented in makeAuraAbility
 makeAbility "Aura Break"
@@ -52,12 +53,16 @@
     return true  if move?.hasFlag('bullet')

 # TODO: Cheek Pouch
+makeAbility "Cheek Pouch"

 # TODO: Competitive
+makeAbility "Competitive"

 # TODO: Flower Veil
+makeAbility "Flower Veil"

 # TODO: Fur Coat
+makeAbility "Fur Coat"

 makeAbility 'Gale Wings', ->
   this::editPriority = (priority, move) ->
@@ -66,10 +71,13 @@
     return priority

 # TODO: Gooey
+makeAbility "Gooey"

 # TODO: Grass Pelt
+makeAbility "Grass Pelt"

 # TODO: Magician
+makeAbility "Magician"

 makeAbility 'Mega Launcher', ->
   this::modifyBasePower = (move, user, target) ->


Server: Tests: remove module.exports.daisy

--- servers/testhelper/index.coffee
+++ servers/testhelper/index.coffee
@@ -8,7 +8,6 @@
 { argv }   = require 'optimist'
 KONFIG      = require('koding-config-manager').load("main.#{argv.c}")
 mongo       = KONFIG.mongoReplSet or "mongodb://#{ KONFIG.mongo }"
-{ daisy }   = Bongo
 { expect }  = require 'chai'

@@ -168,7 +167,6 @@
   _
   hat
   async
-  daisy
   expect
   request
   generateUrl


Nodejs Web Server Tests: export underscore

--- servers/testhelper/index.coffee
+++ servers/testhelper/index.coffee
@@ -156,6 +156,7 @@

 module.exports = {
+  _
   hat
   daisy
   expect


Fix white spaces in curlies

Fix white spaces in curlies

--- servers/testhelper/index.coffee
+++ servers/testhelper/index.coffee
@@ -4,7 +4,7 @@
 request     = require 'request'
 querystring = require 'querystring'

-{ argv  }   = require 'optimist'
+{ argv }   = require 'optimist'
 KONFIG      = require('koding-config-manager').load("main.#{argv.c}")
 mongo       = KONFIG.mongoReplSet or "mongodb://#{ KONFIG.mongo }"
 { daisy }   = Bongo


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -8,7 +8,7 @@

   self.bind "update", ->
     if I.fadeOut
-      I.alpha = startingAlpha * (1 - ((I.age / I.duration) * startingAlpha)).clamp(0, 1)
+      I.alpha = startingAlpha * (1 - (I.age / I.duration)).clamp(0, 1)

     if I.duration != -1 && I.age >= I.duration
       I.active = false


updating isFeedbackImmediate to be cleaner

--- src/flux/task.coffee
+++ src/flux/task.coffee
@@ -301,7 +301,9 @@
     getStepByIndex: (taskId, stepIndex) ->
       @_steps[taskId][stepIndex]

-    isFeedbackImmediate: (taskId) -> @_local[taskId].is_feedback_available
+    isFeedbackImmediate: (taskId) ->
+      {is_feedback_available} = @_get(taskId)
+      is_feedback_available

     isSameStep: (taskId, stepIndices...) ->
       contentUrls = _.chain(stepIndices)


Update greenhorn.coffee

Update greenhorn.coffee

*change document.onmousemove to _elmnts.canvas.onmousemove
*move Sounds._playAll to _startEverything so that background music will be restarted when the engine is

--- src/greenhorn.coffee
+++ src/greenhorn.coffee
@@ -28,10 +28,6 @@
     e.preventDefault()
     Greenhorn.isDown[e.keyCode] = false
     return
-document.onmousemove = (e) ->
-    @mouseX = e.pageX
-    @mouseY = e.pageY
-    return

 #integer ID used to start and stop _masterUpdate
 _masterID = null
@@ -86,10 +82,16 @@
     #append headers to panels
     _elmnts.leftPanel.appendChild _elmnts.leftPanelHeader
     _elmnts.rightPanel.appendChild _elmnts.rightPanelHeader
+
+    #keep track of mouse position over canvas
+    _emnts.canvas.onmousemove = (e) ->
+        @mouseX = e.pageX
+        @mouseY = e.pageY
+        return

     #mouse position getters
-    @getMouseX = -> document.mouseX - @get('main', 'offsetLeft') - @get('canvas', 'offsetLeft') - @get('canvas', 'width') / 2
-    @getMouseY = -> document.mouseY - @get('main', 'offsetTop') - @get('canvas', 'offsetTop') - @get('canvas', 'height') / 2
+    @getMouseX = -> _elmnts.canvas.mouseX - @get('main', 'offsetLeft') - @get('canvas', 'offsetLeft') - @get('canvas', 'width') / 2
+    @getMouseY = -> _elmnts.canvas.mouseY - @get('main', 'offsetTop') - @get('canvas', 'offsetTop') - @get('canvas', 'height') / 2

     #generic element getter/setter
     @get = (elmnt, attr) ->
@@ -134,8 +136,9 @@

     #start all asynchronous functions
     _startEverything = ->
+        Sprites._startAll()
+        Sounds._playAll()
         _masterID = setInterval _masterUpdate, 1000 / env.FRAME_RATE
-        Sprites._startAll()
         return

     #used to keep engine from
@@ -148,8 +151,10 @@
         unless @isRunning() or _elmnts.canvas.onclick?
             #only do all of this once
             if _firstTime
-                #add engine to a user defined '#GREENHORN' div or the document body
+                #add engine to a user defined '.gh' div or the document body
                 (document.querySelector('.gh') ? document.body).appendChild _elmnts.main
+                if _elmnts.main.parent is document.body
+                    document.body.class = 'gh'

                 #set the size of the canvas
                 @set 'canvas', 'width', env.ENGINE.canvasWidth
@@ -183,7 +188,6 @@
                 #that preloads all sounds and
                 #launches all asynchronous updates
                 _elmnts.canvas.onclick = ->
-                    Sounds._playAll()
                     _startEverything()
                     _elmnts.canvas.onclick = undefined

@@ -198,8 +202,6 @@
         Sounds._stopAll()
         clearInterval _masterID
         _masterID = null
-    @isRunning = ->
-        _masterID?
     @clear = ->
         _elmnts.canvas.getContext('2d')
         .clearRect(
@@ -207,6 +209,8 @@
             -@get('canvas', 'height') / 2,
             @get('canvas', 'width'),
             @get('canvas', 'height'))
+    @isRunning = ->
+        _masterID?
     @hide = ->
         @set 'main', 'style', {'display': 'none'}
     @show = ->


Fixes #3

Fixes #3

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -57,7 +57,14 @@
   cardIndex = Math.floor(Math.random()*whiteCards.length)
   return whiteCards[cardIndex]

-db = {}
+db = {
+  scores:         {},                   # {<name>: <score>, ...}
+  activePlayers:  [],                   # [<player name>, ...]
+  blackCard:      random_black_card(),  # <card text>
+  czar:           null,                 # <player name>
+  hands:          {},                   # {<name>: [<card text>, <card text>, ...], ...}
+  answers:        [],                   # [ [<player name>, [<card text>, ...]], ... ]
+}

 # prune inactive player hands, ensure everyone has five cards
 fix_hands = () ->
@@ -192,21 +199,10 @@

 module.exports = (robot) ->

-  brainLoaded = () ->
-    db = robot.brain.get "cah"
-    if (!db)
-      db = {
-        scores:         {},                   # {<name>: <score>, ...}
-        activePlayers:  [],                   # [<player name>, ...]
-        blackCard:      random_black_card(),  # <card text>
-        czar:           null,                 # <player name>
-        hands:          {},                   # {<name>: [<card text>, <card text>, ...], ...}
-        answers:        [],                   # [ [<player name>, [<card text>, ...]], ... ]
-      }
-    robot.brain.set "cah", db
-
-  robot.brain.on "loaded", brainLoaded
-  brainLoaded() # just in case
+  robot.brain.on "loaded", =>
+    if !robot.brain.data.cah
+      robot.brain.data.cah = db
+    db = robot.brain.data.cah

   robot.hear /^cah help$/i, (msg) ->
     msg.send helpSummary


remove ‘playing’  in passed states

remove ‘playing’  in passed states

playing状态不应该被排除在卡断处理的状态之外。会导致卡断处理无法正常生效。

--- src/js/player.coffee
+++ src/js/player.coffee
@@ -134,7 +134,7 @@

             pass = ->
                 self.getState() in [
-                    STATES.PLAYING, STATES.PAUSE, STATES.STOP, STATES.END
+                    STATES.PAUSE, STATES.STOP, STATES.END
                 ]

             @engine = engine.on(EVENTS.STATECHANGE, (e) ->


Terminal: vmControllers vm.start.progress event listener updated

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -379,8 +379,8 @@
           click   : => @addNewTab vm.hostnameAlias
         vmWrapper.addSubView vmWrapper[vm.hostnameAlias]

-      vmController.on 'vm.start.progress', (vmAlias, {message})->
-        log vmAlias, message
+      vmController.on 'vm.start.progress', (vmAlias, update)->
+        {message} = update
         return  if message is 'FINISHED'
         niceMessage = MESSAGE_MAP[message.toLowerCase()]
         vmWrapper[vmAlias].unsetClass 'ready'


main.coffee reverted back to my changes

main.coffee reverted back to my changes

--- client/app/MainApp/main.coffee
+++ client/app/MainApp/main.coffee
@@ -1,101 +1,82 @@
 #Broker.channel_auth_endpoint = KD.config.apiUri+'/1.0/channel/auth';
 #Broker.channel_auth_endpoint = 'http://localhost:8008/auth'

-# THIS OVERRIDE NEEDS TO WAIT
-# NOT YET TOTALLY INTEROPERABLE
-# WITH Encoder.htmlDecode
-
-# Encoder.htmlEncode = do->
-
-#    htmlMap =
-#      '&' : 'amp'
-#      '<' : 'lt'
-#      '"' : 'quot'
-#      '<' : 'lt'
-#      '>' : 'gt'
-#      "'" : '#39'
-#      '`' : '#96'
-#      '!' : '#33'
-#      '@' : '#36'
-#      '%' : '#37'
-#      '(' : '#40'
-#      ')' : '#41'
-#      '=' : '#61'
-#      '+' : '#43'
-#      '{' : '#123'
-#      '}' : '#125'
-#      '[' : '#91'
-#      ']' : '#93'
-
-#    (str)-> str.replace /(&(?!\w\w+;)|'|<|>|"|'|`|\!|\@|\$|\%|\(|\)|\=|\+|\{|\}|\[|\])/g, (match)-> "&#{htmlMap[match]};"
-
-mainController = new MainController
-
-
-KD.remote.on 'loggedInStateChanged', (account)->
-  KD.socketConnected()
-  mainController.accountChanged account
-  AccountMixin.init(KD.remote.api)
-
-# Pistachio.MODE = if KD.env is 'dev' then 'development' else 'production'
-
-#$.cookie 'clientId', localStorage.clientId
-
-# Cacheable = new Cacheable
-
-# setTimeout ->
-#   appManager.openApplication("Chat")
-# ,5000
-
-firstLoad = yes
-connectionLostModalId = null
-connectionLostNotification = null
-
-KD.remote.on 'connected', -> console.log('kd connected')
-
-KD.remote.on 'disconnected', ->
-  #
-  # CONNECTIVITY NOTIFICATIONS PART START
-  #
-  setTimeout -> # to avoid modal to appear on page refresh
-
-    if connectionLostNotification?
-      connectionLostNotification.destroy()
-      connectionLostNotification = null
-
-    # THE MODAL APPEARS WHEN CONNECTION IS LOST
-    unless connectionLostModalId?
-      connectionLostModalId = KDModalController.createAndShowNewModal
-        type    : 'blocking'
-        title   : "Server connection lost"
-        content : """
-

-            Your internet connection may be down, or our server is.

-            If you have unsaved work please close this dialog and
back up your unsaved work locally until the connection is re-established.

-             Trying to reconnect...
-

-          """
-        height  : "auto"
-        overlay : yes
-        buttons :
-          "Close and Refresh later" :
-            style     : "modal-clean-red"
-            callback  : ()->
-              @propagateEvent KDEventType:'KDModalShouldClose'
-              connectionLostModalId = null
-              if connectionLostNotification?
-                connectionLostNotification.destroy()
-                connectionLostNotification = null
-
-              updateModalActive = no
-
-              # THE NOTIFICATION APPEARS WHEN MODAL WAS CLOSED BEFORE CONNECTION RE-ESTABLISHES
-              connectionLostNotification = new KDNotificationView
-                title    : "Server Connection Has Been Lost"
-                content  : "Trying to reconnect..., changes will not be saved until server reconnects, please back up locally."
-                duration : 0
-
-KD.remote.connect -> console.log 'koding is now connected to the backend'
+do ->
+  mainController             = new MainController
+  firstLoad                  = yes
+  connectionLostModal        = null
+  connectionLostNotification = null
+
+  destroyNotification = ->
+    connectionLostNotification?.destroy()
+    connectionLostNotification = null
+
+  # destroyConnectionSetNotification = ->
+  #   connectionSetNotification?.destroy()
+  #   connectionSetNotification = null
+
+  destroyModal = (reconnected = no)->
+    connectionLostModal?.destroy()
+    destroyNotification()
+    if reconnected isnt no
+      new KDNotificationView
+        title         : "Reconnected"
+        type          : "tray"
+        content       : "Server connection has been reset, you can continue working."
+        duration      : 3000
+    else
+      connectionLostNotification = new KDNotificationView
+        title         : "Trying to reconnect..."
+        type          : "tray"
+        closeManually : no
+        content       : "Server connection has been lost, changes will not be saved until server reconnects, please back up locally."
+        duration      : 0
+      connectionLostModal = null
+
+  showModal = ->
+    return if connectionLostModal
+    destroyNotification()
+    connectionLostModal = new KDBlockingModalView
+      title   : "Server connection lost"
+      content : """
+

+          Your internet connection may be down, or our server is.

+          If you have unsaved work please close this dialog and
back up your unsaved work locally until the connection is re-established.

+           Trying to reconnect...
+

+        """
+      height  : "auto"
+      overlay : yes
+      buttons :
+        "Close and Refresh later" :
+          style     : "modal-clean-red"
+          callback  : -> destroyModal()
+
+    connectionLostModal.once "KDObjectWillBeDestroyed", -> destroyModal()
+
+  ###
+  # CONNECTIVITY EVENTS
+  ###
+
+  KD.remote.on 'loggedInStateChanged', (account)->
+    KD.socketConnected()
+    mainController.accountChanged account
+    AccountMixin.init(KD.remote.api)
+
+
+  KD.remote.on 'connected', ->
+    if firstLoad
+      log 'kd remote connected'
+      firstLoad = no
+    else
+      log 'kd remote re-connected'
+      destroyModal yes
+
+  KD.remote.on 'disconnected', ->
+    # to avoid modal to appear on page refresh
+    __utils.wait 500, showModal
+
+  KD.remote.connect()

   #initConnectionEvents conn


throttle sitemap fetch rate

throttle sitemap fetch rate

--- client/lib/neighborhood.coffee
+++ client/lib/neighborhood.coffee
@@ -4,33 +4,44 @@

 module.exports = neighborhood = {}

+
 wiki.neighborhood ?= {}
+nextAvailableFetch = 0
+nextFetchInterval = 2000

 populateSiteInfoFor = (site,neighborInfo)->
   return if neighborInfo.sitemapRequestInflight
+  neighborInfo.sitemapRequestInflight = true

-  neighborInfo.sitemapRequestInflight = true
-
-  sitemapUrl = "http://#{site}/system/sitemap.json"
-  request = $.ajax
-    type: 'GET'
-    dataType: 'json'
-    url: sitemapUrl
+  fetchMap = ->
+    sitemapUrl = "http://#{site}/system/sitemap.json"
+    wiki.log 'fetchMap', site
+    request = $.ajax
+      type: 'GET'
+      dataType: 'json'
+      url: sitemapUrl
+    request
+      .always( -> neighborInfo.sitemapRequestInflight = false )
+      .done (data)->
+        neighborInfo.sitemap = data
+      .fail (data)->
+        wiki.log( "fetchMap failed", site, data )

-  request
-    .always( -> neighborInfo.sitemapRequestInflight = false )
-    .done (data)->
-      neighborInfo.sitemap = data
-    .fail (data)->
-      wiki.log( "failed to retrieve sitemap for ", site, data )
+  now = Date.now()
+  if now > nextAvailableFetch
+    nextAvailableFetch = now + nextFetchInterval
+    fetchMap()
+  else
+    wiki.log 'fetchMap delayed', site, nextAvailableFetch - now
+    setTimeout fetchMap, nextAvailableFetch - now
+    nextAvailableFetch += nextFetchInterval
+

 neighborhood.registerNeighbor = (site)->
-  neighborInfo = {} # nothing to record for now
-
+  return if wiki.neighborhood[site]?
+  neighborInfo = {}
+  wiki.neighborhood[site] = neighborInfo
   populateSiteInfoFor( site, neighborInfo )
-
-  return if wiki.neighborhood[site]?
-  wiki.neighborhood[site] = neighborInfo
   $('body').trigger 'new-neighbor', site

 neighborhood.listNeighbors = ()->
@@ -73,7 +84,6 @@
       wiki.doInternalLink 'welcome-visitors', null, @.title

-  debugger
   search = createSearch({neighborhood})

   $('input.search').on 'keypress', (e)->


Meta description

Meta description

Add optional meta description field.

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -50,11 +50,13 @@
     return cb(null, new Error 'Blog body is required')

   slug = $('[name=slug]', $form).val()
+  metaDesc = $('[name=metaDesc]', $form).val()

   attrs =
     title: $('[name=title]', $form).val()
     tags: $('[name=tags]', $form).val()
     slug: slug
+    metaDesc: metaDesc
     body: body
     updatedAt: new Date()


iotype not iostatus :/

iotype not iostatus :/

--- snippets/openmdao-snippets.cson
+++ snippets/openmdao-snippets.cson
@@ -1,13 +1,13 @@
 '.source.python':
   'New Component':
     'prefix': 'comp'
-    'body': 'class ${1:ComponentName}(Component):\n\t"""${2:$1 is a Component that calculates ${3:...}}"""\n\n\t# inputs\n\t${4:x} = ${5:Float}(${6:0.0}, iostatus=\'in\', desc=\'${7:description}\')\n\n\t# outputs\n\t${8:y} = ${9:Float}(${10:0.0}, iostatus=\'out\', desc=\'${11:description}\')\n\n\tdef execute(self):\n\t\t${12:self.x \= self.y}\n\n\tdef list_deriv_vars(self):\n\t\t${13:pass}\n\n\tdef provideJ(self):\n\t\t${14:pass}\n\n\n$0'
+    'body': 'class ${1:ComponentName}(Component):\n\t"""${2:$1 is a Component that calculates ${3:...}}"""\n\n\t# inputs\n\t${4:x} = ${5:Float}(${6:0.0}, iotype=\'in\', desc=\'${7:description}\')\n\n\t# outputs\n\t${8:y} = ${9:Float}(${10:0.0}, iotype=\'out\', desc=\'${11:description}\')\n\n\tdef execute(self):\n\t\t${12:self.x \= self.y}\n\n\tdef list_deriv_vars(self):\n\t\t${13:pass}\n\n\tdef provideJ(self):\n\t\t${14:pass}\n\n\n$0'
   'New Assembly':
     'prefix': 'assem'
     'body': 'class ${1:AssemblyName}(Assembly):\n\t"""${2:$1 is an Assembly that ${3:...}}"""\n\n\tdef configure(self):\n\t\t${4:self.add(\'${5:comp}\', ${6:constructor}())}\n\n\n$0'
   'New Component Class':
     'prefix': '(Component):'
-    'body': '(Component):\n\t"""${2:A Component that calculates ${3:...}}"""\n\n\t# inputs\n\t${4:x} = ${5:Float}(${6:0.0}, iostatus=\'in\', desc=\'${7:description}\')\n\n\t# outputs\n\t${8:y} = ${9:Float}(${10:0.0}, iostatus=\'out\', desc=\'${11:description}\')\n\n\tdef execute(self):\n\t\t${12:self.x \= self.y}\n\n\tdef list_deriv_vars(self):\n\t\t${13:pass}\n\n\tdef provideJ(self):\n\t\t${14:pass}\n\n\n$0'
+    'body': '(Component):\n\t"""${2:A Component that calculates ${3:...}}"""\n\n\t# inputs\n\t${4:x} = ${5:Float}(${6:0.0}, iotype=\'in\', desc=\'${7:description}\')\n\n\t# outputs\n\t${8:y} = ${9:Float}(${10:0.0}, iotype=\'out\', desc=\'${11:description}\')\n\n\tdef execute(self):\n\t\t${12:self.x \= self.y}\n\n\tdef list_deriv_vars(self):\n\t\t${13:pass}\n\n\tdef provideJ(self):\n\t\t${14:pass}\n\n\n$0'
   'New Assembly Class':
     'prefix': '(Assembly):'
     'body': '(Assembly):\n\t"""${2:An Assembly that ${3:...}}"""\n\n\tdef configure(self):\n\t\t${4:self.add(\'${5:comp}\', ${6:constructor}())}\n\n\n$0'


Default startPosition to 0 for consistency

--- src/oniguruma.coffee
+++ src/oniguruma.coffee
@@ -1,7 +1,7 @@
 {OnigScanner} = require('bindings')('onig_scanner.node')
 OnigRegExp = require './onig-reg-exp'

-OnigScanner::findNextMatch = (string, startPosition, callback) ->
+OnigScanner::findNextMatch = (string, startPosition=0, callback) ->
   if typeof startPosition is 'function'
     callback = startPosition
     startPosition = 0


EDSC-335: Remove Jasmine specs referencing removed code

--- spec/javascripts/models/querySpec.coffee
+++ spec/javascripts/models/querySpec.coffee
@@ -257,13 +257,3 @@
       @query.params(params)
       @query.clearFilters()
       expect(param(@query.params())).toEqual(param(defaults))
-
-  describe "DataQualitySummaryQuery", ->
-    beforeEach ->
-      @ds_id = 'my_item_id'
-      @query = new ns.DataQualitySummaryQuery(@ds_id)
-
-    it 'serializes catalog item ids', ->
-      params = {catalog_item_id: @ds_id}
-      @query.params(params)
-      expect(param(@query.params())).toEqual(param(params))


improve message showing

improve message showing

--- app/scripts/main.coffee
+++ app/scripts/main.coffee
@@ -21,10 +21,12 @@
       success: (data)->
         console.log 'autosave success'
         editor.setConnecting false
-        $('#server-status').text 'Saved!'
         setTimeout ->
-          $('#server-status').text ''
-        , 3000
+          $('#server-status').text 'Saved'
+          setTimeout ->
+            $('#server-status').text ''
+          , 3000
+        , 500
       error: ->
         $('#server-status').text ''
         console.log 'unable to autosave'


Original file name -> file name (construct protocol change)

--- src/queue_tea.coffee
+++ src/queue_tea.coffee
@@ -122,7 +122,7 @@
     stdout.write("\r")
     console.log "Print Jobs:\n"
     for job, i in jobs
-      console.log "  #{i} ) #{job.original_file_name}"
+      console.log "  #{i}) #{job.file_name}"
     console.log ""
     @cli.rl.prompt()
     @cli.rl.resume()


Math button updates, equation builder and calculator

--- javascripts/widgets/math_buttons.coffee
+++ javascripts/widgets/math_buttons.coffee
@@ -11,27 +11,34 @@
         do (num)=>
           @button({
             value: "#{num}"
-            class: 'math-button number-specifier'
+            class: 'math-button number-specifier number'
           }, opts)

-    exponent: (opts)->
+    caret: (opts)->
       @button({
         value: '^'
         label: 'ˆ'
-        class: 'math-button other exponent'
+        class: 'math-button other caret'
       }, opts)

     negative: (opts)->
       @button({
         value: 'negative'
         label: '(–)'
+        class: 'math-button number-specifier negative'
+      }, opts)
+
+    negative_slash_positive: (opts)->
+      @button({
+        value: 'negative-slash-positive'
+        label: "–/+"
         class: 'math-button number-specifier'
       }, opts)

     decimal: (opts)->
       @button({
         value: '.'
-        class: 'math-button number-specifier'
+        class: 'math-button number-specifier decimal'
       }, opts)

     addition: (opts)->
@@ -72,7 +79,7 @@
     equals: (opts)->
       @button({
         value: '='
-        class: 'math-button operation'
+        class: 'math-button operation equal'
       }, opts)

     clear: (opts)->
@@ -88,12 +95,47 @@
         class: 'math-button other square'
       }, opts)

-    square_root: (opts)->
+    exponent: (opts)->
+      base = opts.base || "x"
+      power = opts.power || "y"
       @button({
-        value: 'squareroot'
-        label: '√—'
+        value: 'exponent'
+        label: "#{base}#{power}"
+        class: 'math-button other exponent'
+      }, opts)
+
+    root: (opts)->
+      degree = if opts.degree
+        "
#{opts.degree}
"
+      else
+        ""
+      radicand = if opts.radicand
+        "
#{opts.radicand}
"
+      else
+        ""
+
+      @button({
+        value: 'root'
+        label: """
+          #{degree}
+          #{radicand}
+
√

+
—

+        """
         class: 'math-button other square-root'
       }, opts)
+
+
+    numerator_denominator: (opts)->
+      @button({
+        value: 'numeratordenominator'
+        label: """
+
a

+
—

+
b

+          """
+        class: 'math-button other numerator-denominator'
+      })

     lparen: (opts)->
       @button({
@@ -129,12 +171,12 @@
       @button({
         value: 'fn'
         label: 'ƒ'
-        class: 'math-button other'
+        class: 'math-button other function'
       }, opts)

-    button: (type_opts, opts)->
+    button: (type_opts, opts={})->
       ui_elements.button_builder.build(
-        _.extend({}, type_opts, @opts, opts || {}))
+        _.extend({}, type_opts, @opts, opts))

   class_mixer(ButtonBuilder)


social: copy embedbox payload data into activities curreny payload for comments

--- client/activity/lib/views/comments/commentinputwidget.coffee
+++ client/activity/lib/views/comments/commentinputwidget.coffee
@@ -1,3 +1,4 @@
+_  = require 'lodash'
 kd = require 'kd'
 KDButtonView = kd.ButtonView
 KDCustomHTMLView = kd.CustomHTMLView
@@ -73,7 +74,10 @@

     activity = @getData()
     {app}    = @getOptions()
-    payload  = @getPayload()
+
+    embedBoxPayload = @getEmbedBoxPayload()
+
+    payload = _.assign {}, activity?.payload, embedBoxPayload

     timestamp = Date.now()
     clientRequestId = generateFakeIdentifier timestamp


Refactor chat head context menu watch item view construction

--- client/ide/lib/views/statusbar/idestatusbaravatarview.coffee
+++ client/ide/lib/views/statusbar/idestatusbaravatarview.coffee
@@ -97,13 +97,7 @@
       isWatching  = watchMap.indexOf(@nickname) > -1
       menuWidth   = 172

-      unless @hasClass 'offline'
-        menuItems.Watch =
-          type         : 'customView'
-          view         : new IDEChatHeadWatchItemView
-            isWatching : isWatching
-            nickname   : @nickname
-            delegate   : this
+      @createWatchToggle menuItems, isWatching

       if amIHost
         menuItems.Kick =
@@ -138,6 +132,19 @@
       MENU.once 'KDObjectWillBeDestroyed', => MENU = null

+  createWatchToggle: (menuItems, isWatching) ->
+
+    return  unless menuItems
+    return  if @hasClass 'offline'
+
+    menuItems.Watch =
+      type          : 'customView'
+      view          : new IDEChatHeadWatchItemView
+        isWatching  : isWatching
+        nickname    : @nickname
+        delegate    : this
+
+
   setWatchState: (shouldWatch, nickname) ->

     @toggleClass 'watching'


Define read only toggle view creator method

--- client/ide/lib/views/statusbar/idestatusbaravatarview.coffee
+++ client/ide/lib/views/statusbar/idestatusbaravatarview.coffee
@@ -3,6 +3,7 @@
 FSHelper = require 'app/util/fs/fshelper'
 AvatarView = require 'app/commonviews/avatarviews/avatarview'
 IDEChatHeadWatchItemView = require './idechatheadwatchitemview'
+IDEChatHeadReadOnlyItemView = require './idechatheadreadonlyitemview'
 module.exports = class IDEStatusBarAvatarView extends AvatarView

   INTENT_DELAY = 177
@@ -145,6 +146,18 @@
         delegate    : this

+  createReadOnlyToggle: (menuItems, permission) ->
+
+    return  unless menuItems
+    return  if @hasClass 'offline'
+
+    menuItems.ReadOnly =
+      type             : 'customView'
+      view             : new IDEChatHeadReadOnlyItemView
+        permission     : permission
+        delegate       : this
+
+
   setWatchState: (state) ->

     @toggleClass 'watching'


Ace: Overriding Cmd+, shortcut to disable built-in ace settings view.

--- client/landing/app/Applications/Ace.kdapplication/ace.coffee
+++ client/landing/app/Applications/Ace.kdapplication/ace.coffee
@@ -100,6 +100,7 @@
       @addKeyCombo "preview", "Ctrl-Shift-P", => @getDelegate().preview()
       @addKeyCombo "fullscreen", "Ctrl-Enter", => @getDelegate().toggleFullscreen()
       @addKeyCombo "gotoLine", "Ctrl-G", @bound "showGotoLine"
+      @addKeyCombo "settings", "Ctrl-,", noop # ace creates a settings view for this shortcut, overriding it.

   showFindReplaceView: (openReplaceView) ->
     {findAndReplaceView} = @getDelegate()


Removes crappy workaround

Removes crappy workaround

--- coffee/game/ride_engine.coffee
+++ coffee/game/ride_engine.coffee
@@ -7,12 +7,16 @@
     @generator = null

   _onCarMove: (e) ->
+    toRemove = []
     for k, v of @pickupZones
       if v.zone.isNearMe(@car.getCurrentPosition())
         clearTimeout(v.timer)
         v.zone.hide()
         EventBus.get('RideEngine').post(OnPickupEvent.NAME, new OnPickupEvent(v.zone))
-        setTimeout(() => delete @pickupZones[k])
+        toRemove.push k # Remove from current pickup zones
+
+    for e in toRemove
+      delete @pickupZones[e]

   start: () ->
     EventBus.get('Car').register OnCarMoveEvent.NAME, (e) => @_onCarMove(e)


fix node standalone example again

fix node standalone example again

--- examples/node/standalone.coffee
+++ examples/node/standalone.coffee
@@ -1,6 +1,6 @@
 fs = require 'fs'
+lead = require 'lead.js'
 React = require 'react'
-lead = require 'lead.js'
 Context = lead.require 'context'
 CoffeeScriptCell = lead.require 'coffeescript_cell'

@@ -9,4 +9,8 @@
 context = Context.create_standalone_context(imports: ['compat'])
 Context.run_in_context context, CoffeeScriptCell.create_fn script
 # FIXME doesn't render graph, Q logs error
-console.log React.renderComponentToString context.component
+elt = document.createElement 'div'
+React.renderComponent context.component, elt
+setTimeout ->
+	console.log elt.innerHTML
+, 100


Don't let the dummy response break things.

--- public/coffee/controllers.coffee
+++ public/coffee/controllers.coffee
@@ -195,14 +195,15 @@

     $log.debug(query)

-    dummyRes = {query, response: '', code: 'pending', call: "#{ m.HTTPMethod } #{ m.URI }"}
+    dummyRes = {query, call: m.URI, response: '', headers: [], code: 'pending'}

     $http.post('run', query).then ({data}) ->
       data.query = query
       i = m.results.indexOf dummyRes
       if i >= 0
-        m.results.splice i, 1
-      m.results.push data
+        m.results.splice i, 1, data
+      else
+        m.results.push data

     m.results.push dummyRes
     $scope.show = res: true
@@ -280,7 +281,7 @@
   $scope.parsedData = []
   $scope.tree = expanded: false

-  ct = $scope.headers['content-type']
+  ct = ($scope.headers['content-type'] ? '')
   promises = []
   if $scope.res.response?.length
     if ct.match(/^application\/json/)


[skip] Made `cancelOnEsc` and `saveOnEnter` options as well (5m)

--- vendor/assets/javascripts/neat/collection_editor.coffee
+++ vendor/assets/javascripts/neat/collection_editor.coffee
@@ -21,10 +21,9 @@
   templateOptions: {}
   pageSize: 30
   useKeyboardToChangeRows: true
-
-  keyDownHandlers:
-    ESC:    -> @viewInEdit?.cancelEdit()
-    RETURN: -> @viewInEdit?.save()
+  cancelOnEsc: true
+  saveOnEnter: true
+  keyDownHandlers: {}

   initialize: ->
     @viewPath = @viewPath ? @resource
@@ -74,9 +73,15 @@
     $(@el).delegate '.header a', 'click', _.bind(@sort, @)
     $(@el).delegate '.editor', 'keydown', _.bind(@onKeyDown, @)

+    if @cancelOnEsc
+      @keyDownHandlers['ESC']    = -> @viewInEdit?.cancelEdit()
+
+    if @saveOnEnter
+      @keyDownHandlers['RETURN'] = -> @viewInEdit?.save()
+
     if @useKeyboardToChangeRows
-      @keyDownHandlers['UP']   = -> @edit @prevView()
-      @keyDownHandlers['DOWN'] = -> @edit @nextView()
+      @keyDownHandlers['UP']     = -> @edit @prevView()
+      @keyDownHandlers['DOWN']   = -> @edit @nextView()

     @views = []
     @viewInEdit = null


Fix incorrect checking of contact name

Fix incorrect checking of contact name

--- services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee
+++ services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee
@@ -39,7 +39,7 @@
 					$scope.autocompleteContacts = data.contacts or []
 					for contact in $scope.autocompleteContacts
 						if contact.type == "user"
-							if contact.last_name == "" and contact.first_name = contact.email.split("@")[0]
+							if contact.last_name and contact.first_name == contact.email.split("@")[0]
 								# User has not set their proper name so use email as canonical display property
 								contact.display = contact.email
 							else


Include analytics on inquiry namespaced routes

--- assets/analytics.coffee
+++ assets/analytics.coffee
@@ -23,6 +23,6 @@
   require '../analytics/account_login.js'
   require '../analytics/bidding.js'

-  if route.test(/^\/dev/) or route.test(/^\/artwork\/.*/)
+  if route.test(/^\/inquiry\/.*/) or route.test(/^\/artwork\/.*/)
     require '../analytics/embedded_inquiry.js'
     require '../analytics/inquiry_questionnaire.js'


removed redundant whiteline

removed redundant whiteline

--- core/app/backbone/views/user_channel_suggestions_view.coffee
+++ core/app/backbone/views/user_channel_suggestions_view.coffee
@@ -20,6 +20,5 @@
   checkSuggestionsEmpty: ->
     @trigger 'no_suggestions' if @collection.length == 0

-
   showEmptyView: => @$el.hide()
   closeEmptyView: => @$el.show()


Throw errors on subscribe and command

Throw errors on subscribe and command

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -427,6 +427,21 @@
 $.Event.prototype.targetView = -> $(@target).containingView()

 # Deprecations
+
+View::subscribe = ->
+  throw new Error """
+    `subscribe` is no longer available. Please use native the `addEventListener`,
+    jQuery's `on` or subscribe to commands via `atom.commands.add`. See the
+    docs at https://atom.io/docs/api/latest/CommandRegistry#instance-add.
+  """
+
+View::command = ->
+  throw new Error """
+    `command` is no longer available. Please subscribe to commands via
+    `atom.commands.add`. See the docs at
+    https://atom.io/docs/api/latest/CommandRegistry#instance-add
+    Collect the results in a CompositeDisposable https://atom.io/docs/api/v0.150.0/CompositeDisposable
+  """

 JQueryTrigger = $.fn.trigger
 $.fn.trigger = (eventName, data) ->


Whitespace

Whitespace

--- source/coffee/Enumerable.coffee
+++ source/coffee/Enumerable.coffee
@@ -2,7 +2,7 @@

 # Enumerable
 # ==========
-#
+#
 class Enumerable

   constructor : ->
@@ -16,7 +16,7 @@
   set : (value) ->
     @values.push value
     @trigger 'set', value
-
+
   get : (index) ->
     val = @values[index] if @values[index]?
     if val? then @trigger 'get', val
@@ -41,7 +41,7 @@

   map : (fn, context) ->
     @trigger 'map', _.map @values, fn, context
-
+
   forEach : (fn, context) ->
     @each fn, context

@@ -50,23 +50,23 @@

   reduceRight : (fn, memo, context) ->
     @trigger 'reduceRight', _.reduce @values, fn, memo, context
-
+
   register : (event, fn) ->
     @events[event] = fn
-
+
   trigger : (event, args...) ->
     if event? && @events[event]?
       @events[event](args)
     @cleanReturnVal args
-
+
   cleanReturnVal : (val) ->
     if val instanceof Array && val.length == 1
       return val[0]
     val
-
+
   toString : ->
     @values.toString()
-
+

 # Do some checking of the global space to see if we're in AMD /
 # Node.js land


Test all direct returns

Test all direct returns

--- src/test/core.coffee
+++ src/test/core.coffee
@@ -23,12 +23,17 @@
 						$hide: true
 						$id: false
 				}
-			#it 'should return routes creation methods', ->
-				#assert trocha.$ROUTE, "ROUTE"
-				#assert trocha.$SCOPE, "SCOPE"
+			it 'should return routes types', ->
+				assert trocha.ROUTE, "ROUTE"
+				assert trocha.SCOPE, "SCOPE"
+				assert trocha.RESOURCE, "RESOURCE"
 		describe 'Constructor', ->
 			it 'should create a valid trocha object', ->
 				assert trocha, ->
 				r = trocha()
 				assert r, {}
+				assert r._custom, ->
+				assert r._newResource, ->
+				assert r._newRoute, ->
+				assert r._newScope, ->
 )()


add some specs for buffer modified indicator

--- spec/app/status-bar-spec.coffee
+++ spec/app/status-bar-spec.coffee
@@ -3,7 +3,7 @@
 StatusBar = require 'status-bar'

 describe "StatusBar", ->
-  [rootView, editor, statusBar] = []
+  [rootView, editor, statusBar, buffer] = []

   beforeEach ->
     rootView = new RootView(require.resolve('fixtures/sample.js'))
@@ -11,6 +11,7 @@
     StatusBar.activate(rootView)
     editor = rootView.getActiveEditor()
     statusBar = rootView.find('.status-bar').view()
+    buffer = editor.getBuffer()

   afterEach ->
     rootView.remove()
@@ -24,8 +25,9 @@
       expect(rootView.panes.find('.pane > .status-bar').length).toBe 2

   describe ".initialize(editor)", ->
-    it "displays the editor's buffer path and cursor buffer position", ->
+    it "displays the editor's buffer path, cursor buffer position, and buffer modified indicator", ->
       expect(statusBar.currentPath.text()).toBe 'sample.js'
+      expect(statusBar.bufferModified.text()).toBe ''
       expect(statusBar.cursorPosition.text()).toBe '1,1'

     describe "when associated with an unsaved buffer", ->
@@ -44,6 +46,16 @@
       rootView.open(require.resolve 'fixtures/sample.txt')
       expect(statusBar.currentPath.text()).toBe 'sample.txt'

+  describe "when the associated editor's buffer's content changes", ->
+    it "updates the buffer modified indicator", ->
+      expect(statusBar.bufferModified.text()).toBe ''
+      oldText = buffer.getText()
+      editor.insertText("/")
+      expect(statusBar.bufferModified.text()).toBe '*'
+      editor.backspace()
+      buffer.save()
+      expect(statusBar.bufferModified.text()).toBe ''
+
   describe "when the associated editor's cursor position changes", ->
     it "updates the cursor position in the status bar", ->
       editor.setCursorScreenPosition([1, 2])


collaboration: controller realtime.getReferences helper

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -345,26 +345,18 @@

   setCollaborativeReferences: ->

-    nickname           = nick()
-    myWatchMapName     = "#{nickname}WatchMap"
-    mySnapshotName     = "#{nickname}Snapshot"
-    defaultPermission  = default: 'edit'
-
-    @participants      = @rtm.getFromModel 'participants'
-    @changes           = @rtm.getFromModel 'changes'
-    @permissions       = @rtm.getFromModel 'permissions'
-    @broadcastMessages = @rtm.getFromModel 'broadcastMessages'
-    @myWatchMap        = @rtm.getFromModel myWatchMapName
-    @mySnapshot        = @rtm.getFromModel mySnapshotName
-
-    @participants      or= @rtm.create 'list',   'participants', []
-    @changes           or= @rtm.create 'list',   'changes', []
-    @permissions       or= @rtm.create 'map',    'permissions', defaultPermission
-    @broadcastMessages or= @rtm.create 'list',   'broadcastMessages', []
-    @myWatchMap        or= @rtm.create 'map',    myWatchMapName, {}
-
-    initialSnapshot      = if @amIHost then @getWorkspaceSnapshot() else {}
-    @mySnapshot        or= @rtm.create 'map',    mySnapshotName, initialSnapshot
+    initialSnapshot = if @amIHost then @getWorkspaceSnapshot() else {}
+
+    refs = realtimeHelpers.getReferences @rtm, @getSocialChannelId(), initialSnapshot
+
+    # for backwards compatibility.
+    # TODO: keep this until CollaborationModel abstraction. ~Umut
+    @define 'participants',      -> refs.participants
+    @define 'changes',           -> refs.changes
+    @define 'permissions',       -> refs.permissions
+    @define 'broadcastMessages', -> refs.broadcastMessages
+    @define 'myWatchMap',        -> refs.watchMap
+    @define 'mySnapshot',        -> refs.snapshot

   registerCollaborationSessionId: ->
@@ -400,11 +392,6 @@

   bindRealtimeEvents: ->
-
-    @rtm.bindRealtimeListeners @changes, 'list'
-    @rtm.bindRealtimeListeners @broadcastMessages, 'list'
-    @rtm.bindRealtimeListeners @myWatchMap, 'map'
-    @rtm.bindRealtimeListeners @permissions, 'map'

     @rtm.on 'ValuesAddedToList', (list, event) =>


disable book

disable book

--- client/landing/app/CommonViews/HelpBox.coffee
+++ client/landing/app/CommonViews/HelpBox.coffee
@@ -11,7 +11,7 @@
     @setPartial @partial()

   click:->
-    @getSingleton('mainController').emit "ShowInstructionsBook"
+    #@getSingleton('mainController').emit "ShowInstructionsBook"

   partial:()->
     """


- remove unused comments

- remove unused comments

--- src/type-info.coffee
+++ src/type-info.coffee
@@ -300,13 +300,6 @@
     result
   _toObject:(aOptions, aNameRequired = true)->
     result = @mergeTo aOptions, null, true, aNameRequired
-    ###
-    vEncoding = result.encoding
-    if vEncoding and vEncoding.name isnt Type.DEFAULT_ENCODING.name
-      result.encoding = vEncoding.name
-    else
-      delete result.encoding
-    ###
     result
   toObject: (aOptions, aNameRequired)->
     if aOptions


KDTokenizedInputView::addToken "type" added to emitter

--- components/inputs/KDTokenizedInputView.coffee
+++ components/inputs/KDTokenizedInputView.coffee
@@ -130,7 +130,7 @@
     @tokenInput.nextSibling.textContent = "\u00a0"
     @utils.selectText @tokenInput.nextSibling, 1
     @tokenInput.remove()
-    @emit "TokenAdded", item
+    @emit "TokenAdded", type, item

   keyDown: (event) ->
     switch event.which


Added `fallbackSelector` and `loadingSelector` to constructor instance options - both `fallbackSelector` and `loadingSelector` are useful when not subclassing CollectionView. - dont need a template on the CollectionView to work.

--- src/chaplin/views/collection_view.coffee
+++ src/chaplin/views/collection_view.coffee
@@ -96,7 +96,8 @@
   constructor: (options) ->
     # Apply options to view instance.
     if (options)
-      _.extend this, _.pick options, ['renderItems', 'itemView']
+      _.extend this, _.pick options,
+        ['renderItems', 'itemView', 'fallbackSelector', 'loadingSelector']

     # Initialize list for visible items.
     @visibleItems = []


created keys for plugin and create random string generator to futher prevent namespace collision

--- src/coffee/responsive.breadcrumbs.coffee
+++ src/coffee/responsive.breadcrumbs.coffee
@@ -42,8 +42,6 @@
         o         = ($ el).data '_obj', {}
         _this     = this
         metadata  = ($ el).data "#{ pluginName.toLowerCase() }-options"
-        stateKey  = "#{ pluginName.toLowerCase() }-state"
-        resizeKey = "resize.#{ pluginName }"

         o.debug = ->
             # Skip browsers w/o firebug or console
@@ -97,14 +95,18 @@

             o.opts.onLoad(_this) if $.isFunction(o.opts.onLoad)

+            # plugin keys ::start
+            # generate random key for resize event to prevent event namespace conflicts
+            o.resizeKey = "resize.#{ pluginName }_#{ o.generateRandomKey() }"
+            o.stateKey  = "#{ pluginName.toLowerCase() }-state"
+            # plugin keys ::end
+
             # add the wrapper class to the element
             o.el.addClass o.opts.wrapperClass

-            if o.opts.fixIEResize
-                _.each resize, (v, i) ->
-                    resize[i] = _.debounce(v, o.opts.debounceTime) if $.isFunction(v)
-
-            o.el.data stateKey, 'decompressed'
+            o.resize = _.debounce(o.resize, o.opts.debounceTime) if o.opts.fixIEResize
+
+            o.el.data o.stateKey, 'decompressed'

             # bind the element to a custom event named compress
             o.el.on "compress.#{ pluginName }", (e) ->
@@ -112,7 +114,7 @@

                 items = current.children 'li'

-                current.data stateKey, 'compressed'
+                current.data o.stateKey, 'compressed'

                 # slice the items beginning to the second element up to the second to the last element
                 hiddenItems = items
@@ -137,7 +139,7 @@
             o.el.on "decompress.#{ pluginName }", (e) ->
                 current = $ this

-                current.data stateKey, 'decompressed'
+                current.data o.stateKey, 'decompressed'

                 holder = current.find ".#{ o.opts.holder.class }"

@@ -159,13 +161,13 @@

                 o.opts.onAfterDecompress(_this) if $.isFunction(o.opts.onAfterDecompress)

-            o.browserWindow.on resizeKey, o.resize
+            o.browserWindow.on o.resizeKey, o.resize

             # execute custom code after the plugin has loaded
             o.opts.onAfterLoad(_this) if $.isFunction(o.opts.onAfterLoad)

             # trigger the resize event after the plugin has loaded
-            o.browserWindow.trigger resizeKey
+            o.browserWindow.trigger o.resizeKey

         o.resize = (e) ->
             current      = $ this
@@ -207,8 +209,14 @@
                 .removeClass(o.opts.wrappedClass)
                 .trigger "decompress.#{ pluginName }"

+        o.generateRandomKey = ->
+            Math
+                .random()
+                .toString(36)
+                .substring 7
+
         _this.getState = ->
-            state = o.el.data stateKey
+            state = o.el.data o.stateKey

             return if typeof state is "undefined" then 'decompressed' else state


BF-46 Save user settings

BF-46 Save user settings


Fixed the disabled selection issue when cancel deletion

--- main/static/src/script/site/user.coffee
+++ main/static/src/script/site/user.coffee
@@ -41,12 +41,12 @@
   ($ '#user-delete').click (e) ->
     clear_notifications()
     e.preventDefault()
-    user_keys = []
-    ($ 'input[name=user_db]:checked').each ->
-      ($ this).attr 'disabled', true
-      user_keys.push ($ this).val()
-    confirm_message = (($ this).data 'confirm').replace '{users}', user_keys.length
+    confirm_message = (($ this).data 'confirm').replace '{users}', ($ 'input[name=user_db]:checked').length
     if confirm confirm_message
+      user_keys = []
+      ($ 'input[name=user_db]:checked').each ->
+        ($ this).attr 'disabled', true
+        user_keys.push ($ this).val()
       delete_url = ($ this).data 'service-url'
       success_message = ($ this).data 'success'
       error_message = ($ this).data 'error'


Ok, this is even more explicit.

Ok, this is even more explicit.

--- app/client/js/react/app.coffee
+++ app/client/js/react/app.coffee
@@ -26,26 +26,26 @@
     @component = React.createClass

       render: ->
-        pageProps = @pageProps()
-        page = pageProps && @page() || notFoundPage
+        pageProps = {}
+        page = @page pageProps

         div className: 'app',
           header.component() if page != songPage
           page.component pageProps
           footer.component() if page != songPage

-      pageProps: ->
+      page: (props) ->
         switch routes.active
+          when routes.home then homePage
+          when routes.myNewSong then editSongPage
           when routes.mySong, routes.editMySong
             song = userStore.songByRoute routes.active
-            song && song: song
-          else {}
-
-      page: ->
-        switch routes.active
-          when routes.home then homePage
-          when routes.myNewSong, routes.editMySong then editSongPage
-          when routes.mySong then songPage
+            return notFoundPage if !song
+            props.song = song
+            switch routes.active
+              when routes.mySong then songPage
+              when routes.editMySong then editSongPage
+          else notFoundPage

       componentDidMount: ->
         goog.events.listen window, 'orientationchange', @onOrientationChange


put a unique end of stream marker in the session dictionary

--- app/coffee/ASpellWorker.coffee
+++ app/coffee/ASpellWorker.coffee
@@ -45,6 +45,7 @@
 		# receive the end of data marker
 		@state = 'busy'
 		@callback = callback
+		@setEndOfStreamMarker()
 		@setTerseMode()
 		@write(words)
 		@flush()
@@ -60,13 +61,18 @@
 	flush: () ->
 		# get aspell to send an end of data marker "*" when ready
 		@sendCommand("%")		# take the aspell pipe out of terse mode so we can look for a '*'
-		@sendCommand("end") # send a valid word ("end") so it will generate a '*'
+		@sendCommand("^ENDOFSTREAMMARKER") # send our marker which will generate a '*'
 		@sendCommand("!")		# go back into terse mode

 	shutdown: (reason) ->
 		logger.log process: @pipe.pid, reason: reason, 'shutting down'
 		@state = "closing"
 		@pipe.stdin.end()
+
+	setEndOfStreamMarker: () ->
+		return if @setup
+		@sendCommand("@ENDOFSTREAMMARKER") # make this string a valid word
+		@setup = true

 	setTerseMode: () ->
 		@sendCommand("!")


Update clipboard-plus settings

Update clipboard-plus settings

--- Atom/config.cson
+++ Atom/config.cson
@@ -33,4 +33,4 @@
   "clipboard-plus":
     minimumTextLength: 1
     maximumTextLength: 5000
-    maximumLinesNumber: 3
+    maximumLinesNumber: 6


Autogenerate basic manifest when none is provided

Closes #519

Fix call to separateSubjects to make sure tooBigFiles is correctly
provided

Closes #1144

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -153,7 +153,6 @@

         <UploadDropTarget accept="text/csv, text/tab-separated-values, image/*" multiple onSelect={@handleFileSelection}>
-          Manifest files are required.

           Drag-and-drop or click to upload manifests and subject images here.

           Manifests must be .csv or .tsv. The first row should define metadata headers. All other rows should include at least one reference to an image filename in the same directory as the manifest.

           Subject images can be up to {MAX_FILE_SIZE/1000}KB and any of: {{ext}{', ' if VALID_SUBJECT_EXTENSIONS[i + 1]?} for ext, i in VALID_SUBJECT_EXTENSIONS}{' '}
@@ -228,9 +227,14 @@
           @_addManifest file
           gotManifest = true

-
-      if gotFile and not gotManifest
-        @forceUpdate()
+    unless gotManifest
+      autoManifest = []
+      for name, _ of @state.files
+        autoManifest.push { Filename: name }
+      for name, _ of @state.tooBigFiles
+        autoManifest.push { Filename: name }
+
+      @subjectsFromManifest(autoManifest, [], "AutoGeneratedManifest")

   _addManifest: (file) ->
     reader = new FileReader
@@ -238,23 +242,25 @@
       # TODO: Look into PapaParse features.
       # Maybe wan we parse the file object directly in a worker.
       {data, errors} = Papa.parse e.target.result.trim(), header: true
-
-      metadatas = for rawData in data
-        cleanData = {}
-        for key, value of rawData
-          cleanData[key.trim()] = value?.trim?() ? value
-        cleanData
-
-      subjects = []
-      for metadata in metadatas
-        locations = @_findFilesInMetadata metadata
-        unless locations.length is 0
-          subjects.push {locations, metadata}
-
-      @state.manifests[file.name] = {errors, subjects}
-      @forceUpdate()
-
+      @subjectsFromManifest(data, errors, file.name)
     reader.readAsText file
+
+  subjectsFromManifest: (data, errors, fileName) ->
+    console.log(data, errors, fileName)
+    metadatas = for rawData in data
+      cleanData = {}
+      for key, value of rawData
+        cleanData[key.trim()] = value?.trim?() ? value
+      cleanData
+
+    subjects = []
+    for metadata in metadatas
+      locations = @_findFilesInMetadata metadata
+      unless locations.length is 0
+        subjects.push {locations, metadata}
+
+    @state.manifests[fileName] = {errors, subjects}
+    @forceUpdate()

   _findFilesInMetadata: (metadata) ->
     filesInMetadata = []
@@ -271,7 +277,7 @@
   createSubjects: ->
     allSubjects = []
     for name, {subjects} of @state.manifests
-      {ready} = ManifestView.separateSubjects subjects, @state.files
+      {ready} = ManifestView.separateSubjects subjects, @state.files, @state.tooBigFiles
       allSubjects.push ready...

     uploadAlert = (resolve) =>


fix bug with candidatures pen sometimes not display pen

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -317,9 +317,6 @@
       for candidature in candidatures
           artist_id = candidature.artist.match(/\d+$/)[0]
           candidature.progress = $scope.get_candidature_progress(candidature)
-          arr.push(candidature)
-
-
           if(candidature.application_completed)
               ArtistsV2.one(artist_id).withHttpConfig({ cache: true}).get().then((artist) ->
                   current_cantidature = _.filter(candidatures, (c) -> return c.artist == artist.url)
@@ -330,6 +327,7 @@
                     current_cantidature[0].artist.user = user_infos
                   )
               )
+              # get media  justifications
               if(candidature.cursus_justifications != null)
                   gallery_id = candidature.cursus_justifications.match(/\d+$/)[0]
                   Galleries.one(gallery_id).get().then((gallery_infos) ->
@@ -346,6 +344,8 @@
               # chek if there is observations
               observation = if candidature.observation != null then JSON.parse(candidature.observation) else {jury:""}
               candidature.has_observation = (observation.jury != '' || (observation[$rootScope.user.username] && observation[$rootScope.user.username] != ""))
+          # push candidature after all treatments
+          arr.push(candidature)
     )
     return arr

@@ -475,7 +475,7 @@
       Candidatures.one(id).get().then((candidature) ->
         $scope.candidature = candidature
         $scope.itw_date = new Date(candidature.interview_date)
-        # has observations
+        # has observations
         observation = if candidature.observation != null then JSON.parse(candidature.observation) else {jury:""}
         $scope.candidature.has_observation = (observation.jury != '' || (observation[$rootScope.user.username] && observation[$rootScope.user.username] != ""))
         artist_id = candidature.artist.match(/\d+$/)[0]


Fix sunglare

Fix sunglare

--- app/scripts/scenes/City.coffee
+++ app/scripts/scenes/City.coffee
@@ -42,7 +42,7 @@
   duration = steps * 500 * 2

   sun = Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween, Collision')
-    .attr(w: 60, h: 60, z: -1000, x: 550, y: 710)
+    .attr(w: 60, h: 60, z: -1000)
     .color('#DDDD00')
     .viewportRelativeMotion(
       x: 550
@@ -53,8 +53,19 @@
     .attr(dx: 200)

   glare = []
-  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween')
-    .attr(w: 80, h: 80, z: 1000, x: 150, y: 10, alpha: 0.7, originalAlpha: 0.7)
+  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween, Glare')
+    .attr(w: 90, h: 90, z: 900, alpha: 0.4, originalAlpha: 0.4)
+    .color('#FFFFFF')
+    .viewportRelativeMotion(
+      x: 550
+      y: 395
+      speed: 0
+    )
+    .tween({ dy: -340 }, duration)
+    .attr(dx: 185)
+  )
+  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween, Glare')
+    .attr(w: 80, h: 80, z: 1000, alpha: 0.7, originalAlpha: 0.7)
     .color('#B0B0FF')
     .viewportRelativeMotion(
       x: 150
@@ -65,8 +76,8 @@
     .attr(dx: -200)
   )

-  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween')
-    .attr(w: 10, h: 10, z: 1000, x: 150, y: 10, alpha: 0.8, originalAlpha: 0.8)
+  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween, Glare')
+    .attr(w: 10, h: 10, z: 1000, alpha: 0.8, originalAlpha: 0.8)
     .color('#FF9090')
     .viewportRelativeMotion(
       x: 150
@@ -77,8 +88,8 @@
     .attr(dx: -100)
   )

-  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween')
-    .attr(w: 200, h: 200, z: 1000, x: 150, y: 10, alpha: 0.2, originalAlpha: 0.2)
+  glare.push(Crafty.e('2D, Canvas, Color, ViewportRelativeMotion, Tween, Glare')
+    .attr(w: 200, h: 200, z: 1000, alpha: 0.2, originalAlpha: 0.2)
     .color('#FF9090')
     .viewportRelativeMotion(
       x: 150
@@ -88,10 +99,13 @@
     .tween({ dy: 410 }, duration)
     .attr(dx: -300)
   )
-  sun.onHit '2D', (c) ->
-    covered = 0
+  Crafty.bind 'EnterFrame', ->
+    covered = [0]
     sunArea = sun.area()
-    for o in c
+
+    for o in sun.hit('2D')
+      continue if o.obj is sun
+      continue if o.obj.has 'Glare'
       e = o.obj
       xMin = Math.max(sun.x, e.x)
       xMax = Math.min(sun.x + sun.w, e.x + e.w)
@@ -99,9 +113,10 @@
       yMin = Math.max(sun.y, e.y)
       yMax = Math.min(sun.y + sun.h, e.y + e.h)
       h = yMax - yMin
-      covered += w * h
+      covered.push(w * h)

-    perc = covered / sunArea
+
+    perc = Math.max(covered...) / sunArea
     perc = 1 if covered > sunArea
     for e in glare
       e.attr alpha: e.originalAlpha * (1 - perc)


add prod db dump

add prod db dump

--- app/templates/_Gulpfile.coffee
+++ app/templates/_Gulpfile.coffee
@@ -110,7 +110,11 @@
 # 	http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html
 #*------------------------------------*/
 gulp.task "db_dump:local", shell.task [
-  'mysqldump --host=' + pvt.db_local.host + ' --user=' + pvt.db_local.user + ' --password=' + pvt.db_local.pass + ' ' + pvt.db_local.name + ' > ./database/local2.sql'
+  'mysqldump --host=' + pvt.db_local.host + ' --user=' + pvt.db_local.user + ' --password=' + pvt.db_local.pass + ' ' + pvt.db_local.name + ' > ./database/local.sql'
+]
+
+gulp.task "db_dump:prod", shell.task [
+  'mysqldump --host=' + pvt.db_prod.host + ' --user=' + pvt.db_prod.user + ' --password=' + pvt.db_prod.pass + ' ' + pvt.db_prod.name + ' > ./database/prod.sql'
 ]


Use mongohq if env var is available

Use mongohq if env var is available

--- server.coffee
+++ server.coffee
@@ -4,7 +4,7 @@
 require 'datejs'
 md = require("node-markdown").Markdown

-mongoose.connect 'mongodb://localhost/lnug'
+mongoose.connect process.env['MONGOHQ_URL'] || 'mongodb://localhost/lnug'

 Schema = mongoose.Schema
 ObjectId = Schema.ObjectId


:art: Clear up mergeSuggestionsFromProviders even more!

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -126,11 +126,9 @@
       .then(_.partial(@mergeSuggestionsFromProviders, providers))
       .then((suggestions) => @displaySuggestions(suggestions, suggestionsPromise, options))

-  # providers - An array of providers to check against provided suggestions
   # providerSuggestions - array of arrays of suggestions provided by all called providers
-  mergeSuggestionsFromProviders: (providers, providerSuggestions) ->
-    providerSuggestions.reduce (suggestions, providerSuggestions, index) ->
-      provider = providers[index]
+  mergeSuggestionsFromProviders: (providerSuggestions) ->
+    providerSuggestions.reduce (suggestions, providerSuggestions) ->
       suggestions = suggestions.concat(providerSuggestions) if providerSuggestions?.length
       suggestions
     , []


Use TextBuffer.prototype.onDidChangeText

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -74,6 +74,7 @@
     # Subscribe to buffer events:
     @editorSubscriptions.add(@buffer.onDidSave(@bufferSaved))
     @editorSubscriptions.add(@buffer.onDidChange(@bufferChanged))
+    @editorSubscriptions.add(@buffer.onDidChangeText(@bufferChangedText))

     # Watch IME Events To Allow IME To Function Without The Suggestion List Showing
     compositionStart = => @compositionInProgress = true
@@ -442,18 +443,22 @@
   #
   # data - An {Object} containing information on why the cursor has been moved
   cursorMoved: ({textChanged}) =>
-    # The delay is a workaround for the backspace case. The way atom implements
-    # backspace is to select left 1 char, then delete. This results in a
-    # cursorMoved event with textChanged == false. So we delay, and if the
-    # bufferChanged handler decides to show suggestions, it will cancel the
-    # hideSuggestionList request. If there is no bufferChanged event,
-    # suggestionList will be hidden.
-    @requestHideSuggestionList() unless textChanged
+    @requestHideSuggestionList() if not textChanged and not @shouldActivate

   # Private: Gets called when the user saves the document. Cancels the
   # autocompletion.
   bufferSaved: =>
     @hideSuggestionList() unless @autosaveEnabled
+
+  bufferChangedText: =>
+    if @shouldActivate
+      @cancelHideSuggestionListRequest()
+      @requestNewSuggestions()
+    else
+      @cancelNewSuggestionsRequest()
+      @hideSuggestionList()
+
+    @shouldActivate = false

   # Private: Cancels the autocompletion if the user entered more than one
   # character with a single keystroke. (= pasting)
@@ -461,34 +466,27 @@
   # event - The change {Event}
   bufferChanged: ({newText, newRange, oldText, oldRange}) =>
     return if @disposed
+    return if @shouldActivate
     return @hideSuggestionList() if @compositionInProgress
-    shouldActivate = false
+
     cursorPositions = @editor.getCursorBufferPositions()

     if @autoActivationEnabled or @suggestionList.isActive()
-
       # Activate on space, a non-whitespace character, or a bracket-matcher pair.
       if newText.length > 0
-        shouldActivate =
+        @shouldActivate =
           (cursorPositions.some (position) -> newRange.containsPoint(position)) and
           (newText is ' ' or newText.trim().length is 1 or newText in @bracketMatcherPairs)

       # Suggestion list must be either active or backspaceTriggersAutocomplete must be true for activation to occur.
       # Activate on removal of a space, a non-whitespace character, or a bracket-matcher pair.
       else if oldText.length > 0
-        shouldActivate =
+        @shouldActivate =
           (@backspaceTriggersAutocomplete or @suggestionList.isActive()) and
           (cursorPositions.some (position) -> newRange.containsPoint(position)) and
           (oldText is ' ' or oldText.trim().length is 1 or oldText in @bracketMatcherPairs)

-      shouldActivate = false if shouldActivate and @shouldSuppressActivationForEditorClasses()
-
-    if shouldActivate
-      @cancelHideSuggestionListRequest()
-      @requestNewSuggestions()
-    else
-      @cancelNewSuggestionsRequest()
-      @hideSuggestionList()
+      @shouldActivate = false if @shouldActivate and @shouldSuppressActivationForEditorClasses()

   shouldSuppressActivationForEditorClasses: ->
     for classNames in @suppressForClasses


added logging to bug me

added logging to bug me

--- scripts/slackbot-examples.coffee
+++ scripts/slackbot-examples.coffee
@@ -82,6 +82,8 @@
   robot.router.get '/hubot/my-custom-url/:room', (req, res) ->
     robot.emit "bug-me", {
       room: req.params.room
+      # note the REMOVE THIS PART in this example -- since we are using a GET and the link is being published in the chat room
+      # it can cause an infinite loop since slack itself pre-fetches URLs it sees
       source: "a HTTP call to #{process.env.HEROKU_URL or ''}[/ REMOVE THIS PART ]/hubot/my-custom-url/#{req.params.room} (could be any room name)"
     }
     # reply to the browser
@@ -92,6 +94,7 @@
   # note that if you direct message this command to the bot, you don't need to prefix it with the name of the bot
   ###
   robot.respond /bug me/i, (msg) ->
+    console.log(msg)
     robot.emit "bug-me", {
       room: get_user_id(msg),
       source: 'use of the bug me command'


add default options to withDummyClient and withConvertedClient

--- workers/social/testhelper/index.coffee
+++ workers/social/testhelper/index.coffee
@@ -65,6 +65,9 @@

 withDummyClient = (context, callback) ->

+  [context, callback] = [callback, context]  unless callback
+  context         ?= { group : 'koding' }
+
   generateDummyClient context, (err, client) ->
     expect(err).to.not.exist
     callback { client }
@@ -72,6 +75,7 @@

 withConvertedUser = (opts, callback) ->

+  [opts, callback]          = [callback, opts]  unless callback
   { context, userFormData } = opts  if opts

   context      ?= { group : 'koding' }
@@ -84,7 +88,7 @@
       client.sessionToken        = newToken
       client.connection.delegate = account

-      if opts.userFormData?
+      if opts?.userFormData?
       then callback { client, account, sessionToken : newToken }
       else callback { client, account, sessionToken : newToken, userFormData }

@@ -154,6 +158,7 @@

 module.exports = {
+  _
   daisy
   expect
   withDummyClient


update-tag

update-tag

--- lib/_config/_config.coffee
+++ lib/_config/_config.coffee
@@ -8,7 +8,7 @@
 	title: ->
 			TAPi18n.__ 'Buy Sell and Pay'
 	subtitle: ->
-			TAPi18n.__ 'Anyone'
+			TAPi18n.__ 'any Thing'
 	logo: ->
 		'<b>' + @name + '</b>'
 	footer: ->


Bug fix

Bug fix

--- rest_controller.coffee
+++ rest_controller.coffee
@@ -35,7 +35,11 @@
       cursor.toJSON (err, json) =>
         return res.send(404) if err
         return res.json({result: json}) if req.query.$count
-        return res.json(json) unless json
+        unless json
+          if req.query.$one
+            return res.status(404).send(error: "Model not found with id: #{req.query.id}")
+          else
+            res.json(json)
         return res.json(json) unless @render

         # TODO: intgrate the cache -> would need to be done as part of the fetch
@@ -58,7 +62,7 @@
       cursor = cursor.whiteList(@white_lists.show) if @white_lists.show
       cursor.toJSON (err, json) =>
         return res.status(404).send(error: err.toString()) if err
-        return res.status(404).send("Model not found with id: #{req.params.id}") unless json
+        return res.status(404).send(error: "Model not found with id: #{req.params.id}") unless json
         json = _.pick(json, @white_lists.show) if @white_lists.show
         return res.json(json) unless @render

@@ -94,7 +98,7 @@
       json = if @white_lists.update then _.pick(req.body, @white_lists.update) else req.body
       @model_type.find req.params.id, (err, model) =>
         return res.status(404).send(error: err.toString()) if err
-        return res.status(404).send("Model not found with id: #{req.params.id}") unless model
+        return res.status(404).send(error: "Model not found with id: #{req.params.id}") unless model
         model.save model.parse(json), {
           success: =>
             json = model.toJSON()


Update index.coffee

Update index.coffee

--- samples/node/src/index.coffee
+++ samples/node/src/index.coffee
@@ -1,3 +1,7 @@
+# Author : Xavier Carpentier
+# Twitter : @xcapetir
+# Mail : contact@xavier-carpentier.com
+
 HOST = '127.0.0.1'
 PORT = 4242


Fix mail sending

Fix mail sending

--- server/mails/mail_handler.coffee
+++ server/mails/mail_handler.coffee
@@ -155,17 +155,28 @@
                 fs.writeFile icsPath, calendar.toString(), (err) ->
                     if (err)
                         log.error """
-                          An error occured while creating invitation file #{icsPath}
+                          An error occured while creating invitation file
+                          #{icsPath}
                         """
                         log.error err
                     else
-                        needSaving   = true
-                        guest.status = 'NEEDS-ACTION' # ical = waiting an answer
-
-                    fs.unlink icsPath, (errUnlink) ->
-                        if errUnlink
-                            log.error "Error deleting ics file #{icsPath}"
-                        done err
+                        # Send mail through CozyDB API
+                        cozydb.api.sendMailFromUser mailOptions, (err) ->
+                            if err
+                                log.error """
+                                    An error occured while sending invitation
+                                """
+                                log.error err
+                            else
+                                needSaving   = true
+                                guest.status = 'NEEDS-ACTION' # ical = waiting an answer
+
+                            fs.unlink icsPath, (errUnlink) ->
+                                if errUnlink
+                                    log.error """
+                                        Error deleting ics file #{icsPath}
+                                    """
+                                done err

         # Catch errors when doing async foreach
         , (err) ->


fix localization in inviation email

fix localization in inviation email

--- server/mails/mail_handler.coffee
+++ server/mails/mail_handler.coffee
@@ -44,12 +44,12 @@
             subject = localization.t subjectKey, description: event.description
             url     = "#{domain}public/calendar/events/#{event.id}"

-            date          = event.formatStart dateFormat
-            dateFormat    = localization.t dateFormatKey
             dateFormatKey = if event.isAllDayEvent()
                 'email date format allday'
             else
                 'email date format'
+            dateFormat    = localization.t dateFormatKey
+            date          = event.formatStart dateFormat

             {description, place} = event.toJSON()
             place = if place?.length > 0 then place else ""


add aliases to command line options

add aliases to command line options

--- bin/tourney-time.coffee
+++ bin/tourney-time.coffee
@@ -6,6 +6,9 @@
   .default('time', 33)
   .default('rest', 7)
   .default('areas', 1)
+  .alias('t', 'time')
+  .alias('r', 'rest')
+  .alias('a', 'areas')
   .argv

 {teams, time, rest, areas} = argv


Paging data responses

Paging data responses

--- rest.coffee
+++ rest.coffee
@@ -1,27 +1,41 @@
 Q = require 'q'

+# API endpoints setup
 init = (app, api) ->
   for [route, method, handler] in endpoints()
     console.log "adding endpoint: #{route}"
     app[method](route, handler(api))

+# Enumeration of all endpoints
 endpoints = -> [
   [/user\/\w+/, 'get', listUser]
   ['/user', 'get', listUsers]
-  [/user/, 'post', createUser]
+  ['/user', 'post', createUser]
 ]

+# Endpoint handler methods
+
+# Lists one user
 listUser = (api) ->
   (req, res) ->
+    userId =
     console.log 'list user'

+# Lists all users
 listUsers = (api) ->
   (req, res) ->
     api.listUsers()
     .then((users) ->
       if users?.length
         console.log "200: found users: ", users
-        res.json(200, users)
+        page =
+          totalCount: users.length
+          count: users.length
+          offset: 0
+          limit: -1
+          objects: users
+
+        res.json(200, page)
       else
         console.log "404: didn't find any users"
         res.json(404, {})


Mimic bracket-matcher surround selected text with backtick bracket-matcher fails to work with backticks following a tagname. e.g. tag` so I remedied that but forgot to also allow it to work with wrapping selected text with a backtick. Issue #370 ci skip

--- lib/did-insert-text.coffee
+++ lib/did-insert-text.coffee
@@ -64,8 +64,11 @@
     return true unless @bracketMatcherBackticks()
     cursorBufferPosition = @editor.getCursorBufferPosition()
     return true if 'punctuation.definition.quasi.begin.js' is @editor.scopeDescriptorForBufferPosition(cursorBufferPosition).getScopesArray().slice(-1).toString()
-    @editor.insertText("``")
-    @editor.moveLeft()
+    selectedText = @editor.getSelectedText()
+    cursorPosition = @editor.getCursorBufferPosition()
+    @editor.insertText("`" + selectedText + "`")
+    @editor.setCursorBufferPosition(cursorPosition)
+    @editor.moveRight()
     false


Use sequentially instead of fromArray

Use sequentially instead of fromArray

per FAQ recommendation,
https://github.com/baconjs/bacon.js/wiki/FAQ#why-isnt-my-subscriber-call
ed

--- lib/atom/project-paths.coffee
+++ lib/atom/project-paths.coffee
@@ -3,11 +3,11 @@

 filteredPathsStream = (pairwisePathsStream, filterFn) ->
   pairwisePathsStream.flatMap (pair) ->
-    Bacon.fromArray filterFn(pair)
+    Bacon.sequentially 0, filterFn(pair)

 module.exports = ->
   pathsStream = atomStreams.fromDisposable atom.project, 'onDidChangePaths'
-    .merge Bacon.fromArray [ [], atom.project.getPaths() ]
+    .merge Bacon.sequentially 0, [ [], atom.project.getPaths() ]
   lastProjectPathsProp = pathsStream.slidingWindow(2, 2)

   addStream = filteredPathsStream lastProjectPathsProp, ([currentPaths, newPaths]) ->


replace buttons by menu.. first try

replace buttons by menu.. first try

--- lib/go-outline-view.coffee
+++ lib/go-outline-view.coffee
@@ -34,6 +34,12 @@
         @div class: "go-outline-search", =>
           @subview 'searchField', new TextEditorView({mini: true, placeholderText:"filter"})
           @div class: "icon icon-x", outlet: 'btnResetFilter'
+      @div class: "select-list popover-list", =>
+        @ol class: "list-group", =>
+          @li class:"", =>
+            @div class: "status status-ignored  icon icon-mention", "Show Variables"
+          @li "second"
+          @li "third"
       @div class: 'go-outline-tree-scroller order--center', outlet: 'scroller', =>
         @ol class: 'go-outline-tree full-menu list-tree has-collapsable-children focusable-panel', tabindex: -1, outlet: 'list'
       @div class: 'go-outline-tree-resize-handle', outlet: 'resizeHandle'


Update vansteki.giphy.coffee

Update vansteki.giphy.coffee

--- scripts/vansteki.giphy.coffee
+++ scripts/vansteki.giphy.coffee
@@ -14,6 +14,6 @@
   robot.hear /gi (.*)/i, (msg) ->
     tags = msg.match[1].trim().split(', ')
     getRandomGiphyGif(msg, tags)
-  robot.hear /bitch|婊|婊子/ig, (msg) ->
+  robot.hear /bitch/ig, (msg) ->
     msg.send "我最喜歡婊子了!"
     getRandomGiphyGif(msg, 'bitch')


:abc: Simplify Docs

:abc: Simplify Docs

--- lib/autocomplete-paths.coffee
+++ lib/autocomplete-paths.coffee
@@ -15,7 +15,7 @@
       @editorSubscription = atom.workspace.observeTextEditors((editor) => @registerProvider(Provider, editor))

   ###
-   * Registers a Provider for each editor
+   * Registers a Provider for an editor
   ###
   registerProvider: (Provider, editor) ->
     return unless Provider?
@@ -28,7 +28,7 @@
       @providers.push(provider)

   ###
-   * Cleans everything up, unregisters all SnippetProvider instances
+   * Cleans everything up, unregisters all Provider instances
   ###
   deactivate: ->
     @editorSubscription?.dispose()


remove tree-view:remove-project-folder command listener

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -165,7 +165,6 @@
       'tree-view:recursive-expand-directory': => @resize()
       'tree-view:collapse-directory': => @resize()
       'tree-view:recursive-collapse-directory': => @resize()
-      'tree-view:remove-project-folder': => @resize()

     @disposables.add 'atom-workspace', 'mouseup', '.tree-view-resizer .entry.directory', => @resize()


Refactor enterprise controller to reduce code duplication

--- app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee
+++ app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee
@@ -69,25 +69,29 @@
       $scope.newUser = $scope.invite_errors = $scope.invite_success = null

     $scope.removeLogo = ->
-      return unless confirm(t("js.admin.enterprises.form.images.immediate_logo_removal_warning"))
+      return unless confirm($scope.translation("immediate_logo_removal_warning"))

-      Enterprises.removeLogo($scope.Enterprise).then (data) ->
+      Enterprises.removeLogo($scope.Enterprise)
+        .then $scope.removeImageSuccessCallback("removed_logo_successfully"),
+              $scope.removeImageSuccessCallback()
+
+    $scope.removePromoImage = ->
+      return unless confirm($scope.translation("immediate_promo_image_removal_warning"))
+
+      Enterprises.removePromoImage($scope.Enterprise)
+        .then $scope.removeImageSuccessCallback("removed_promo_image_successfully"),
+              $scope.removeImageSuccessCallback()
+
+    $scope.removeImageSuccessCallback = (success_message_key) ->
+      (data) ->
         $scope.Enterprise = angular.copy(data)
         $scope.$emit("enterprise:updated", $scope.Enterprise)
+        StatusMessage.display("success", $scope.translation(success_message_key))

-        StatusMessage.display("success", t("js.admin.enterprises.form.images.removed_logo_successfully"))
-      , (response) ->
+    $scope.removeImageErrorCallback = ->
+      (response) ->
         if response.data.error?
           StatusMessage.display("failure", response.data.error)

-    $scope.removePromoImage = ->
-      return unless confirm(t("js.admin.enterprises.form.images.immediate_promo_image_removal_warning"))
-
-      Enterprises.removePromoImage($scope.Enterprise).then (data) ->
-        $scope.Enterprise = angular.copy(data)
-        $scope.$emit("enterprise:updated", $scope.Enterprise)
-
-        StatusMessage.display("success", t("js.admin.enterprises.form.images.removed_promo_image_successfully"))
-      , (response) ->
-        if response.data.error?
-          StatusMessage.display("failure", response.data.error)
+    $scope.translation = (key) ->
+      t('js.admin.enterprises.form.images.' + key)