Update es-419.coffee (#6402)

Update es-419.coffee (#6402)

Spanish Latin America Translation

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -26,7 +26,7 @@
     century_skills_subtitle3: "Colaboración"
     century_skills_subblurb3: "A lo largo del juego hay oportunidades para que los estudiantes colaboren cuando se atasquen y trabajen juntos utilizando nuestra guía de programación en pareja."
     century_skills_quote4: "Siempre he tenido aspiraciones de diseñar videojuegos y aprender a programar ... esto me está dando un gran punto de partida."
-#    century_skills_quote4_author: "Joey, 10th Grade"
+    century_skills_quote4_author: "Joey, décimo grado"
     century_skills_subtitle4: "Comunicación"
     century_skills_subblurb4: "La programación requiere que los niños practiquen nuevas formas de comunicación, incluida la comunicación con la computadora y la transmisión de sus ideas utilizando el código más eficiente."
     classroom_in_box_title: "Nos esforzamos por:"
@@ -34,7 +34,7 @@
     classroom_in_box_blurb2: "Apoderar a cualquier educador para que se sienta seguro al enseñar programación."
     classroom_in_box_blurb3: "Inspirar a todos los líderes escolares para crear un programa de Ciencias de la Computación de clase mundial."
 #    classroom_in_box_blurb4: ""
-#    click_here: "Click here"
+    click_here: "Click aquí"
     creativity_rigor_title: "Donde la creatividad alcanza el rigor"
     creativity_rigor_subtitle1: "Haga que la programación sea divertida y enseñe habilidades del mundo real"
     creativity_rigor_blurb1: "Los estudiantes escriben Python y JavaScript real mientras juegan juegos que fomentan el ensayo y error, el pensamiento crítico y la creatividad. Luego, los estudiantes aplican las habilidades de programación que han aprendido al desarrollar sus propios juegos y sitios web en cursos basados en proyectos."
@@ -83,24 +83,24 @@
     quotes_quote2: " Me sorprendió lo fácil e intuitivo que CodeCombat hace que aprendan Ciencias de la Computación. Las calificaciones en el examen AP fueron mucho más altas de lo que esperaba y creo que CodeCombat es la razón por la que este fue el caso."
     quotes_quote3: "CodeCombat ha sido lo más beneficioso para enseñar a mis alumnos las capacidades de programación de la vida real. Mi esposo es ingeniero de software y ha probado todos mis programas. Puso esto como su mejor opción."
     quotes_quote4: "Los comentarios ... han sido tan positivos que estamos estructurando una clase de informática en torno a CodeCombat. El programa realmente involucra a los estudiantes con una plataforma estilo juego que es entretenida e instructiva al mismo tiempo. ¡Sigan con el buen trabajo, CodeCombat!"
-#    quotes_quote5: "Even though the class starts every Saturday at 7am, my son is so excited that he wakes up before me! CodeCombat creates a pathway for my son to advance his coding skills."
-#    quotes_quote5_author: "Latthaphon Pohpon, Parent"
+    quotes_quote5: "Aunque la clase comienza todos los sábados a las 7 am, ¡mi hijo está tan emocionado que se despierta antes que yo! CodeCombat crea un camino para que mi hijo avance en sus habilidades de codificación."
+    quotes_quote5_author: "Latthaphon Pohpon, Padre"
     see_example: "Ver ejemplo"
     slogan: "El juego más interesante para aprender programación." # {change}
-#    slogan_power_of_play: "Learn to Code Through the Power of Play"
+    slogan_power_of_play: "Aprenda a codificar a través del poder del juego"
     teach_cs1_free: "Enseña programación gratis"
     teachers_love_codecombat_title: "A los maestros les encanta CodeCombat"
     teachers_love_codecombat_blurb1: "Informan que sus estudiantes disfrutan usando CodeCombat para aprender a programar"
     teachers_love_codecombat_blurb2: "Recomendarían CodeCombat a otros maestros de Ciencias de la Computación"
     teachers_love_codecombat_blurb3: "Dicen que CodeCombat los ayuda a mejorar las habilidades de resolución de problemas de sus estudiantes"
     teachers_love_codecombat_subblurb: "En asociación con McREL International, líder en orientación basada en investigación y evaluaciones de tecnología educativa."
-#    top_banner_blurb: "Parents, give your child the gift of coding and personalized instruction with our live teachers!"
-#    top_banner_summer_camp: "Enrollment now open for our summer coding camps–ask us about our week-long virtual sessions starting at just $199."
+    top_banner_blurb: "¡Padres, denle a su hijo el regalo de la codificación y la instrucción personalizada con nuestros maestros en vivo!"
+    top_banner_summer_camp: "La inscripción ya está abierta para nuestros campamentos de programación de verano; pregúntenos acerca de nuestras sesiones virtuales de una semana a partir de solo $ 199dls."
     try_the_game: "Prueba el juego"
     classroom_edition: "Versión para el aula:"
     learn_to_code: "Aprende a programar:"
     play_now: "Jugar Ahora"
-#    im_a_parent: "I'm a Parent"
+    im_a_parent: "Soy Padre/Madre"
     im_an_educator: "Soy un Educador"
     im_a_teacher: "Soy un Maestro"
     im_a_student: "Soy un Estudiante"
@@ -163,37 +163,37 @@
     want_coco: "¿Quisiera CodeCombat en su escuela?"
     educator: "Maestro"
     student: "Estudiante"
-#    our_coding_programs: "Our Coding Programs"
+    our_coding_programs: "Nuestros programas de codificación"
 #    codecombat: "CodeCombat"
 #    ozaria: "Ozaria"
-#    codecombat_blurb: "Our original coding game. Recommended for parents, individuals, educators, and students who want to experience one of the most-loved coding games in the world."
-#    ozaria_blurb: "An adventure game and Computer Science program where students master the lost magic of coding to save their world. Recommended for educators and students."
-#    try_codecombat: "Try CodeCombat"
-#    try_ozaria: "Try Ozaria"
-#    explore_codecombat: "Explore CodeCombat"
-#    explore_ai_league: "Explore AI League"
-#    explore_ozaria: "Explore Ozaria"
-#    explore_online_classes: "Explore Online Classes"
-#    explore_pd: "Explore Professional Development"
-#    new_adventure_game_blurb: "Ozaria is our brand new adventure game and your turnkey solution for teaching Computer science. Our student-facing __slides__ and teacher-facing notes make planning and delivering lessons easier and faster."
-#    lesson_slides: "lesson slides"
-#    pd_blurb: "Learn the skills to effectively teach computer science with our self-directed, CSTA-accredited professional development course. Earn up to 40 credit hours any time, from any device. Pairs well with Ozaria Classroom."
-#    ai_league_blurb: "Competitive coding has never been so epic with this educational esports league, uniquely both an AI battle simulator and game engine for learning real code."
-#    codecombat_live_online_classes: "CodeCombat Live Online Classes"
-#    learning_technology_blurb: "Our original game teaches real-world skills through the power of play. The scaffolded curriculum systematically builds on student’s experiences and knowledge as they progress."
-#    learning_technology_blurb_short: "Our innovative game-based learning technology has transformed the way students learn to code."
-#    online_classes_blurb: "Our online coding classes combine the power of gameplay and personalized instruction for an experience your child will love. With both private or group options available, this is remote learning that works."
-#    for_educators: "For Educators"
-#    for_parents: "For Parents"
-#    for_everyone: "For Everyone"
-#    what_our_customers_are_saying: "What Our Customers Are Saying"
-#    game_based_learning: "Game-Based Learning"
-#    unique_approach_blurb: "With our unique approach, students embrace learning as they play and write code from the very start of their adventure, promoting active learning and a growth mindset."
-#    text_based_coding: "Text-Based Coding"
-#    custom_code_engine_blurb: "Our custom code engine and interpreter is designed for beginners, teaching true Python, JavaScript, and C++ programming languages using human, beginner-friendly terms."
-#    student_impact: "Student Impact"
-#    help_enjoy_learning_blurb: "Our products have helped over 20 million students enjoy learning Computer Science, teaching them to be critical, confident, and creative learners. We engage all students, regardless of experience, helping them to realize a pathway to success in Computer Science."
-#    global_community: "Join Our Global Community"
+    codecombat_blurb: "Nuestro juego de codificación original. Recomendado para padres, individuos, educadores y estudiantes que quieran experimentar uno de los juegos de codificación más queridos del mundo."
+    ozaria_blurb: "Un juego de aventuras y un programa de informática donde los estudiantes dominan la magia perdida de la codificación para salvar su mundo. Recomendado para educadores y estudiantes."
+    try_codecombat: "Prueba CodeCombat"
+    try_ozaria: "Prueba Ozaria"
+    explore_codecombat: "Explora CodeCombat"
+    explore_ai_league: "Explora AI League"
+    explore_ozaria: "Explora Ozaria"
+    explore_online_classes: "Explore las clases en línea"
+    explore_pd: "Explore el desarrollo profesional"
+    new_adventure_game_blurb: "Ozaria es nuestro nuevo juego de aventuras y su solución llave en mano para la enseñanza de la informática. Nuestras __ diapositivas__ para los estudiantes y las notas para los maestros hacen que la planificación y la entrega de lecciones sean más fáciles y rápidas."
+    lesson_slides: "diapositivas de la lección"
+    pd_blurb: "Aprenda las habilidades para enseñar ciencias de la computación de manera efectiva con nuestro curso de desarrollo profesional autodirigido y acreditado por la CSTA. Obtenga hasta 40 horas crédito en cualquier momento, desde cualquier dispositivo. Combina bien con Ozaria Classroom."
+    ai_league_blurb: "La codificación competitiva nunca ha sido tan épica con esta liga educativa de deportes electrónicos, exclusivamente un simulador de batalla de IA y un motor de juego para aprender código real."
+    codecombat_live_online_classes: "Clases en línea de CodeCombat Live"
+    learning_technology_blurb: "Nuestro juego original enseña habilidades del mundo real a través del poder del juego. El plan de estudios escalonado se basa sistemáticamente en las experiencias y el conocimiento de los estudiantes a medida que avanzan."
+    learning_technology_blurb_short: "Nuestra innovadora tecnología de aprendizaje basada en juegos ha transformado la forma en que los estudiantes aprenden a codificar."
+    online_classes_blurb: "Nuestras clases de codificación en línea combinan el poder del juego y la instrucción personalizada para una experiencia que le encantará a su hijo. Con opciones tanto privadas como grupales disponibles, este es un aprendizaje remoto que funciona."
+    for_educators: "Para Maestros"
+    for_parents: "Para Padres"
+    for_everyone: "Para Todos"
+    what_our_customers_are_saying: "Lo que nuestros clientes están diciendo"
+    game_based_learning: "Aprendizaje basado en juegos"
+    unique_approach_blurb: "Con nuestro enfoque único, los estudiantes adoptan el aprendizaje mientras juegan y escriben código desde el comienzo de su aventura, promoviendo el aprendizaje activo y una mentalidad de crecimiento."
+    text_based_coding: "Codificación basada en texto"
+    custom_code_engine_blurb: "Nuestro motor e intérprete de código personalizado está diseñado para principiantes, y enseña verdaderos lenguajes de programación Python, JavaScript y C ++ utilizando términos humanos para principiantes."
+    student_impact: "Impacto estudiantil"
+    help_enjoy_learning_blurb: "Nuestros productos han ayudado a más de 20 millones de estudiantes a disfrutar del aprendizaje de la informática, enseñándoles a ser aprendices críticos, seguros y creativos. Involucramos a todos los estudiantes, independientemente de su experiencia, ayudándolos a realizar un camino hacia el éxito en Ciencias de la Computación."
+    global_community: "Únase a nuestra comunidad global"
 #    million: "__num__ Million"
 #    billion: "__num__ Billion"

@@ -241,15 +241,15 @@
     apcsp: "Principios AP CS"
     parent: "Padres"
 #    esports: "Esports"
-#    browser_recommendation: "For the best experience we recommend using the latest version of Chrome. Download the browser here!"
-#    ozaria_classroom: "Ozaria Classroom"
-#    codecombat_classroom: "CodeCombat Classroom"
-#    ozaria_dashboard: "Ozaria Dashboard"
-#    codecombat_dashboard: "CodeCombat Dashboard"
-#    professional_development: "Professional Development"
-#    new: "New!"
-#    admin: "Admin"
-#    api_dashboard: "API Dashboard"
+    browser_recommendation: "Para obtener la mejor experiencia, recomendamos utilizar la última versión de Chrome. ¡Descarga el navegador aquí!"
+    ozaria_classroom: "Aula Ozaria"
+    codecombat_classroom: "Aula CodeCombat"
+    ozaria_dashboard: "Tablero de Ozaria"
+    codecombat_dashboard: "Tablero de CodeCombat"
+    professional_development: "Desarrollo profesional"
+    new: "Nuevo!"
+    admin: "Administrador"
+    api_dashboard: "Tablero de API"

   modal:
     close: "Cerrar"


Save the original opinion when clicking "Yes"

--- core/app/backbone/views/fact_relations/vote_view.coffee
+++ core/app/backbone/views/fact_relations/vote_view.coffee
@@ -3,32 +3,37 @@
   className: 'vote-up-down'

   initialize: ->
-    @_originalFactOpinion = @model.getFact().getFactWheel().userOpinion()
-    @_originalFactRelationOpinion = @model.current_opinion()
     @listenTo @model, "change", @render
     @listenTo @model.getFact().getFactWheel(), "change", @render

   set_fact_relation_opinion: (opinion) ->
     is_current_opinion = @model.current_opinion() == opinion
-    @model.setOpinion opinion unless is_current_opinion
+    unless is_current_opinion
+      @_originalFactRelationOpinion = @model.current_opinion()
+      @model.setOpinion opinion

   unset_fact_relation_opinion: (opinion) ->
     is_current_opinion = @model.current_opinion() == opinion
     if is_current_opinion
-      if @_originalFactRelationOpinion? && @model.current_opinion() != opinion
+      if @_originalFactRelationOpinion?
         @model.setOpinion @_originalFactRelationOpinion
+        @_originalFactRelationOpinion = null
       else
         @model.removeOpinion()

   set_fact_opinion: (opinion) ->
     is_current_opinion = @model.getFact().getFactWheel().isUserOpinion opinion
-    @_set_fact_opinion opinion unless is_current_opinion
+
+    unless is_current_opinion
+      @_originalFactOpinion = @model.getFact().getFactWheel().userOpinion()
+      @_set_fact_opinion opinion

   unset_fact_opinion: (opinion) ->
     is_current_opinion = @model.getFact().getFactWheel().isUserOpinion opinion
     if is_current_opinion
-      if @_originalFactOpinion? && !@model.getFact().getFactWheel().isUserOpinion @_originalFactOpinion
+      if @_originalFactOpinion?
         @_set_fact_opinion @_originalFactOpinion
+        @_originalFactOpinion = null
       else
         @_unset_fact_opinion opinion


Update es-419.coffee

Update es-419.coffee

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -74,7 +74,7 @@
     anonymous: "Jugador Anónimo"
     level_difficulty: "Dificultad: "
     campaign_beginner: "Campaña para principiantes"
-    awaiting_levels_adventurer_prefix: "Nosotros creamos 5 nuevos niveles cada semana" # {change}
+    awaiting_levels_adventurer_prefix: "Nosotros libreamos nuevos niveles cada semana"
     awaiting_levels_adventurer: "Registrate como un aventurero"
     awaiting_levels_adventurer_suffix: "para ser el primero en jugar nuevos niveles."
     adjust_volume: "Ajustar el volumen"
@@ -84,7 +84,7 @@
     campaign_old_multiplayer_description: "Reliquias de una era más civilizada. Ninguna simulación es ejecutada para estas arenas multijugador antiguas y sin héroes."

   share_progress_modal:
-    blurb: "¡Estás haciendo un gran progreso! Cuéntale a alguien cuánto has aprendido con CodeCombat." # {change}
+    blurb: "¡Estás haciendo un gran progreso! Cuéntale a tus padres cuánto has aprendido con CodeCombat."
     email_invalid: "Dirección de Email inválida."
     form_blurb: "¡Ingresa su email debajo y les enseñaremos!"
     form_label: "Dirección de Email"
@@ -294,7 +294,7 @@
     infinite_loop_reset_level: "Reiniciar Nivel"
     infinite_loop_comment_out: "Comente Mi Código"
     tip_toggle_play: "Activa jugar/pausa con Ctrl+P."
-    tip_scrub_shortcut: "Ctrl+[ y Ctrl+] rebobina y avance rápido." # {change}
+    tip_scrub_shortcut: "Ctrl+[ y Ctrl+] para rebobinar y avanzar rápido."
     tip_guide_exists: "Haga click en la guía en la parte superior de la página para obtener información útil"
     tip_open_source: "¡CodeCombat es 100% código abierto!"
     tip_tell_friends: "¿Disfrutando de CodeCombat? ¡Cuéntale a tus amigos acerca de nosotros!"
@@ -362,7 +362,7 @@

   leaderboard:
     leaderboard: "Tabla de Posiciones"
-    view_other_solutions: "Ver Otras Soluciones" # {change}
+    view_other_solutions: "Ver la Tabla de Posiciones"
     scores: "Puntajes"
     top_players: "Mejores jugadores"
     day: "Hoy"
@@ -402,10 +402,10 @@

   subscribe:
     comparison_blurb: "Agudiza tus habilidades con la suscripción a CodeCombat!"
-    feature1: "Más de 60 niveles basicos a lo largo de 4 mundos" # {change}
-    feature2: "7 poderosos nuevos heroés con habilidades unicas!" # {change}
-    feature3: "Más de 30 niveles extras" # {change}
-    feature4: "3500 gemas bonus cada mes!"
+    feature1: "Más de 110 niveles basicos a lo largo de 4 mundos"
+    feature2: "10 poderosos nuevos heroés con habilidades unicas!"
+    feature3: "Más de 70 niveles extras"
+    feature4: "3500 gemas de bono cada mes!"
     feature5: "Video tutoriales"
     feature6: "Soporte Premium vía email"
     feature7: "Clan Privado"
@@ -586,15 +586,15 @@
     press_paragraph_1_link: "paquete de prensa"
     press_paragraph_1_suffix: ". Todos los logos e imágenes pueden ser usados sin contactarnos directamente."
     team: "Equipo"
-    george_title: "Director Ejecutivo"
+    george_title: "Cofundador"
     george_blurb: "Negociante"
-    scott_title: "Programador" # {change}
+    scott_title: "Cofundador"
     scott_blurb: "Razonable"
-    nick_title: "Programador" # {change}
+    nick_title: "Cofundador"
     nick_blurb: "Gurú motivacional"
     michael_title: "Programador"
     michael_blurb: "Sys Admin"
-    matt_title: "Programador" # {change}
+    matt_title: "Cofundador"
     matt_blurb: "Bicicletero"
     cat_title: "Jefe Artesano"
     cat_blurb: "Maestro del Aire"


[fix] resolve paths using node path instead of fs.realpath

--- server/mails/mail_handler.coffee
+++ server/mails/mail_handler.coffee
@@ -13,7 +13,7 @@

 {VCalendar} = require 'cozy-ical'

-logoPath = fs.realpathSync './build/server/mails/assets/cozy-logo.png'
+logoPath = path.resolve './assets/cozy-logo.png'

 localization = require '../libs/localization_manager'

@@ -235,4 +235,3 @@
                 done err

         , callback
-


Add verbose mode to scripts:build to show processed modules

--- bokehjs/gulp/tasks/scripts.coffee
+++ bokehjs/gulp/tasks/scripts.coffee
@@ -2,6 +2,7 @@

 browserify = require "browserify"
 gulp = require "gulp"
+util = require "gulp-util"
 rename = require "gulp-rename"
 transform = require "vinyl-transform"
 uglify = require "gulp-uglify"
@@ -15,6 +16,7 @@
 fs = require "fs"
 path = require "path"
 shasum = require "shasum"
+argv = require("yargs").argv

 customLabeler = (b, fn) ->
   labels = {}
@@ -47,6 +49,11 @@
       .replace(/\.(coffee|js|eco)$/, "")
       .split(path.sep).join("/")
       .replace(/^(src\/(coffee|vendor)|node_modules)\//, "")
+
+  if argv.verbose
+    util.log("Processing #{name}")
+
+  name

 gulp.task "scripts:build", ->
   preludePath = path.resolve(process.cwd(), "./src/js/prelude.js")


rename vars

rename vars

--- ccss.coffee
+++ ccss.coffee
@@ -1,14 +1,14 @@
-@compile = (ccss) ->
+@compile = (rules) ->
   css = ''

-  for selector, pairs of ccss
-    # a pair can be a css declaration, or a pair of the child ccss object
-    child = {}
+  for selector, pairs of rules
+    # a pair is either a css declaration, or a nested rule
     declarations = ''
+    nested = {}

     for key, value of pairs
       if typeof value is 'object'
-        child["#{selector} #{key}"] = value
+        nested["#{selector} #{key}"] = value
       else
         #borderRadius -> border-radius
         key = key.replace /[A-Z]/g, (s) -> '-' + s.toLowerCase()
@@ -20,6 +20,6 @@
       css += declarations
       css += '}\n'

-    css += @compile child
+    css += @compile nested

   css


Solidify security standards

Solidify security standards

--- server/crypto.coffee
+++ server/crypto.coffee
@@ -15,4 +15,4 @@
   Crypto.SECRET_KEY = Meteor.settings.secretKey
 else
   console.warn "Secret key setting missing, using public one"
-  Crypto.SECRET_KEY = "whatever"+  Crypto.SECRET_KEY = "She sang beyond the genius of the sea. The water never formed to mind or voice, Like a body wholly body, fluttering Its empty sleeves; and yet its mimic motion Made constant cry, caused constantly a cry, That was not ours although we understood, Inhuman, of the veritable ocean."


Fixes #36: Set initial amount and sum of operations when precomputing bankaccount fields

--- server/models/bankaccount.coffee
+++ server/models/bankaccount.coffee
@@ -105,6 +105,9 @@
             accountNumber = account.accountNumber
             initialAmount = account.initialAmount

+            accounts[i].__data.operationSum = 0
+            accounts[i].setBalance 0
+
             for balance in balances
                 if balance.key is accountNumber
                     amount = (initialAmount + balance.value).toFixed 2
@@ -122,4 +125,4 @@
 BankAccount::toJSON = ->
     json = @toObject true
     json.amount = @getBalance()
-    return json+    return json


Better secret mismatch reporting.

Better secret mismatch reporting.

--- server/server.coffee
+++ server/server.coffee
@@ -3,8 +3,8 @@
 # Set the environment variable below, and the server will refuse to serve clients who don't know the secret.
 secret = process.env.TEXT_AID_TOO_SECRET

-# The first two of these must be installed via "npm".
-for module in [ "watchr", "optimist", "fs", "child_process" ]
+# The first three of these must be installed via "npm".
+for module in [ "watchr", "optimist", "ws", "fs", "child_process" ]
   try
     global[module] = require module
   catch
@@ -50,7 +50,7 @@
 console.log "editor: #{args.editor}"
 console.log "server: ws://#{config.host}:#{args.port}"

-WSS  = require("ws").Server
+WSS  = ws.Server
 wss  = new WSS port: args.port, host: config.host
 wss.on "connection", (ws) -> ws.on "message", handler ws

@@ -68,7 +68,11 @@

   if secret? and 0 < secret.length
     unless request.secret? and request.secret == secret
-      console.log "Mismatched or invalid secret; exiting:", secret, request.secret
+      console.log """
+        mismatched or invalid secret; aborting request:
+          required secret: #{secret}
+          received secret: #{request.secret}
+        """
       return exit()

   text = request.message


update config to take DOCUMENT_UPDATER_HOST

--- services/track-changes/config/settings.defaults.coffee
+++ services/track-changes/config/settings.defaults.coffee
@@ -11,7 +11,7 @@
 			host: process.env["LISTEN_ADDRESS"] or "localhost"
 	apis:
 		documentupdater:
-			url: "http://#{process.env["DOCUPDATER_HOST"] or "localhost"}:3003"
+			url: "http://#{process.env["DOCUMENT_UPDATER_HOST"] or process.env["DOCUPDATER_HOST"] or "localhost"}:3003"
 		docstore:
 			url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016"
 		web:


JDomain: Use group instead groupSlug

JDomain: Use group instead groupSlug

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -311,19 +311,19 @@
         then console.error err  unless err.code is 11000
         else updateRelationship domainObj

-  @ensureDomainSettingsForVM = ({account, vm, type, nickname, groupSlug, stack})->
+  @ensureDomainSettingsForVM = ({account, vm, type, nickname, group, stack})->
     domain = 'kd.io'
     if type in ['user', 'expensed']
-      requiredDomains = ["#{nickname}.#{groupSlug}.#{domain}"]
-      if groupSlug in ['koding', 'guests']
+      requiredDomains = ["#{nickname}.#{group}.#{domain}"]
+      if group in ['koding', 'guests']
         requiredDomains.push "#{nickname}.#{domain}"
     else
-      requiredDomains = ["#{groupSlug}.#{domain}", "shared.#{groupSlug}.#{domain}"]
+      requiredDomains = ["#{group}.#{domain}", "shared.#{group}.#{domain}"]

     {hostnameAlias} = vm
     @createDomains {
       account, hostnameAlias, stack,
-      domains:requiredDomains, group: groupSlug
+      domains:requiredDomains, group
     }

   bindVM: (client, params, callback)->


This might be terrible, but show buttons and make text different

--- app/assets/javascripts/incidents/batch_export.coffee
+++ app/assets/javascripts/incidents/batch_export.coffee
@@ -22,9 +22,11 @@
      window.location.href = 'incidents/batch_hastus_export?' + $.param(ids: ids)
   else
     $('table.incidents .batch-hastus-export').show()
-    $(this).text 'Select incidents to export...'
+    $('.batch-hastus-export #main-button').hide()
     $('.batch-hastus-export #xml-button').show()
     $('.batch-hastus-export #csv-button').show()
+    $('.batch-hastus-export #xml-button').text 'Select incidents to export...'
+    $('.batch-hastus-export #csv-button').text 'Select incidents to export...'
     $('.batch-hastus-export #select-all').show()
     setSelectAllText()


Fixed Fastclick initialization.

Fixed Fastclick initialization.

Initialization for the pre-minified version of Fastclick available from Github is a bit different, see:

https://github.com/ftlabs/fastclick#minified

It looks to me like a limitation of their build system. Oh well.

--- app/assets/javascripts/misc/fastclick_init.js.coffee
+++ app/assets/javascripts/misc/fastclick_init.js.coffee
@@ -3,5 +3,6 @@
 ########################################################

 $( ->
-  FastClick.attach document.body
+  attachFastClick = Origami.fastclick
+  attachFastClick document.body
 )


Attempt to initialize and bind events in different functions

--- app/assets/javascripts/mixingpanel/tracker.js.coffee
+++ app/assets/javascripts/mixingpanel/tracker.js.coffee
@@ -1,9 +1,10 @@
-class MixingpanelTracker
-  constructor: ->
-
-  activate: (internal_domain)->
+class @MixingpanelTracker
+  constructor: (internal_domain)->
+    throw "$ is not defined yet. Wait until document is ready" unless $?
     @properties = new MixingpanelProperties(internal_domain)
     @source = new MixingpanelSource(@properties)
+
+  bind: ->
     @_bindActions()

   _bindActions: ->
@@ -64,5 +65,3 @@
     mixpanel.people.set
       '$email': email
     mixpanel.identify id
-
-window.mixingpanel_tracker = new MixingpanelTracker()


Make crash reporter work.

Make crash reporter work.

--- src/atom-application.coffee
+++ src/atom-application.coffee
@@ -1,6 +1,7 @@
 AtomWindow = require './atom-window'
 BrowserWindow = require 'browser-window'
 Menu = require 'menu'
+crashReporter = require 'crash-reporter'
 app = require 'app'
 ipc = require 'ipc'
 dialog = require 'dialog'
@@ -43,6 +44,7 @@
     @pathsToOpen ?= []
     @windows = []

+    @setupCrashReporter()
     @listenForArgumentsFromNewProcess()
     @setupNodePath()
     @setupJavaScriptArguments()
@@ -86,6 +88,11 @@

     process.env['NODE_PATH'] = resourcePaths.join path.delimiter

+  setupCrashReporter: ->
+    crashReporter.setCompanyName 'GitHub'
+    crashReporter.setSubmissionURL 'https://speakeasy.githubapp.com/submit_crash_log'
+    crashReporter.setAutoSubmit true
+
   listenForArgumentsFromNewProcess: ->
     fs.unlinkSync socketPath if fs.existsSync(socketPath)
     server = net.createServer (connection) =>


Run, jenkins

Run, jenkins

--- app/assets/javascripts/oxalis/view/plane_view.coffee
+++ app/assets/javascripts/oxalis/view/plane_view.coffee
@@ -150,7 +150,6 @@

   draw : ->
-
     #Apply a single draw
     @flycam.update()


Tweak behaviours

Tweak behaviours

--- app/assets/javascripts/search_autocomplete.js.coffee
+++ app/assets/javascripts/search_autocomplete.js.coffee
@@ -25,7 +25,8 @@

     @saveOriginalState()

-    if @locationBadgeEl.is(':empty')
+    # If there's no location badge
+    if !@locationBadgeEl.children().length
       @createAutocomplete()

     @bindEvents()
@@ -65,7 +66,7 @@
   createAutocomplete: ->
     @query = "?project_id=" + @projectId + "&project_ref=" + @projectRef

-    @catComplete = @searchInput.catcomplete
+    @searchInput.catcomplete
       appendTo: 'form.navbar-form'
       source: @autocompletePath + @query
       minLength: 1
@@ -96,6 +97,7 @@
           # Return false to avoid focus on the next element
           return false

+    @autocomplete = @searchInput.data 'customCatcomplete'

   bindEvents: ->
     @searchInput.on 'keydown', @onSearchInputKeyDown
@@ -112,14 +114,19 @@
     # Remove tag when pressing backspace and input search is empty
     if e.keyCode is @keyCode.BACKSPACE and e.currentTarget.value is ''
       @removeLocationBadge()
-      @destroyAutocomplete()
+      # @destroyAutocomplete()
       @searchInput.focus()
     else if e.keyCode is @keyCode.ESCAPE
       @restoreOriginalState()
     else
       # Create new autocomplete if hasn't been created yet and there's no badge
-      if !@catComplete? and @locationBadgeEl.is(':empty')
-        @createAutocomplete()
+      if @autocomplete is undefined
+        if !@locationBadgeEl.children().length
+          @createAutocomplete()
+      else
+        # There's a badge
+        if @locationBadgeEl.children().length
+          @destroyAutocomplete()

   onSearchInputFocus: =>
     @wrap.addClass('search-active')
@@ -181,5 +188,6 @@
     location.href = result.url

   destroyAutocomplete: ->
-    @catComplete.destroy() if @catComplete?
-    @catComplete = null
+    @autocomplete.destroy() if @autocomplete isnt undefined
+    @searchInput.attr('autocomplete', 'off')
+    @autocomplete = undefined


Replace lab index use of history with browserHistory (#4700)

--- app/pages/lab/index.cjsx
+++ app/pages/lab/index.cjsx
@@ -1,6 +1,6 @@
 React = require 'react'
 createReactClass = require 'create-react-class'
-{Link} = require 'react-router'
+{browserHistory, Link} = require 'react-router'
 apiClient = require 'panoptes-client/lib/api-client'
 ModalFormDialog = require 'modal-form/dialog'
 { Helmet } = require 'react-helmet'
@@ -225,7 +225,7 @@
     queryUpdate[which] = page
     newQuery = Object.assign {}, @props.location.query, queryUpdate
     newLocation = Object.assign {}, @props.location, query: newQuery
-    @props.history.replace newLocation
+    browserHistory.push newLocation

   showProjectCreator: ->
     @setState creationInProgress: true
@@ -236,7 +236,7 @@
   handleProjectCreation: (project) ->
     @hideProjectCreator()
     newLocation = Object.assign {}, @props.location, pathname: "/lab/#{project.id}"
-    @props.history.push newLocation
+    browserHistory.push newLocation

   render: ->
     if @props.user?


New API version (`createLazy`)

New API version (`createLazy`)

--- index.coffee
+++ index.coffee
@@ -5,60 +5,76 @@

 {assert} = console

-prefixedNamesCache = {}
+createSignal = (obj, name) ->
+	expect(obj).not().toBe.primitive()
+	expect(name).toBe.truthy().string()
+
+	handlerName = exports.getHandlerName name
+
+	assert not obj[name]?
+	, "Signal `#{name}` can't be created, because object `#{obj}` " +
+	  "has already defined such property"
+
+	obj[name] = createSignalFunction obj
+
+createHandler = (obj, name) ->
+	expect(obj).not().toBe.primitive()
+	expect(name).toBe.truthy().string()
+
+	handlerName = exports.getHandlerName name
+
+	assert not obj[handlerName]?
+	, "Handler `#{handlerName}` can't be created, because object `#{obj}` " +
+	  "has already defined such property"
+
+	signal = obj[name]
+	obj[handlerName] = createHandlerFunction obj, signal
+
+exports.getHandlerName = do ->
+	cache = {}
+
+	(name) ->
+		if cache.hasOwnProperty name
+			cache[name]
+		else
+			cache[name] = "on#{utils.capitalize name}"

 exports.create = (obj, name) ->
 	expect(obj).not().toBe.primitive()
 	expect(name).toBe.truthy().string()

-	signal = exports.createOnlySignal obj, name
-	exports.createHandler obj, name
+	createSignal obj, name
+	createHandler obj, name

-	signal
-
-exports.createOnlySignal = (obj, name) ->
+exports.createLazy = (obj, name) ->
 	expect(obj).not().toBe.primitive()
 	expect(name).toBe.truthy().string()

-	assert not obj[name]?
-	, "Signal `#{name}` can't be created, because object `#{obj}` has already defined such property"
+	handlerName = exports.getHandlerName name
+	handler = createHandler obj, name

-	signal = obj[name] = newSignal()
-	signal.connected = newSignal()
-	signal.disconnected = newSignal()
+	exports.create handler, 'initialized'

-	signal
+	utils.defProp obj, handlerName, 'ec', ->
+		signal = @[name]
+		unless signal?
+			signal = @[name] = createSignalFunction @, handler
+			handler.initialized @, name

-exports.createHandler = (obj, name) ->
-	expect(obj).not().toBe.primitive()
-	expect(name).toBe.truthy().string()
+		handler.listeners = signal.listeners
+		handler
+	, null

-	# fast connecting
-	prefixedName = exports.getHandlerName name
-	connect = (arg1) -> @[name].connect arg1
-	utils.defProp obj, prefixedName, 'c', ->
-		connect
-	, (listener) ->
-		@[name].disconnectAll()
-		if listener?
-			@[name].connect listener
+	handler

-exports.getHandlerName = (name) ->
-	prefixedName = prefixedNamesCache[name]
-	unless prefixedName
-		prefixedName = prefixedNamesCache[name] = "on#{utils.capitalize name}"
-	prefixedName
-
-newSignal = ->
-	args = []
-
+createSignalFunction = (obj) ->
 	signal = ->
 		n = listeners.length
 		return unless n

+		args = []
 		for arg, i in arguments
 			args[i] = arg
-		args.length = arguments.length

 		i = -1
 		while ++i < n
@@ -68,24 +84,30 @@
 				i--; n--
 				continue

-			func.apply @, args
+			func.apply obj, args

 		null

 	listeners = signal.listeners = []

-	utils.setPrototypeOf signal, SignalPrototype
-
 	signal

-SignalPrototype =
+createHandlerFunction = (obj, signal) ->
+	handler = (listener) ->
+		HandlerPrototype.connect.call handler, listener
+
+	handler.listeners = signal?.listeners
+	utils.setPrototypeOf handler, HandlerPrototype
+
+	handler
+
+HandlerPrototype =
 	connect: (listener) ->
 		expect(@).toBe.function()
 		expect(listener).toBe.function()
 		expect().some(@listeners).not().toBe listener

 		@listeners.push listener
-		@connected? listener

 	disconnect: (listener) ->
 		expect(@).toBe.function()
@@ -94,7 +116,6 @@

 		index = @listeners.indexOf listener
 		@listeners[index] = null
-		@disconnected? listener

 	disconnectAll: ->
 		expect(@).toBe.function()


Use block texture files instead of flat shading. Closes #3

--- index.coffee
+++ index.coffee
@@ -17,8 +17,12 @@
   defaults =
     generate: voxel.generator['Valley']
     chunkDistance: 2
-    materials: ['#fff', '#000'],
-    materialFlatColor: true,
+    materials: [
+      ['grass_top', 'dirt', 'grass_dirt_side'],
+      'obsidian',
+      'dirt',
+      ]
+    texturePath: '/textures/'
     worldOrigin: [0, 0, 0],
     controls:
       discreteFire: true
@@ -125,4 +129,3 @@
       walk.stopWalking()
     else
       walk.startWalking()
-


travis

travis

--- index.coffee
+++ index.coffee
@@ -20,3 +20,4 @@
     s.options = options || {}
     s.options.minPrefixLength ?= 3
     s
+


Added "asyncForOf" function

Added "asyncForOf" function

--- index.coffee
+++ index.coffee
@@ -284,6 +284,26 @@
 @asyncForIn = asyncForIn

-
-
-
+###
+  @asyncForOf
+###
+
+asyncForOf = (object, forEachFn = null)->
+
+  array = Object.keys object
+
+  it = new AsyncIterator
+
+  it.generateWith (expectedIndex)->
+    if expectedIndex < array.length then [ array[expectedIndex], object[array[expectedIndex]] ] else null
+
+  if forEachFn
+    it.forEach forEachFn
+
+  return it
+
+@asyncForOf = asyncForOf
+
+
+
+


Fix getActiveEditor issue

Fix getActiveEditor issue

--- index.coffee
+++ index.coffee
@@ -31,8 +31,8 @@
   activate: ->
     atom.commands.add 'atom-workspace',
       'pretty-json:prettify': ->
-        editor = atom.workspace.getActiveEditor()
+        editor = atom.workspace.getActiveTextEditor()
         prettify(editor)
       'pretty-json:sort-and-prettify': ->
-        editor = atom.workspace.getActiveEditor()
+        editor = atom.workspace.getActiveTextEditor()
         prettify(editor, true)


implemented saveMirror function for creating new mirrors and updating existing ones

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- coffee/controllers/main.coffee
+++ coffee/controllers/main.coffee
@@ -132,6 +132,19 @@
         }
       $scope.mirrors.unshift(nu)
       nu
+
+    $scope.saveMirror = (mirror)->
+      if(mirror.id is undefined)
+        Restangular.one('mirrors').post('',mirror).
+          then (n) ->
+            mirror.id = n.id
+            mirror.saved = true
+          , fetchError('add mirror')
+      else
+        Restangular.one('mirrors', mirror.id).patch(mirror).
+          then (n) ->
+            mirror.saved = true
+          , fetchError('update mirror')

     $scope.fetchNode = (id)->
       Restangular.one('node', id).get().


add updateProfile to user records interface

--- app/records-interfaces/user-records-interface.coffee
+++ app/records-interfaces/user-records-interface.coffee
@@ -13,3 +13,6 @@

     resetPassword: (params) ->
       @remote.post('reset_password', params)
+
+    updateProfile: (params) ->
+      @remote.post('update_profile', user: params)


s/window./@.

s/window./@.

--- app/assets/javascripts/opinionated/admin.js.coffee
+++ app/assets/javascripts/opinionated/admin.js.coffee
@@ -8,7 +8,7 @@

 # for more details see: http://emberjs.com/guides/application/

-window.Opinionated = Ember.Application.create({
+@Opinionated = Ember.Application.create({
   # LOG_TRANSITIONS_INTERNAL: true,
   # LOG_VIEW_LOOKUPS: true
 })


Matching the style.

Matching the style.

--- lib/documents/person.coffee
+++ lib/documents/person.coffee
@@ -61,8 +61,8 @@
       updatedAt: UpdatedAtTrigger ['user', 'givenName', 'familyName', 'publications._id']
       lastActivity: LastActivityTrigger ['library._id']
       personLastActivity: RelatedLastActivityTrigger Person, ['invited.by._id'], (doc, oldDoc) ->
-        oldInvited = _.pluck _.pluck(oldDoc?.invited, 'by'), '_id'
-        newInvited = _.pluck _.pluck(doc?.invited, 'by'), '_id'
+        newInvited = (invited.by._id for invited in doc.invited or [])
+        oldInvited = (invited.by._id for invited in oldDoc.invited or [])
         _.difference newInvited, oldInvited
       # TODO: For now we are updating last activity of all publications in a library, but we might consider removing this and leave it to the "trending" view
       publicationsLastActivity: RelatedLastActivityTrigger Publication, ['library._id'], (doc, oldDoc) ->


Fix ubret_tool for new calling style

Fix ubret_tool for new calling style

--- app/views/ubret_tool.coffee
+++ app/views/ubret_tool.coffee
@@ -46,6 +46,7 @@
     return keys

   selectElements: (ids) =>
+    console.log 'here'
     @model.setElements ids

   selectKey: (key) =>
@@ -55,6 +56,7 @@
     @model.tool.selectKeys(@model.get('selectedKey').slice()).start()

   toolSelectElements: =>
+    console.log 'tool-select'
     @model.tool.selectIds(@model.get('selectedElements').slice()).start()

   toolAddFilters: =>


these silly things

these silly things

--- apps/auction_lots/test/templates/artwork-meta.coffee
+++ apps/auction_lots/test/templates/artwork-meta.coffee
@@ -26,16 +26,3 @@
       @html.should.containEql "<meta property=\"og:url\" content=\"http://localhost:5000/artwork/#{@artwork.get('id')}"
       @html.should.containEql "<meta property=\"og:description\" content=\"Related auction results for From Gagosian Gallery, Andy Warhol, Skull (1999), Watercolor on Paper, 10 × 20 × 30in"
       @html.should.containEql "<meta property=\"og:title\" content=\"Andy Warhol, Skull (1999) | Related Auction Results | Artsy"
-
-  describe 'with an image', ->
-
-    beforeEach ->
-      @artwork = new Artwork fabricate 'artwork'
-      @artwork.set image_versions: ["large"]
-      @html = jade.render fs.readFileSync(@file).toString(),
-        artwork: @artwork
-        sd: @sd
-
-    it 'includes og:image and twitter card', ->
-      @html.should.containEql "<meta property=\"og:image\" content=\"http://localhost:5000/artwork/#{@artwork.get('id')}.jpg"
-      @html.should.containEql "<meta property=\"twitter:card\" content=\"summary_large_image"


tweak image editing tools

tweak image editing tools

--- app/assets/javascripts/storytime/wysiwyg.js.coffee
+++ app/assets/javascripts/storytime/wysiwyg.js.coffee
@@ -28,13 +28,13 @@
     "drop-font-tags": true,
     "tidy-mark": false

-  editorDiv = "
" +
+  editorDiv = "
" +
                 "
" +
                   "
" +
-                    "
" +
+                    "
" +
                       "<input type='text' id='medium-image-width' placeholder='Width' class='form-control' />" +
                     "
" +
-                    "
" +
+                    "
" +
                       "<input type='text' id='medium-image-height' placeholder='Height' class='form-control' />" +
                     "
" +
                   "
" +
@@ -74,16 +74,14 @@
         $(".medium-active-image").css("width", "")
         $("#medium-image-height").val("")
       else
-        newWidth = parseFloat($(this).val())
-        $(".medium-active-image").css("width", newWidth)
+        $(".medium-active-image").css("width", $(this).val())
         $(".medium-active-image").css("height", "")
         $("#medium-image-height").val($(".medium-active-image").css("height"))

     $('body').on "keyup", "#medium-image-height", () ->
       # set the height, then find the new width and set that and remove the height so image remains responsive
-      newHeight = parseFloat($(this).val())
       $(".medium-active-image").css("width", "")
-      $(".medium-active-image").css("height", newHeight)
+      $(".medium-active-image").css("height", $(this).val())
       newWidth = $(".medium-active-image").css("width")
       $("#medium-image-width").val(newWidth)
       $(".medium-active-image").css("width", newWidth)


Fixed bug where messages received from faye would not properly recognize the topic.

--- app/assets/javascripts/views/clouds/chat.js.coffee
+++ app/assets/javascripts/views/clouds/chat.js.coffee
@@ -38,7 +38,8 @@

     nfc.on "#{@model.type}s:#{@model.id}:chat:messages", (payload) =>
       if payload.client_id != session.get('client_id')
-        payload.topic = @model
+        payload.topic_id = @model.id
+        payload.topic_type = @model.type
         message = new Cloudsdale.Models.Message(payload)

         @appendMessage(message)


Fixed multiple events for same event.

Fixed multiple events for same event.

--- client/list.coffee
+++ client/list.coffee
@@ -33,6 +33,7 @@
     templateEvent = "click .select-#{data.single.dasherize()}"
     if not DMUtils.find(Template._listRow._events, 'selector', ".select-#{data.single.dasherize()}")
       _listRowEvents[templateEvent] = (event,template) ->
+        event.stopImmediatePropagation()
         data = template.data
         single = data.context.single
         sessionVar = "selected-#{single}"
@@ -46,6 +47,7 @@
     templateEvent = "click .enable-#{data.single.dasherize()}"
     if not DMUtils.find(Template._listRow._events, 'selector', ".enable-#{data.single.dasherize()}")
       _listRowEvents[templateEvent] = (event,template) ->
+        event.stopImmediatePropagation()
         data = template.data
         single = data.context.single
         Klass = data.context.klass
@@ -61,6 +63,7 @@
     templateEvent = "click .disable-#{data.single.dasherize()}"
     if not DMUtils.find(Template._listRow._events, 'selector', ".disable-#{data.single.dasherize()}")
       _listRowEvents[templateEvent] = (event,template) ->
+        event.stopImmediatePropagation()
         data = template.data
         single = data.context.single
         Klass = data.context.klass
@@ -86,6 +89,7 @@
     templateEvent = "click .edit-#{data.single.dasherize()}"
     if not DMUtils.find(Template._listRow._events, 'selector', ".edit-#{data.single.dasherize()}")
       _listRowEvents[templateEvent] = (event,template) ->
+        event.stopImmediatePropagation()
         data = template.data
         # console.log data
         if data.context.formType is 'link'
@@ -130,6 +134,7 @@
     templateEvent = "click .close-form-#{data.single.dasherize()}"
     if not DMUtils.find(Template._listRow._events, 'selector', ".close-form-#{data.single.dasherize()}")
       _listRowEvents[templateEvent] = (event,template) ->
+        event.stopImmediatePropagation()
         data = template.data
         single = data.context.single
         element = $("##{data._id}")
@@ -197,6 +202,7 @@
     templateEvent = "click .add-#{data.single.dasherize()}"
     if not DMUtils.find(Template.listNav._events, 'selector', ".add-#{data.single.dasherize()}")
       navEvents[templateEvent] = (event, template) ->
+        event.stopImmediatePropagation()
         context = template.data
         single = context.single
         klass = context.klass
@@ -220,6 +226,7 @@
     templateEvent = "click .purge-disabled-#{data.plural.dasherize()}"
     if not DMUtils.find(Template.listNav._events, 'selector', ".purge-disabled-#{data.plural.dasherize()}")
       navEvents[templateEvent] = (event, template) ->
+        event.stopImmediatePropagation()
         context = template.data
         plural = context.plural
         console.log "You have not defined a custom purge handler for #{data.title}.  Using the default."
@@ -246,6 +253,7 @@
     templateEvent = "click .reset"
     if not DMUtils.find(Template.listNav._events, 'selector', ".reset")
       navEvents[templateEvent] = (event, template) ->
+        event.stopImmediatePropagation()
         context = template.data
         single = context.single
         $(".#{single}-search").addClass('hide')
@@ -260,6 +268,7 @@
     templateEvent = "keyup .#{data.single.dasherize()}-search-query"
     if not DMUtils.find(Template.listNav._events, 'selector', ".#{data.single.dasherize()}-search-query")
       navEvents[templateEvent] = (event, template) ->
+        event.stopImmediatePropagation()
         context = template.data
         single = context.single
         value = event.target.value
@@ -423,6 +432,7 @@

   listFormEvents =
     'click .close-form': (event, template) ->
+      event.stopImmediatePropagation()
       event.preventDefault()
       data = template.data.data
       element = $("##{data._id}")
@@ -434,6 +444,7 @@
         return false

     'click .show-created-updated': (event, template) ->
+      event.stopImmediatePropagation()
       event.preventDefault()
       console.log 'Clicked show-created-updated'
       show = Session.get('showDateTime')


RequestStore: output all IIPs for a given group

--- components/RequestStore.coffee
+++ components/RequestStore.coffee
@@ -1,13 +1,14 @@
 noflo = require 'noflo'

 class RequestStore extends noflo.Component
-  description: ''
+  description: 'Store IIPs using their group and outputs them when'
   constructor: ->
     @inPorts =
       push: new noflo.Port 'object'
       pop: new noflo.Port 'bang'
     @outPorts =
       out: new noflo.Port 'object'
+      # TODO : add loadgroup/loadiip ports

     @iips = {}
     @groups = []
@@ -32,10 +33,12 @@
     @inPorts.pop.on 'data', () =>
       group = @groupsReq[@groupsReq.length - 1]
       return unless group?
-      iip = @iips[group].shift()
-      return unless iip?
-      return unless @outPorts.out.isAttached()
-      @outPorts.out.send iip
+      return unless @iips[group]?
+      if @outPorts.out.isAttached()
+        while @iips[group].length > 0
+          iip = @iips[group].shift()
+          @outPorts.out.send iip
+      delete @iips[group]
     @inPorts.pop.on 'endgroup', () =>
       @groupsReq.pop()
       return unless @outPorts.out.isAttached()


mp3 files render in an audio tag that has controls

--- lib/assets/javascripts/attachinary.js.coffee
+++ lib/assets/javascripts/attachinary.js.coffee
@@ -12,6 +12,8 @@

               <% if(files[i].resource_type == "raw") { %>

+              <% } else if (files[i].format == "mp3") { %>
+                <audio src="<%= $.cloudinary.url(files[i].public_id, { "version": files[i].version, "resource_type": 'video', "format": 'mp3'}) %>" controls />
               <% } else { %>
                 <img
                   src="<%= $.cloudinary.url(files[i].public_id, { "version": files[i].version, "format": 'jpg', "crop": 'fill', "width": 75, "height": 75 }) %>"


Fix bug : DatePicker errors on input from Chrome on Windows 8.1 (#7027)

closes #4965

--- bokehjs/src/coffee/models/widgets/date_picker.coffee
+++ bokehjs/src/coffee/models/widgets/date_picker.coffee
@@ -70,7 +70,7 @@
     return @

   _on_select: (date) =>
-    @model.value = date.toString()
+    @model.value = date.toISOString()
     @change_input()

 export class DatePicker extends InputWidget


Makes datacenter deletion work.

Makes datacenter deletion work.

--- admin/static/coffee/clusterview.coffee
+++ admin/static/coffee/clusterview.coffee
@@ -443,38 +443,25 @@
             log_initial '(initializing) modal dialog: remove datacenter'
             super @template

-        render : (datacenters_to_delete) ->
-            log_render '(rendering) remove datacenter dialog'
-
+        render: (datacenters_to_delete) ->
+            log_render '(rendering) remove datacenters dialog'
             validator_options =
                 submitHandler: =>
-                    url = '/ajax/datacenters?ids='
-                    num_datacenters = datacenters_to_delete.length
                     for datacenter in datacenters_to_delete
-                        url += datacenter.id
-                        url += "," if num_datacenters-=1 > 0
-
-                    url += '&token=' + token
-
-                    # TODO: This action causes a bunch of machines to
-                    # reference a datacenter that no longer exists,
-                    # and javascript elsewhere can't handle this
-                    # inconsistent data.  Generally speaking, all our
-                    # javascript needs to be able to handle
-                    # inconsistent data.
-
-                    $('form', @$modal).ajaxSubmit
-                        url: url
-                        type: 'DELETE'
-
-                        success: (response) =>
-                            clear_modals()
-
-                            # Parse the response JSON, apply appropriate diffs, and show an alert
-                            apply_diffs(response)
-                            #TODO hook this back up
-                            #for datacenter in response_json.op_result
-                                #$('#user-alert-space').append @alert_tmpl datacenter
+                        $.ajax
+                            url: "/ajax/datacenters/#{datacenter.id}"
+                            type: 'DELETE'
+                            contentType: 'application/json'
+
+                            success: (response) =>
+                                clear_modals()
+
+                                if (response)
+                                    throw "Received a non null response to a delete... this is incorrect"
+                                datacenters.remove(datacenter.id)
+                                #TODO hook this up
+                                #for namespace in response_json.op_result
+                                    #$('#user-alert-space').append @alert_tmpl namespace

             array_for_template = _.map datacenters_to_delete, (datacenter) -> datacenter.toJSON()
             super validator_options, { 'datacenters': array_for_template }


[pm] unify messaging interface

[pm] unify messaging interface

--- client/app/js/views/private_messages_view.coffee
+++ client/app/js/views/private_messages_view.coffee
@@ -62,6 +62,10 @@
     @scrollToBottom($popup)
     @repositionPopups()

+  addMessage: ($popup, message) =>
+    $messages = $popup.find('.popup_messages')
+    $messages.append(message)
+
   # todo: make this and receiveMessage construct messages from a common source
   addLogMessages: ($popup, log) =>
     messageHtml = ""
@@ -72,26 +76,25 @@
       else
         messageHtml += "
#{username}: #{message}
"

-    $messages = $popup.find('.popup_messages').append($(messageHtml))
+    @addMessage($popup, messageHtml)
     @scrollToBottom($popup)

   # todo: make this and addLogMessages construct messages from a common source
   receiveMessage: (messageModel, messageId, username, message, options) =>
     $popup = @$findOrCreatePopup(messageId)
-    $messages = $popup.find('.popup_messages')
     wasAtBottom = @isAtBottom($popup)
     username = "Me"  if username == PokeBattle.username
     if options.type == 'error'
-      $messages.append("
#{message}
")
+      @addMessage($popup, "
#{message}
")
     else if options.type == 'alert'
-      $messages.append("
#{message}
")
+      @addMessage($popup, "
#{message}
")
     else
       if username != "Me" && !$popup.find('.chat_input').is(":focus")
         $popup.addClass('new_message')
         PokeBattle.notifyUser(PokeBattle.NotificationTypes.PRIVATE_MESSAGE, username)
       else
         @resetNotifications(messageModel)
-      $messages.append("
#{username}: #{message}
")
+      @addMessage($popup, "
#{username}: #{message}
")
     if wasAtBottom then @scrollToBottom($popup)

   openChallenge: (messageId, generation, conditions) =>


Fetch folders and labels at the same time

--- internal_packages/worker-sync/lib/nylas-sync-worker.coffee
+++ internal_packages/worker-sync/lib/nylas-sync-worker.coffee
@@ -123,7 +123,8 @@
     needed = [
       {model: 'threads'},
       {model: 'messages', maxFetchCount: 5000}
-      {model: @_account.categoryCollection(), initialPageSize: 1000}
+      {model: 'folders', initialPageSize: 1000}
+      {model: 'labels', initialPageSize: 1000}
       {model: 'drafts'},
       {model: 'contacts'},
       {model: 'calendars'},


Actually use error messages

Actually use error messages

--- src/natives/classes/java.net.PlainSocketImpl.coffee
+++ src/natives/classes/java.net.PlainSocketImpl.coffee
@@ -43,7 +43,7 @@

       error_cb = (msg) -> ->
         clear_state()
-        except_cb -> rs.java_throw rs.get_bs_class('Ljava/io/IOException;'), 'Connection failed!'
+        except_cb -> rs.java_throw rs.get_bs_class('Ljava/io/IOException;'), msg

       # Success case
       _this.$ws.on('open', ->
@@ -52,8 +52,8 @@
         resume_cb())

       # Error cases
-      _this.$ws.on('close', error_cb('Connection failed!'))
-      _this.$ws.on('error', error_cb('Connection failed!'))
+      _this.$ws.on('close', error_cb('Connection failed! (Closed)'))
+      _this.$ws.on('error', error_cb('Connection failed! (Error)'))

       # Timeout case
       id = window.setTimeout(error_cb('Connection timeout!'), timeout) if timeout > 0


fixed code style on servers.lib.server.subscriptions.coffee

--- servers/lib/server/subscriptions.coffee
+++ servers/lib/server/subscriptions.coffee
@@ -1,32 +1,32 @@
 { get } = require (
-  "../../../workers/social/lib/social/models/socialapi/requests.coffee"
+  '../../../workers/social/lib/social/models/socialapi/requests.coffee'
 )

 module.exports = (req, res) ->
-  errMsg = (msg)->
+  errMsg = (msg) ->
     {
-      "description" : msg
-      "error"       : "bad_request"
+      'description' : msg
+      'error'       : 'bad_request'
     }

-  {account_id, kloud_key} = req.query
+  { account_id, kloud_key } = req.query

   unless account_id
-    return res.status(400).send errMsg "account_id is required"
+    return res.status(400).send errMsg 'account_id is required'

   unless kloud_key
-    return res.status(401).send errMsg "kloud_key is required"
+    return res.status(401).send errMsg 'kloud_key is required'

   # Hardcoding is wrong, however this key won't change
   # depending on environments, so there's point of
   # putting it in config : SA
-  unless kloud_key is "R1PVxSPvjvDSWdlPRVqRv8IdwXZB"
-    return res.status(401).send errMsg "kloud_key is wrong"
+  unless kloud_key is 'R1PVxSPvjvDSWdlPRVqRv8IdwXZB'
+    return res.status(401).send errMsg 'kloud_key is wrong'

   url  = "/payments/subscriptions/#{account_id}"
-  url += "?default=false"
+  url += '?default=false'

-  get url, {}, (err, response)->
+  get url, {}, (err, response) ->
     return res.status(400).send err  if err

     res.status(200).send response


Proxy message link opened within the iframe, should open in new window

--- js-library/app/js/jail_iframe/classes/proxy_message.coffee
+++ js-library/app/js/jail_iframe/classes/proxy_message.coffee
@@ -3,7 +3,7 @@

       ×

-      You're looking at this page through Factlink

+      You're looking at this page through Factlink

       (visit original page)

   """


Fix emptying fetch pool on mentions page

--- assets/javascripts/views/fetch_posts_pool.js.coffee
+++ assets/javascripts/views/fetch_posts_pool.js.coffee
@@ -49,7 +49,7 @@

     # Update Mentions Profile Cursor / Unread Badge
     if posts_feed_view.constructor.view_name == 'mentions_posts_feed'
-      posts_feed_view.updateProfileCursor(@posts_collection)
+      posts_feed_view.updateProfileCursor?(@posts_collection)

     @render()


Sorting by client.

Sorting by client.

--- lib/sensu-dashboard/assets/javascripts/collections/events.coffee
+++ lib/sensu-dashboard/assets/javascripts/collections/events.coffee
@@ -5,7 +5,7 @@
     url: "/events"

     comparator: (event) ->
-      event.get("status_name")
+      event.get("client")

     getSelected: ->
       @where(selected: true)


Added mountain -> glacier completion link

--- app/views/play/level/modal/HeroVictoryModal.coffee
+++ app/views/play/level/modal/HeroVictoryModal.coffee
@@ -397,7 +397,7 @@
       AudioPlayer.playSound name, 1

   getNextLevelCampaign: ->
-    {'kithgard-gates': 'forest', 'kithgard-mastery': 'forest', 'siege-of-stonehold': 'desert', 'clash-of-clones': 'mountain'}[@level.get('slug')] or @level.get 'campaign'  # Much easier to just keep this updated than to dynamically figure it out.
+    {'kithgard-gates': 'forest', 'kithgard-mastery': 'forest', 'siege-of-stonehold': 'desert', 'clash-of-clones': 'mountain', 'summits-gate': 'glacier'}[@level.get('slug')] or @level.get 'campaign'  # Much easier to just keep this updated than to dynamically figure it out.

   getNextLevelLink: (returnToCourse=false) ->
     if @level.get('type', true) is 'course' and nextLevel = @level.get('nextLevel') and not returnToCourse


clean this up a bit

clean this up a bit

--- src/enc.iced
+++ src/enc.iced
@@ -192,25 +192,29 @@
   #   the ciphertext, depending on the `output_iv` option.
   run_salsa20 : ({ input, key, iv, output_iv, progress_hook }, cb) ->
     await @_check_scrubbed key, "Salsa20", cb, defer()
-    riv = null
+
+    args = { input, progress_hook }

     # In the newer versions of TripleSec, we fix the fact that our
     # inputs to Xsalsa20 were reversed endianwise.  It wasn't a security
     # bug, it was just wrong.  This fixes it going forward.  We might
     # want a slightly cleaner fix by the way...
-    siv = if @version.fix_xsalsa20_rev
-      key.endian_reverse()
-      riv = iv.clone().endian_reverse()
-      riv
-    else iv
-
-    await salsa20.bulk_encrypt { input, key, iv : siv, progress_hook}, defer ct
+    if @version.fix_xsalsa20_rev
+      args.key = key.clone().endian_reverse()
+      args.iv = iv.clone().endian_reverse()
+    else
+      args.key = key
+      args.iv = iv
+
+    await salsa20.bulk_encrypt args, defer ct

     # Despite the reversals, always output the original IV.
     ct = iv.clone().concat(ct) if output_iv

-    # Scrub the reversed IV since we'll never use it again.
-    riv.scrub() if riv?
+    # Scrub the reversed IV & key since we'll never use it again.
+    if @version.fix_xsalsa20_rev
+      args.key.scrub()
+      args.iv.scrub()

     cb null, ct


send message to window.dataLayer, tweak turbolinks callbacks

--- app/cells/folio/cookie_consent/cookie_consent.coffee
+++ app/cells/folio/cookie_consent/cookie_consent.coffee
@@ -1,22 +1,39 @@
 #= require cookieconsent

 if window.folioCookieConsentConfiguration
-  # TODO
-  # if window.dataLayer
-  #   window.dataLayer.push()
+  bindTurbolinks = Turbolinks?

-  window.folioCookieConsent = initCookieConsent()
-  window.folioCookieConsent.run(window.folioCookieConsentConfiguration)
+  if bindTurbolinks
+    detached = null

-  detached = null
-
-  $(document)
-    .on 'turbolinks:load', ->
+    onLoad = ->
       if detached
         $('body').append(detached)
         detached = null

-    .on 'turbolinks:before-render', ->
+    onBeforeRender = ->
       $main = $('#cc--main')
       return unless $main.length
       detached = $main.detach()
+
+  onAccept = (cookie) ->
+    if bindTurbolinks
+      bindTurbolinks = false
+
+      $(document)
+        .off 'turbolinks:load', onLoad
+        .off 'turbolinks:before-render', onBeforeRender
+
+    if window.dataLayer
+      window.dataLayer.push
+        event: 'cookieConsent'
+        level: cookie.level
+
+  window.folioCookieConsent = initCookieConsent()
+
+  window.folioCookieConsent.run(Object.assign({}, window.folioCookieConsentConfiguration, { onAccept: onAccept }))
+
+  if bindTurbolinks
+    $(document)
+      .on 'turbolinks:load', onLoad
+      .on 'turbolinks:before-render', onBeforeRender


Remove comments

Remove comments

--- packages/rocketchat-statistics/server/models/MRStatistics.coffee
+++ packages/rocketchat-statistics/server/models/MRStatistics.coffee
@@ -8,47 +8,3 @@
 			_id: _id

 		return @findOne query, options
-
-	# # FIND
-	# findByMention: (username, options) ->
-	# 	query =
-	# 		"mentions.username": username
-
-	# 	return @find query, options
-
-
-	# # UPDATE
-	# setHiddenById: (_id, hidden=true) ->
-	# 	query =
-	# 		_id: _id
-
-	# 	update =
-	# 		$set:
-	# 			_hidden: hidden
-
-	# 	return @update query, update
-
-
-	# # INSERT
-	# createWithTypeRoomIdMessageAndUser: (type, roomId, message, user, extraData) ->
-	# 	record =
-	# 		t: type
-	# 		rid: roomId
-	# 		ts: new Date
-	# 		msg: message
-	# 		u:
-	# 			_id: user._id
-	# 			username: user.username
-
-	# 	_.extend record, extraData
-
-	# 	record._id = @insert record
-	# 	return record
-
-
-	# # REMOVE
-	# removeById: (_id) ->
-	# 	query =
-	# 		_id: _id
-
-	# 	return @remove query


No reason to set xsrf on get authorize

No reason to set xsrf on get authorize

It gets overwritten by get sign_in or get confirm

--- src/server/routes/get/authorize.coffee
+++ src/server/routes/get/authorize.coffee
@@ -32,7 +32,6 @@
 				type = "invalid_request"
 				return next new oauth2.AuthorizationError message, type
 			# TODO Add oauth2 authorization scope parameter
-			res.cookie "xsrf", uuid.v4()
 			res.redirect url
 		auth.errorHandler mode: "indirect"
 	]


Update open-weather-map.coffee

Update open-weather-map.coffee

--- src/open-weather-map.coffee
+++ src/open-weather-map.coffee
@@ -7,6 +7,7 @@
 # Configuration:
 #   HUBOT_OPEN_WEATHER_MAP_APIKEY = required API key from http://openweathermap.org/faq#error401
 #   HUBOT_OPEN_WEATHER_MAP_URL (optional)
+#   HUBOT_OPEN_WEATHER_MAP_FORECAST_URL (optional)
 #   HUBOT_OPEN_WEATHER_MAP_DEFAULT_CITIES (optional)
 #   HUBOT_OPEN_WEATHER_MAP_UNITS (optional) imperial, metric
 #
@@ -22,7 +23,7 @@
 #   github.com/tan3

 weatherURL = process.env.HUBOT_OPEN_WEATHER_MAP_URL or 'http://api.openweathermap.org/data/2.5/weather?q='
-forecastURL = 'http://api.openweathermap.org/data/2.5/forecast?q='
+forecastURL = process.env.HUBOT_OPEN_WEATHER_MAP_FORECAST_URL or 'http://api.openweathermap.org/data/2.5/forecast?q='
 cities     = process.env.HUBOT_OPEN_WEATHER_MAP_DEFAULT_CITIES
 apiKey     = process.env.HUBOT_OPEN_WEATHER_MAP_APIKEY
 units      = process.env.HUBOT_OPEN_WEATHER_MAP_UNITS or 'imperial'


Enable player name change

Enable player name change

--- coffees/src/controllers/player_controller.coffee
+++ coffees/src/controllers/player_controller.coffee
@@ -43,4 +43,13 @@
     request.app.get('redisClient').publish(request.player.LeagueId, JSON.stringify(data))
     response.status(204).json("")

+  router.put '/players/:id', (request, response) ->
+    request.player.set("name", request.body["name"]);
+    request.player.save()
+    data =
+      event: "playerUpdate"
+      player: request.player.toJSON()
+    request.app.get('redisClient').publish(request.player.LeagueId, JSON.stringify(data))
+    response.status(200).json("")
+
   router


selection_manager.coffee: use .set instead of .save

.save doesn't work anymore; I'm not sure why it was using that instead
of set, and I'm worried this breaks something as a result, but save()
definitely doesn't work anymore.

--- bokehjs/src/coffee/common/selection_manager.coffee
+++ bokehjs/src/coffee/common/selection_manager.coffee
@@ -64,8 +64,8 @@
     return @selectors[rview.model.id]

   _save_indices: (indices) ->
-    @get('source').save({
+    @get('source').set({
       "selected": indices
-    }, {patch: true})
+    })

 module.exports = SelectionManager


Direct render of imageviews

Direct render of imageviews

--- app/assets/javascripts/cartilage/views/image_view.js.coffee
+++ app/assets/javascripts/cartilage/views/image_view.js.coffee
@@ -46,11 +46,16 @@
     # Initialize the View
     super(options)

+  render: ->
+    ($ @el).html @_imageElement
+
     # Bind to Events manually because event delegation will not work for
     # image load and error events...
     ($ @imageElement).load @handleLoadEvent
     ($ @imageElement).error @handleErrorEvent

+    @
+
   cleanup: ->
     @clear { silent: true }
     super()


fix upper resize

fix upper resize

--- app/assets/javascripts/components/image-subject-viewer.cjsx
+++ app/assets/javascripts/components/image-subject-viewer.cjsx
@@ -218,10 +218,10 @@

     currentMark = @state.selectedMark

-    dy = currentMark.yLower - y # NOTE: reverse sign for upper resize
+    dy = currentMark.yUpper - y # NOTE: reverse sign for upper resize
     yUpper_p = y
     markHeight_p = currentMark.yLower - currentMark.yUpper + dy
-    y_p = yUpper_p - markHeight_p/2
+    y_p = yUpper_p + markHeight_p/2

     currentMark.yUpper = yUpper_p
     currentMark.markHeight = markHeight_p


Fix missing category filter in normal map view.

--- app/assets/javascripts/controllers/map.controller.js.coffee
+++ app/assets/javascripts/controllers/map.controller.js.coffee
@@ -1,8 +1,6 @@
 Wheelmap.MapSessionControllerMixin = Ember.Mixin.create
   needs: ['toolbar', 'map']
-  queryParams: [
-    'status', 'categories', 'q', 'bbox', 'lat', 'lon', 'zoom', 'toilet', 'show_categories'
-  ]
+  queryParams: ['status', 'categories', 'q', 'bbox', 'lat', 'lon', 'zoom', 'toilet', 'show_categories']

   latBinding: 'controllers.map.lat'
   lonBinding: 'controllers.map.lon'
@@ -141,7 +139,11 @@
   ).observes('bbox')

   queryShowChangesDidChange: (->
-    @set('controllers.toolbar.showCategories', !!parseInt(@get('show_categories'), 10));
+    showCategories = @get('show_categories');
+
+    showCategories = unless showCategories? then true else !!parseInt(showCategories)
+
+    @set('controllers.toolbar.showCategories', showCategories, 10)
   ).observes('show_categories')

   actions:


ADD: set video duration

ADD: set video duration

Finding input field with class name '.js-uploader-duration' and set duration

--- app/assets/javascripts/media_lib/inserter_factory.js.coffee
+++ app/assets/javascripts/media_lib/inserter_factory.js.coffee
@@ -24,7 +24,7 @@
       when 'tinymce'
         switch @uploader.site
           when 'rusnovosti.ru'
-            new MediaLib.RusnovostiTinyMCE3Inserter @uploader, @model
+            new MediaLib.RusnovostiTinyMCE3Inserter @uploader, @model
           else
             new MediaLib.TinyMCE3Inserter @uploader, @model
       when 'same_image'
@@ -59,6 +59,7 @@
 # Стратегия вставки видео-модели
 class MediaLib.VideoInserter extends MediaLib.BaseInserter
   insert: ($uploader) ->
+    $uploader.find('.js-uploader-duration').val @model.duration
     $uploader.find('.js-uploader-input').val @model.static_name
     $uploader.find('.js-uploader-input').trigger 'change'


pricing: use dictionary instead of an empty object

--- client/Pricing/views/plans.coffee
+++ client/Pricing/views/plans.coffee
@@ -1,6 +1,6 @@
 class PricingPlansView extends KDView

-  initialState: {}
+  initialState: KD.utils.dict()

   constructor: (options = {}, data) ->


:fire: Remove old message registry

:fire: Remove old message registry

--- lib/message-registry.coffee
+++ lib/message-registry.coffee
@@ -1,78 +0,0 @@
-{Emitter, TextEditor, CompositeDisposable} = require('atom')
-validate = require('./validate')
-helpers = require('./helpers')
-
-class MessageRegistry
-  constructor: ->
-    @updated = false
-    @publicMessages = []
-    @subscriptions = new CompositeDisposable()
-    @emitter = new Emitter
-    @linterResponses = new Map()
-    @editorMessages = new Map()
-
-    @subscriptions.add(@emitter)
-    @subscriptions.add atom.config.observe('linter.ignoredMessageTypes', (ignoredMessageTypes) =>
-      @ignoredMessageTypes = ignoredMessageTypes
-    )
-    @shouldUpdatePublic = true
-    helpers.requestUpdateFrame => @updatePublic()
-
-  set: ({linter, messages, editor}) ->
-    try validate.messages(messages) catch e then return helpers.error(e)
-    messages = messages.filter((entry) => @ignoredMessageTypes.indexOf(entry.type) is -1)
-    if linter.scope is 'project'
-      @linterResponses.set(linter, messages)
-    else
-      return unless editor.alive
-      throw new Error("Given editor isn't really an editor") unless editor instanceof TextEditor
-      if not @editorMessages.has(editor) then @editorMessages.set(editor, new Map())
-      @editorMessages.get(editor).set(linter, messages)
-    @updated = true
-
-  updatePublic: ->
-    return unless @shouldUpdatePublic
-    if @updated
-      @updated = false
-
-      publicMessages = []
-      added = []
-      removed = []
-
-      @linterResponses.forEach (messages) -> publicMessages = publicMessages.concat(messages)
-      @editorMessages.forEach (linters) -> linters.forEach (messages) ->
-        publicMessages = publicMessages.concat(messages)
-
-      currentKeys = publicMessages.map (i) -> i.key
-      lastKeys = publicMessages.map (i) -> i.key
-
-      publicMessages.forEach (i) ->
-        added.push(i) if lastKeys.indexOf(i) is -1
-      @publicMessages.forEach (i) ->
-        removed.push(i) if currentKeys.indexOf(i) is -1
-
-      @publicMessages = publicMessages
-      @emitter.emit 'did-update-messages', {added, removed, messages: publicMessages}
-
-    helpers.requestUpdateFrame => @updatePublic()
-
-  onDidUpdateMessages: (callback) ->
-    return @emitter.on('did-update-messages', callback)
-
-  deleteMessages: (linter) ->
-    if @linterResponses.has(linter)
-      @updated = true
-      @linterResponses.delete(linter)
-
-  deleteEditorMessages: (editor) ->
-    if @editorMessages.has(editor)
-      @updated = true
-      @editorMessages.delete(editor)
-
-  deactivate: ->
-    @shouldUpdatePublic = false
-    @subscriptions.dispose()
-    @linterResponses.clear()
-    @editorMessages.clear()
-
-module.exports = MessageRegistry


:bug: Fix bad plugin name in registration

--- lib/minimap-git-diff.coffee
+++ lib/minimap-git-diff.coffee
@@ -11,10 +11,11 @@
     @subscriptions = new CompositeDisposable

   isActive: -> @pluginActive
+
   activate: ->

   consumeMinimapServiceV1: (@minimap) ->
-    @minimap.registerPlugin 'selection', this
+    @minimap.registerPlugin 'git-diff', this

   deactivate: ->
     binding.destroy() for id,binding of @bindings


Removed: useless event bindings

Removed: useless event bindings

--- client/app/views/task_view.coffee
+++ client/app/views/task_view.coffee
@@ -15,8 +15,6 @@
         "click .task-infos a": "onListLinkClicked"
         "dragstart": "onDragStart"
         "dragover": "onDragOver"
-        "dragenter": "onDragEnter"
-        "dragleave": "onDragLeave"
         "drop": "onDrop"
         "dragend": "onDragEnd"
         "hover": "onMouseOver"
@@ -79,8 +77,6 @@
         event.originalEvent.dataTransfer.effectAllowed = 'all'
         @list.draggedItem = @

-
-
     onDragOver: (event) ->
         if event.preventDefault
             event.preventDefault()
@@ -102,14 +98,6 @@
             $(@list.$el.children()[index + 1]).css('visibility', 'visible')

         return false
-
-    onDragEnter: (event) ->
-
-        # nothing
-
-
-    onDragLeave: (event) ->
-        # nothing

     onDrop: (event) ->
         if event.stopPropagation


Handle tree-view copy/paste and block weird move paths

--- lib/models/synced-fs.coffee
+++ lib/models/synced-fs.coffee
@@ -1,5 +1,6 @@
 ipc = require 'ipc'
 fs = require 'fs-plus'
+pathUtil = require 'path'
 _ = require 'underscore-plus'

 module.exports =
@@ -44,8 +45,10 @@
       console.log event.type
       switch event.type
         when 'core:confirm' then @onCoreConfirmDidDispatch(event)
+        when 'core:copy', 'core:cut' then @onCoreCopyOrCut(event)
         when 'tree-view:remove' then @onTreeViewRemove(event)
         when 'tree-view:toggle' then @addTreeViewListeners(event)
+        when 'tree-view:paste' then @onTreeViewPaste(event)

     atom.workspace.observeTextEditors (editor) =>
       editor.onDidSave => @onSave(editor)
@@ -60,7 +63,6 @@
       el.addEventListener 'dragend', (event) => @onTreeViewDragEnd(event)

   onSave: (editor) =>
-
     {project, buffer} = editor
     {file} = buffer
     inCodeDir = !!@formatPath(file.path).match(/\.atom\/code/)
@@ -68,7 +70,7 @@
     return unless inCodeDir

     @popupNoConnectionWarning() if @connectionState is 'closed'
-    @sendLocalEvent @localSave(editor.project, file, buffer)
+    @sendLocalEvent @localSave(project, file, buffer)

   onCoreConfirmWillDispatch: (event) =>
     return unless @willDispatchCommand
@@ -79,6 +81,9 @@
     @syncMoves()
     @syncDuplication()

+  onCoreCopyOrCut: (event) =>
+    @treeViewCopiedPath = null
+
   onResync: (event) ->
     path = @getPath(event.target)
     fs.removeSync(path)
@@ -87,8 +92,19 @@
   onTreeViewRemove: (event) =>
     @syncRemovals()

+  onTreeViewPaste: (event) =>
+    return unless @treeViewCopiedPath?
+    source = @treeViewCopiedPath
+    target = @pathAtWillDispatch
+    @sendLocalEvent @localDuplicate(source, target)
+
   onTreeViewWillDispatch: (event) =>
     {type, target} = event
+
+    if type is 'tree-view:copy' or type is 'tree-view:cut'
+      @treeViewCopiedPath = @getPath(target) || @getTreeViewSelectedPath()
+      return
+
     @willDispatchCommand = type
     @entriesAtWillDispatch = fs.listTreeSync(@projectPath)
     @pathAtWillDispatch = @getPath(target) || @getTreeViewSelectedPath()
@@ -145,9 +161,10 @@
     return unless @willDispatchCommand is 'tree-view:move'
     @purgeTreeViewEvent()

-    return unless source? and target?
-
-    @sendLocalEvent @localMove(source, target)
+    return unless source? and target? and target isnt "#{@projectPath}/"
+
+    if pathUtil.resolve(target).startsWith(@projectPath)
+      @sendLocalEvent @localMove(source, target)

   syncDuplication: =>
     source = @pathAtWillDispatch
@@ -156,9 +173,10 @@
     return unless @willDispatchCommand is 'tree-view:duplicate'
     @purgeTreeViewEvent()

-    return unless source? and target?
-
-    @sendLocalEvent @localDuplicate(source, target)
+    return unless source? and target? and target isnt "#{@projectPath}/"
+
+    if pathUtil.resolve(target).startsWith(@projectPath)
+      @sendLocalEvent @localDuplicate(source, target)

   sendLocalEvent: (payload) ->
     console.log payload
@@ -234,7 +252,7 @@
     dialog = document.querySelectorAll('.tree-view-dialog atom-text-editor.mini')[0]
     textContainer = dialog?.shadowRoot?.querySelector('.text.plain')

-    return null unless dialog? and textContainer?
+    return '' unless dialog? and textContainer?
     textContainer.innerText

   getTreeViewSelectedPath: ->


edit tool icon code again

edit tool icon code again

--- client/code/view/tool/tile.coffee
+++ client/code/view/tool/tile.coffee
@@ -24,25 +24,26 @@
   # :TODO: Horrible kludge to avoid tool manifest changes right now (we've done worse)
   monkeypatchIconManifest: (model) ->
     manifest = model.get 'manifest'
-    if manifest.displayName.indexOf('in your browser') > -1
+    n = manifest.displayName.toLowerCase()
+    if n.indexOf('in your browser') > -1
       manifest.icon = '/image/tool-icon-classic.png'
       manifest.color = '#6AAFD1'
-    else if manifest.displayName.indexOf('Code') == 0
+    else if n.indexOf('code') == 0
       manifest.icon = '/image/tool-icon-code.png'
       manifest.color = '#555'
-    else if manifest.displayName.indexOf('Twitter') > -1
+    else if n.indexOf('twitter') > -1 or n.indexOf('tweet') > -1
       manifest.icon = '/image/tool-icon-twitter.png'
       manifest.color = '#3cf'
-    else if manifest.displayName.indexOf('Upload') == 0
+    else if n.indexOf('upload') == 0
       manifest.icon = '/image/tool-icon-spreadsheet-upload.png'
       manifest.color = '#029745'
-    else if manifest.displayName.indexOf('Download') == 0
+    else if n.indexOf('download') == 0
       manifest.icon = '/image/tool-icon-spreadsheet-upload.png'
       manifest.color = '#029745'
-    else if manifest.displayName.indexOf('Test') == 0
+    else if n.indexOf('test') == 0
       manifest.icon = '/image/tool-icon-test.png'
       manifest.color = '#b0df18'
-    else if manifest.displayName.indexOf('table') > -1
+    else if n.indexOf('table') > -1
       manifest.icon = '/image/tool-icon-data-table.png'
       manifest.color = '#f6b730'
     model.set 'manifest', manifest


fixes #66 - only react to one click when installing a plugin/app

--- client/code/view/tool/tile.coffee
+++ client/code/view/tool/tile.coffee
@@ -13,12 +13,16 @@
     @$el.addClass @model.get('name')
     @

+  checkInstall: (e) ->
+    @install(e) unless @active
+
 class Cu.View.AppTile extends Cu.View.ToolTile
   events:
-    'click' : 'install'
+    'click' : 'checkInstall'

   install: (e) ->
     e.preventDefault()
+    @active = true
     @$el.addClass 'loading'

     @model.install (jqXHR, text) =>
@@ -37,15 +41,18 @@
           delete dataset.new
           window.app.navigate "/dataset/#{dataset.id}/settings", {trigger: true}
         error: (model, xhr, options) ->
+          @active = false
           @$el.removeClass 'loading'
           console.warn "Error saving dataset (xhr status: #{xhr.status} #{xhr.statusText})"

 class Cu.View.PluginTile extends Cu.View.ToolTile
   events:
-    'click' : 'install'
+    'click' : 'checkInstall'

   install: (e) ->
     e.preventDefault()
+    @active = true
+
     @$el.addClass 'loading'
     dataset = Cu.Model.Dataset.findOrCreate
       user: window.user.effective.shortName
@@ -57,5 +64,6 @@
           console.warn 'Error', err if err?
           window.app.navigate "/dataset/#{dataset.id}/view/#{view.id}", trigger: true
       error: (model, xhr, options) ->
+        @active = false
         @$el.removeClass 'loading'
         console.warn xhr


Add support for `.esdoc.json` and `.esdoc.js` (#757)

--- config.cson
+++ config.cson
@@ -1537,7 +1537,7 @@
 	ESDoc:
 		icon: "esdoc"
 		priority: 2
-		match: /^\.?esdoc\.json$/i
+		match: /^\.?esdoc\.js(on)?$/i
 		colour: "medium-red"

 	ESLint:


better exception console output

better exception console output

--- alight/utilits.coffee
+++ alight/utilits.coffee
@@ -100,7 +100,7 @@

 alight.exceptionHandler = (e, title, locals) ->
-    console.warn title, locals
+    console.warn title, (e.message || '') + '\n', locals
     err = if typeof(e) is 'string' then e else e.stack
     console.error err


When clickd to "all comments" it was jumping to top

--- client/Social/Activity/views/activityactions.coffee
+++ client/Social/Activity/views/activityactions.coffee
@@ -46,7 +46,7 @@
       tooltip     :
         title     : "Show all"
       click       : (event)=>
-        # event.preventDefault()
+        event.preventDefault()
         @getDelegate().emit "CommentCountClicked", @
     , activity


Fix network re-start on packets

Fix network re-start on packets

--- src/components/Graph.coffee
+++ src/components/Graph.coffee
@@ -127,11 +127,16 @@
       targetPortName = @isExportedInport port, name, portName
       continue if targetPortName is false
       @inPorts.add targetPortName, port
-      @inPorts[targetPortName].once 'connect', =>
+      @inPorts[targetPortName].on 'connect', =>
         # Start the network implicitly if we're starting to get data
         return if @starting
-        return if @isStarted()
-        @start ->
+        return if @network.isStarted()
+        if @network.startupDate
+          # Network was started, but did finish. Re-start simply
+          @network.setStarted true
+          return
+        # Network was never started, start properly
+        @setUp ->

     for portName, port of outPorts
       targetPortName = @isExportedOutport port, name, portName


activity: fix html encode issues of edit widget

--- client/Social/Activity/views/inputeditwidget.coffee
+++ client/Social/Activity/views/inputeditwidget.coffee
@@ -12,7 +12,7 @@
     data         = @getData()
     {body, link} = data

-    @input.setValue body
+    @input.setValue Encoder.htmlDecode body
     @embedBox.loadEmbed link.link_url  if link

     @addSubView @input


set width 500px for not given with property

--- client/app/lib/components/contentModal/index.coffee
+++ client/app/lib/components/contentModal/index.coffee
@@ -12,6 +12,7 @@
     @contentOptions = _.pick options, contentAttrs

     modalOptions.cssClass = 'ContentModal'
+    modalOptions.width or= 500

     super modalOptions, data


Don't pin when constrainToWindow is true

--- coffee/drop.coffee
+++ coffee/drop.coffee
@@ -120,7 +120,6 @@
       if @options.constrainToWindow isnt false
         constraints.push
           to: 'window'
-          pin: true
           attachment: 'together'
       else
         # To get 'out of bounds' classes


Support import public statements in grammar

fixes #144

--- grammars/language-idris.cson
+++ grammars/language-idris.cson
@@ -127,7 +127,7 @@
         ]
     }
     {
-      begin: '\\b(import)\\b'
+      begin: '\\b(import\\s+public|import)\\b'
       beginCaptures:
         1:
           name: 'keyword.other.idris'


Moved atom.loadPackage to a spec-only helper function

--- spec/bracket-matcher-spec.coffee
+++ spec/bracket-matcher-spec.coffee
@@ -5,7 +5,7 @@

   beforeEach ->
     rootView = new RootView(require.resolve('fixtures/sample.js'))
-    atom.loadPackage('bracket-matcher')
+    window.loadPackage('bracket-matcher')
     rootView.attachToDom()
     editor = rootView.getActiveEditor()
     editSession = editor.activeEditSession


Additional tests for quotes after escape sequences

--- spec/bracket-matcher-spec.coffee
+++ spec/bracket-matcher-spec.coffee
@@ -580,6 +580,41 @@
           editor.insertText '\''
           expect(buffer.lineForRow(0)).toBe "abc\'"

+      describe "when a backslash character is before the cursor", ->
+        it "does not automatically insert the closing quote", ->
+          editor.buffer.setText("\\")
+          editor.setCursorBufferPosition([0,1])
+          editor.insertText '"'
+          expect(buffer.lineForRow(0)).toBe "\\\""
+
+          editor.buffer.setText("\\")
+          editor.setCursorBufferPosition([0,1])
+          editor.insertText '\''
+          expect(buffer.lineForRow(0)).toBe "\\\'"
+
+      describe "when an escape sequence is before the cursor", ->
+        it "does not automatically insert the closing quote", ->
+          editor.buffer.setText("\"\\\"")
+          editor.setCursorBufferPosition([0,3])
+          editor.insertText '"'
+          expect(buffer.lineForRow(0)).toBe "\"\\\"\""
+
+          editor.buffer.setText("\"\\\'")
+          editor.setCursorBufferPosition([0,3])
+          editor.insertText '"'
+          expect(buffer.lineForRow(0)).toBe "\"\\\'\""
+          '\''
+
+          editor.buffer.setText("\'\\\"")
+          editor.setCursorBufferPosition([0,3])
+          editor.insertText '\''
+          expect(buffer.lineForRow(0)).toBe "\'\\\"\'"
+
+          editor.buffer.setText("\'\\\'")
+          editor.setCursorBufferPosition([0,3])
+          editor.insertText '\''
+          expect(buffer.lineForRow(0)).toBe "\'\\\'\'"
+
       describe "when a quote is before the cursor", ->
         it "does not automatically insert the closing quote", ->
           editor.buffer.setText("''")


Add case to describe `model.get('<association>')` is undefined when the association id is set to an empty string

--- spec/brainstem-model-spec.coffee
+++ spec/brainstem-model-spec.coffee
@@ -562,6 +562,7 @@

           it 'should not throw a Brainstem error', ->
             expect(-> timeEntry.get("project")).not.toThrow()
+            expect(timeEntry.get("project")).toBe(undefined)

       describe "HasMany associations", ->
         project = null


Add metadata

Add metadata

--- src/videojs.chromecastComponent.coffee
+++ src/videojs.chromecastComponent.coffee
@@ -75,7 +75,16 @@
     @apiSession = session
     @addClass "connected"

-    mediaInfo = new chrome.cast.media.MediaInfo(@player_.currentSrc(), "video/mp4")
+    mediaInfo = new chrome.cast.media.MediaInfo(@player_.currentSrc(), "video/mp4");
+
+    if @settings.metadata
+      mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
+      mediaInfo.metadata.title = @settings.metadata.title if @settings.metadata.title
+      mediaInfo.metadata.subtitle = @settings.metadata.subtitle if @settings.metadata.subtitle
+
+      if @player_.options_.poster
+        image = new chrome.cast.Image(@player_.options_.poster)
+        mediaInfo.metadata.images = [image]

     loadRequest = new chrome.cast.media.LoadRequest(mediaInfo)
     loadRequest.autoplay = true


change steps

change steps

--- mechanics/003-twilight-zone/simulator.coffee
+++ mechanics/003-twilight-zone/simulator.coffee
@@ -39,7 +39,7 @@
         cut = (val) -> val > 0 ? val : 0
         x3p = x3
         evolve = ->
-            [time, x, v] = step(time, x, v, 0.1)
+            [time, x, v] = step(time, x, v, 1)
             [x1, y1, x2, y2, x3, y3] = x
             [vx1, vy1, vx2, vy2, vx3, vy3] = v
             phase = [x1, y1, vx1, vy1, x2, y2, vx2, vy2, x3, y3, vx3, vy3]


Made fact body clickable

Made fact body clickable

--- core/app/backbone/views/facts/fact_base_view.coffee
+++ core/app/backbone/views/facts/fact_base_view.coffee
@@ -35,7 +35,7 @@
   bodyView: ->
     @_bodyView ?= new FactBodyView
       model: @model
-      clickable: @options.clickable_body
+      clickable: @options.clickable_body = true
       truncate: @options.truncate_body

 class FactBodyView extends Backbone.Marionette.ItemView


update the tests to work with packs

update the tests to work with packs

--- test/unit/coffee/MongoManager/MongoManagerTests.coffee
+++ test/unit/coffee/MongoManager/MongoManagerTests.coffee
@@ -163,12 +163,26 @@

 	describe "getDocUpdates", ->
 		beforeEach ->
-			@updates = ["mock-update"]
+			@results = [
+				{foo: "mock-update", v: 56},
+				{foo: "mock-update", v: 55},
+				{foo: "mock-update", v: 42},
+				{foo: "mock-update", v: 41}
+			]
+			@updates_between = [
+				{foo: "mock-update", v: 55},
+				{foo: "mock-update", v: 42}
+			]
+			@updates_after = [
+				{foo: "mock-update", v: 56},
+				{foo: "mock-update", v: 55},
+				{foo: "mock-update", v: 42}
+			]
 			@db.docHistory = {}
 			@db.docHistory.find = sinon.stub().returns @db.docHistory
 			@db.docHistory.sort = sinon.stub().returns @db.docHistory
 			@db.docHistory.limit = sinon.stub().returns @db.docHistory
-			@db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates)
+			@db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results)

 			@from = 42
 			@to   = 55
@@ -190,12 +204,12 @@
 					.calledWith("v": -1)
 					.should.equal true

-			it "should not limit the results", ->
-				@db.docHistory.limit
-					.called.should.equal false
-
-			it "should call the call back with the updates", ->
-				@callback.calledWith(null, @updates).should.equal true
+			#it "should not limit the results", ->
+			#	@db.docHistory.limit
+			#		.called.should.equal false
+
+			it "should call the call back with the results", ->
+				@callback.calledWith(null, @updates_between).should.equal true

 		describe "without a to version", ->
 			beforeEach ->
@@ -210,7 +224,7 @@
 					.should.equal true

 			it "should call the call back with the updates", ->
-				@callback.calledWith(null, @updates).should.equal true
+				@callback.calledWith(null, @updates_after).should.equal true

 		describe "with a limit", ->
 			beforeEach ->
@@ -224,14 +238,22 @@

 	describe "getDocUpdates", ->
 		beforeEach ->
-			@updates = ["mock-update"]
+			@results = [
+				{foo: "mock-update", v: 56, meta: {end_ts: 110}},
+				{foo: "mock-update", v: 55, meta: {end_ts: 100}},
+				{foo: "mock-update", v: 42, meta:{end_ts: 90}}
+			]
+			@updates = [
+				{foo: "mock-update", v: 55, meta: {end_ts: 100}},
+				{foo: "mock-update", v: 42, meta:{end_ts: 90}}
+			]
 			@db.docHistory = {}
 			@db.docHistory.find = sinon.stub().returns @db.docHistory
 			@db.docHistory.sort = sinon.stub().returns @db.docHistory
 			@db.docHistory.limit = sinon.stub().returns @db.docHistory
-			@db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates)
-
-			@before = Date.now()
+			@db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results)
+
+			@before = 101

 		describe "with a before timestamp", ->
 			beforeEach ->
@@ -269,7 +291,7 @@
 					.should.equal true

 			it "should call the call back with the updates", ->
-				@callback.calledWith(null, @updates).should.equal true
+				@callback.calledWith(null, @results).should.equal true

 		describe "with a limit", ->
 			beforeEach ->


fix a syntactic mistake

fix a syntactic mistake

--- library/nucleus/plumbs.coffee
+++ library/nucleus/plumbs.coffee
@@ -132,7 +132,7 @@
         assert _.isFunction(send?.json), noLibrary
         assert _.isObject(response or 0), terrible
         assert response.send = send, ack.toString()
-        assert try response.json = send.json, ack
+        assert response.json = send.json or 0, ack
         p = get: -> return @socket.parser.incoming
         req = _.isObject response.req or undefined
         Object.defineProperty "req", p unless req


disable old html editor for worksheets; disable use of rangy

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -1244,7 +1244,7 @@
             @focused_codemirror().scrollTo(@html_editor_scroll_info.left, @html_editor_scroll_info.top)
         @html_editor_restore_selection()
         if cmd == "ClassApplier"
-            rangy.createClassApplier(args[0], args[1]).applyToSelection()
+            rangy?.createClassApplier(args[0], args[1]).applyToSelection()
         else
             if cmd == "insertHTML"
                 # more solid and cross platform, e.g., insertHTML doesn't exist on IE
@@ -2270,6 +2270,8 @@
         opts = defaults opts,
             line : required
             cm   : required
+        # DISABLED!
+        return

         cm = opts.cm
         scroll_info = cm.getScrollInfo()
@@ -2522,52 +2524,6 @@
             save_to_output_soon()
             @sync()

-        ###
-        rangy_sel = undefined
-        save_rangy_sel = () =>
-            if not @_html_editor_with_focus?.is(div)
-                return
-            #console.log("save_selection")
-            if rangy_sel?
-                try
-                    rangy.removeMarkers(rangy_sel)
-                catch e
-                    #console.log("WARNING: ", e)
-                rangy_sel = undefined
-            try
-                rangy_sel = rangy.saveSelection()
-            catch e
-                #console.log("WARNING: ", e)
-                rangy_sel = undefined
-
-        restore_rangy_sel = () =>
-            #console.log("rangy restoring selection")
-            if rangy_sel? and @_html_editor_with_focus?.is(div)
-                for x in rangy_sel.rangeInfos
-                    for k in ['startMarkerId', 'MarkerId', 'endMarkerId']
-                        a = x[k]
-                        if a? and not div.find("##{a}").length > 0
-                            console.log("skipping -- missing a marker")
-                            rangy_sel = undefined
-                            div.find(".rangySelectionBoundary").remove()
-                            @html_editor_save_selection()
-                            save_rangy_sel()
-                            return
-                try
-                    rangy.restoreSelection(rangy_sel)
-                catch e
-                    console.log("WARNING: ", e)
-                    rangy_sel = undefined
-                @html_editor_save_selection()
-                save_rangy_sel()
-
-        on_cm_update = () =>
-            #console.log("cm_update")
-            restore_rangy_sel()
-        if not $.browser.ie    # IE is terrible...
-            cm.on('update', on_cm_update)
-
-        ###

         # This was a nightmare because it causes codemirror to update the output div, which contains the editor.  Have to change things so editor is contained somewhere else first (i.e., nontrivial project).
         save_to_output = () =>
@@ -2584,9 +2540,9 @@
             # we have to use rangy, which plants elements in DOM, to save/restore selection,
             # since changing the underlying text behind output_mark causes CodeMirror to
             # reset the selection.
-            rangy_sel = rangy.saveSelection()
+            rangy_sel = rangy?.saveSelection()
             cm.replaceRange(output_line, {line:line,ch:1}, {line:line,ch:v.length})
-            rangy.restoreSelection(rangy_sel)
+            rangy?.restoreSelection(rangy_sel)
             @sync()

         save_to_output_timer = undefined


fixed a bug in saving/validating dashboards.

--- app/assets/javascripts/habberdash/controllers/dashboards_controller.js.coffee
+++ app/assets/javascripts/habberdash/controllers/dashboards_controller.js.coffee
@@ -22,7 +22,8 @@
       dashboard = Habberdash.config.dashboards().find(clone).dup()
       dashboard.updateAttributes(serialized)
     else
-      dashboard = Habberdash.config.dashboards().create($.extend(Habberdash.Dashboard.defaults, serialized))
+      serialized['configuration_id'] = Habberdash.config.id
+      dashboard = new Habberdash.Dashboard($.extend(Habberdash.Dashboard.defaults, serialized))

     return @displayErrors(form, dashboard.validate()) unless dashboard.save()
     @navigate("/#{dashboard.id}")


Improve comments

Improve comments

--- app/assets/javascripts/rglossa/components/left/metadata_categories.jsx.coffee
+++ app/assets/javascripts/rglossa/components/left/metadata_categories.jsx.coffee
@@ -18,7 +18,8 @@
       @closeAllSelects()

   componentWillLeave: (cb) ->
-    $(@getDOMNode()).addClass('sidebar-leave-active')  # starts width transition
+    # Start transitioning the width to zero
+    $(@getDOMNode()).addClass('sidebar-leave-active')

     # Calling the callback removes the sidebar div (we wait until the transition ends)
     # and calls componentDidLeave()
@@ -44,8 +45,8 @@
   componentDidEnter: ->
     # Remove the sidebar-enter class that we added in componentWillEnter(). This will
     # cause the width of the component to be defined by its span3 class again, and the
-    # width will transition from zero to the new width (as defined by the 'sidebar' CSS
-    # class).
+    # width will transition from zero to the new width (the 'sidebar' CSS class defines
+    # the transition).
     $(@getDOMNode()).removeClass('sidebar-enter')

   handleSelectedValuesChanged: ->


edit credential: fix title encode problem

--- client/account/lib/accountcredentialeditmodal.coffee
+++ client/account/lib/accountcredentialeditmodal.coffee
@@ -3,6 +3,7 @@
 KDNotificationView  = kd.NotificationView
 showError           = require 'app/util/showError'
 ContentModal = require 'app/components/contentModal'
+Encoder = require 'htmlencode'

 module.exports = class AccountCredentialEditModal extends ContentModal

@@ -19,10 +20,14 @@
     { ui }                    = kd.singletons.computeController
     { credential, provider }  = options

+    { meta, title } = data
+
+    title = Encoder.htmlDecode title
+
     formOptions       =
       provider        : provider
-      defaultValues   : data.meta
-      defaultTitle    : data.title
+      defaultValues   : meta
+      defaultTitle    : title
       requiredFields  : credential.fields
       callback        : (title, data) =>


Show credit card form on checkout payment screen after invalid data has been entered

Fixes #2122

--- core/app/assets/javascripts/store/checkout.js.coffee
+++ core/app/assets/javascripts/store/checkout.js.coffee
@@ -69,7 +69,11 @@
     ).triggerHandler 'click'

   if ($ '#checkout_form_payment').is('*')
+    # Activate already checked payment method if form is re-rendered
+    # i.e. if user enters invalid data
+    ($ 'input[type="radio"]:checked').click()
+
     ($ 'input[type="radio"][name="order[payments_attributes][][payment_method_id]"]').click(->
       ($ '#payment-methods li').hide()
       ($ '#payment_method_' + @value).show() if @checked
-    ).triggerHandler 'click'
+    )


Use @ in stead of this.

Use @ in stead of this.

--- core/app/backbone/views/related_channels_view.coffee
+++ core/app/backbone/views/related_channels_view.coffee
@@ -10,11 +10,11 @@
   itemViewOptions: => addToCollection : @addToCollection

   templateHelpers: =>
-    is_mine: this.model.get('created_by').username == currentUser.get('username')
+    is_mine: @model.get('created_by').username == currentUser.get('username')

   initialize: (options) ->
-    @addToCollection = this.model.subchannels()
-    @collection =  collectionDifference(new ChannelList, 'id', this.model.relatedChannels(), this.addToCollection, [@model]);
+    @addToCollection = @model.subchannels()
+    @collection =  collectionDifference(new ChannelList, 'id', @model.relatedChannels(), @addToCollection, [@model]);

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


Delete unexpected block comment

Delete unexpected block comment

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- node_modules_koding/jspath/src/jspath.coffee
+++ node_modules_koding/jspath/src/jspath.coffee
@@ -109,6 +109,3 @@
       return no unless ref = ref[component]
       prev.push component
     delete ref[last] # TODO: should this return the deleted value.
-###
-Footnotes:
-  1 - if there's no .split() method, assume it's already an array