Update pt-PT.coffee

Update pt-PT.coffee

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -307,12 +307,12 @@
     project_name: "Nome do Projeto"
 #    project_name_help: "What was the project called?"
     project_description: "Descrição"
-#    project_description_help: "Briefly describe the project."
+    project_description_help: "Descreva o projeto em poucas palavras."
     project_picture: "Imagem"
 #    project_picture_help: "Upload a 230x115px or larger image showing off the project."
     project_link: "Ligação"
 #    project_link_help: "Link to the project."
-#    player_code: "Player Code"
+    player_code: "Código do Jogador"

   employers:
     hire_developers_not_credentials: "Contrate programadores, não cartas de recomendação."
@@ -695,9 +695,9 @@
 #    recruitment_description_italic: "a lot"
 #    recruitment_description_ending: "the site remains free and everybody's happy. That's the plan."
     copyrights_title: "Direitos Autorais e Licensas"
-#    contributor_title: "Contributor License Agreement"
+    contributor_title: "Contrato de Licença do Contribuinte (CLA)"
 #    contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"
-#    cla_url: "CLA"
+    cla_url: "CLA"
 #    contributor_description_suffix: "to which you should agree before contributing."
     code_title: "Código - MIT"
 #    code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the"


Update pt-PT.coffee

Update pt-PT.coffee

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -577,8 +577,8 @@

   teachers:
     title: "CodeCombat: Informações para Professores"
-#    intro_1: "CodeCombat is an online game that teaches programming. Students write code in real programming languages."
-#    intro_2: "No experience required!"
+    intro_1: "O CodeCombat é um jogo 'online' que ensina programação. Os estudantes escrevem código em linguagens de programação reais."
+    intro_2: "Não é necessário ter experiência!"
     free_title: "Quanto custa?"
 #    cost_china: "CodeCombat in China is free for the first five levels, after which it costs $9.99 per month for access to our other 120+ levels on our exclusive China servers."
 #    free_1: "CodeCombat Basic is FREE! There are 70+ free levels which cover every concept."
@@ -604,8 +604,8 @@
     how_much_1: "Uma"
     how_much_2: "subscrição mensal"
     how_much_3: "custa $9.99 e pode ser cancelada a qualquer momento."
-#    how_much_4: "Additionally, we provide discounts for larger groups:"
-#    group_discounts_1: "We also offer group discounts for bulk subscriptions."
+    how_much_4: "Adicionalmente, oferecemos descontos para grupos maiores:"
+    group_discounts_1: "Também oferecemos descontos de grupo para subscrições em massa."
     sys_requirements_title: "Requisitos do Sistema"
     sys_requirements_1: "Um navegador moderno. As versões mais recentes do Chrome, Firefox ou Safari. Internet Explorer 9 ou mais recente."
     sys_requirements_2: "O CodeCombat ainda não é suportado em iPad's."
@@ -867,26 +867,26 @@
 #    scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the "
 #    scribe_introduction_url_mozilla: "Mozilla Developer Network"
 #    scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you."
-    scribe_attribute_1: "Habilidade com palavras é basicamente do que precisas. Não apenas gramática e ortografia, mas seres capaz de explicar ideias complicadas a outros."
+    scribe_attribute_1: "Habilidade com palavras é basicamente o que precisas. Não apenas gramática e ortografia, mas seres capaz de explicar ideias complicadas a outros."
     contact_us_url: "Contacta-nos"
-    scribe_join_description: "fala-nos um bocado de ti, a tua experiência com a programação e o tipo de coisas sobre o qual gostavas de escrever. Começamos a partir daí!"
+    scribe_join_description: "fala-nos um bocado de ti, da tua experiência com a programação e do tipo de coisas sobre as quais gostarias de escrever. Começamos a partir daí!"
     scribe_subscribe_desc: "Receber e-mails sobre anúncios relativos à escrita de artigos."
     diplomat_introduction_pref: "Portanto, se há uma coisa que aprendemos com o nosso "
     diplomat_launch_url: "lançamento em Outubro"
-    diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras noutro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar estes níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti."
+    diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras num outro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar os níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti."
     diplomat_attribute_1: "Fluência em Inglês e no idioma para o qual gostarias de traduzir. Quando são tentadas passar ideias complicadas, é importante uma excelente compreensão das duas!"
     diplomat_i18n_page_prefix: "Podes começar a traduzir os nossos níveis se fores à nossa"
     diplomat_i18n_page: "página de traduções"
     diplomat_i18n_page_suffix: ", ou a nossa interface e website no GitHub."
     diplomat_join_pref_github: "Encontra o ficheiro 'locale' do teu idioma "
     diplomat_github_url: "no GitHub"
-    diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda esta caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!"
+    diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda a caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!"
     diplomat_subscribe_desc: "Receber e-mails sobre desenvolvimentos da i18n e níveis para traduzir."
 #    ambassador_introduction: "This is a community we're building, and you are the connections. We've got forums, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you."
 #    ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!"
 #    ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!"
     ambassador_join_note_strong: "Nota"
-    ambassador_join_note_desc: "Uma das nossas maiores prioridades é construir níveis multijogador onde os jogadores com dificuldade a passar níveis podem invocar feiticeiros mais experientes para serem ajudados. Esta será uma ótima forma para os embixadores fazerem o que sabem. Vamos manter-te atualizado!"
+    ambassador_join_note_desc: "Uma das nossas maiores prioridades é construir níveis multijogador onde os jogadores com dificuldade para passar níveis possam invocar feiticeiros mais experientes para os ajudarem. Esta será uma ótima forma de os embaixadores fazerem o que sabem. Vamos manter-te atualizado!"
     ambassador_subscribe_desc: "Receber e-mails relativos a novidades do suporte e desenvolvimentos do modo multijogador."
     changes_auto_save: "As alterações são guardadas automaticamente quando clicas nas caixas."
     diligent_scribes: "Os Nossos Dedicados Escrivões:"


Update pt-PT.coffee (#5140)

Update pt-PT.coffee (#5140)

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -24,7 +24,7 @@
     democratizing: "Democratizar o processo de aprender a programar está na base da nossa filosofia. Todos devem poder aprender a programar."
     forgot_learning: "Acho que eles se esqueceram que estavam a aprender alguma coisa."
     wanted_to_do: "Programar é algo que sempre quis fazer e nunca pensei que poderia aprender isso na escola."
-#    builds_concepts_up: "I like how CodeCombat builds the concepts up. It's really easy to understand and fun to figure it out."
+    builds_concepts_up: "Gosto da forma como o CodeCombat desenvolve os conceitos. É muito fácil compreendê-los e é divertido descobri-los."
     why_games: "Porque é que aprender através de jogos é importante?"
     games_reward: "Os jogos recompensam o esforço produtivo."
     encourage: "Jogar é algo que encoraja a interação, a descoberta e a tentativa erro. Um bom jogo desafia o jogador a dominar habilidades com o tempo, que é o mesmo processo fundamental que os estudantes atravessam quando aprendem."
@@ -47,7 +47,7 @@
 #    get_started_title: "Set up your class today"
 #    get_started_subtitle: "Set up a class, add your students, and monitor their progress as they learn computer science."
     request_demo: "Pedir uma Demonstração"
-#    setup_a_class: "Set Up a Class"
+    setup_a_class: "Configurar uma Turma"
     have_an_account: "Tens uma conta?"
     logged_in_as: "Atualmente tens sessão iniciada como"
 #    computer_science: "Our self-paced courses cover basic syntax to advanced concepts"
@@ -118,13 +118,13 @@
     subscribe_as_diplomat: "Subscreve-te como Diplomata"

   play:
-#    anon_signup_title_1: "CodeCombat has a"
-#    anon_signup_title_2: "Classroom Version!"
-#    anon_signup_enter_code: "Enter Class Code:"
-#    anon_signup_ask_teacher: "Don't have one? Ask your teacher!"
-#    anon_signup_create_class: "Want to create a class?"
-#    anon_signup_setup_class: "Set up a class, add your students, and monitor progress!"
-#    anon_signup_create_teacher: "Create free teacher account"
+    anon_signup_title_1: "O CodeCombat tem uma"
+    anon_signup_title_2: "Versão para Turmas!"
+    anon_signup_enter_code: "Introduz um Código de Truma:"
+    anon_signup_ask_teacher: "Não tens um? Pede ao teu professor!"
+    anon_signup_create_class: "Queres criar uma turma?"
+    anon_signup_setup_class: "Configura um turma, adiciona estudantes e monitoriza o progresso!"
+    anon_signup_create_teacher: "Criar uma conta de professor gratuita"
     play_as: "Jogar Como" # Ladder page
 #    get_course_for_class: "Assign Game Development and more to your classes!"
 #    request_licenses: "Contact our school specialists for details."
@@ -621,7 +621,7 @@
     methods: "Métodos"
     events: "Eventos"
 #    handlers: "Handlers"
-#    properties: "Properties"
+    properties: "Propriedades"
 #    snippets: "Snippets"
 #    spawnable: "Spawnable"
     html: "HTML"
@@ -1110,7 +1110,7 @@
 #    conversion_warning: "WARNING: Your current account is a Student Account. Once you submit this form, your account will be updated to a Teacher Account."
 #    learn_more_modal: "Teacher accounts on CodeCombat have the ability to monitor student progress, assign licenses and manage classrooms. Teacher accounts cannot be a part of a classroom - if you are currently enrolled in a class using this account, you will no longer be able to access it once you update to a Teacher Account."
     create_account: "Criar uma Conta de Professor"
-    create_account_subtitle: "Obtém acesso a ferramentas reservadas a professores para usares o CodeCombat na sala de aula. Cria uma turma, adiciona os teus alunos e monitoriza o progresso deles!"
+    create_account_subtitle: "Obtém acesso a ferramentas reservadas a professores para usares o CodeCombat na sala de aula. Cria uma turma, adiciona os teus estudantes e monitoriza o progresso deles!"
 #    convert_account_title: "Update to Teacher Account"
 #    not: "Not"

@@ -2190,7 +2190,7 @@
     you_can3: "que pode ser aplicado à tua conta ou dado a outros."

   loading_error:
-    could_not_load: "Erro ao carregar do servidor" # {change}
+    could_not_load: "Erro a carregar do servidor. Experimenta atualizar a página."
     connection_failure: "A Ligação Falhou"
     connection_failure_desc: "Não parece que estejas ligado à internet! Verifica a tua ligação de rede e depois recarrega esta página."
     login_required: "Sessão Iniciada Obrigatória"
@@ -2261,7 +2261,7 @@
     continue_statements: "Declarações 'Continue'"
 #    dom_events: "DOM Events"
 #    dynamic_styling: "Dynamic Styling"
-#    events: "Events"
+    events: "Eventos"
 #    event_concurrency: "Event Concurrency"
 #    event_data: "Event Data"
 #    event_handlers: "Event Handlers"
@@ -2303,8 +2303,8 @@
     math_trigonometry: "Trigonometria"
     object_literals: "'Object Literals'"
     parameters: "Parâmetros"
-#    programs: "Programs"
-#    properties: "Properties"
+    programs: "Programas"
+    properties: "Propriedades"
 #    property_access: "Accessing Properties"
 #    property_assignment: "Assigning Properties"
 #    property_coordinate: "Coordinate Property"
@@ -2322,15 +2322,15 @@
 #    while_condition_loops: "While Loops with Conditionals"
 #    while_loops_simple: "While Loops"
 #    while_loops_nested: "Nested While Loops"
-    xy_coordinates: "Coordenadas Cartesianas"
+    xy_coordinates: "Pares de Coordenadas"
     advanced_strings: "'Strings' Avançadas" # Rest of concepts are deprecated
     algorithms: "Algoritmos"
     boolean_logic: "Lógica Booleana"
     basic_html: "HTML Básico"
     basic_css: "CSS Básico"
 #    basic_web_scripting: "Basic Web Scripting"
-#    intermediate_html: "Intermediate HTML"
-#    intermediate_css: "Intermediate CSS"
+    intermediate_html: "HTML Intermédio"
+    intermediate_css: "CSS Intermédio"
 #    intermediate_web_scripting: "Intermediate Web Scripting"
     advanced_html: "HTML Avançado"
     advanced_css: "CSS Avançado"


Minor UI fix

Minor UI fix

--- views/components/main/parts/ndock-panel.cjsx
+++ views/components/main/parts/ndock-panel.cjsx
@@ -142,7 +142,7 @@
             <span className="ndock-name">
               {i18n.resources.__ @state.docks[i].name}
             </span>
-            <Label className="ndock-timer" bsStyle="default">
+            <Label className="ndock-timer" bsStyle="success">
               {resolveTime 0}
             </Label>
           </div>


Spawn yeti on FirstYeti event.

Spawn yeti on FirstYeti event.

--- app/models/Event.coffee
+++ app/models/Event.coffee
@@ -1,4 +1,4 @@
-Entity = require 'models/Entity'
+Entity   = require 'models/Entity'
 mediator = require 'mediator'

 module.exports = class Event extends Entity
@@ -15,4 +15,16 @@

   onTouchBegin: (body, point, impulse) =>
     if @name == 'FirstYeti'
-      alert 'da fucking yetis come and get you'
+      @name = ''
+      @level.tasks.push =>
+        yeti =
+          name: 'Yeti'
+          type: 'Yeti'
+          x: 16*32
+          y: 16
+          width: 32
+          height: 32
+          properties: {
+          }
+
+        @level.addEntity yeti


atom: Resort atom package without double quote

--- atom/packages.cson
+++ atom/packages.cson
@@ -5,6 +5,7 @@
   "file-icons"
   "highlight-selected"
   "intentions"
+  "linter"
   "linter-coffeelint"
   "linter-csslint"
   "linter-eslint"
@@ -19,7 +20,6 @@
   "linter-ui-default"
   "linter-write-good"
   "linter-xmllint"
-  "linter"
   "minimap"
   "omni-ruler"
   "package-sync"


Update projects

Update projects

--- atom/projects.cson
+++ atom/projects.cson
@@ -4,6 +4,9 @@
     paths: [
       "/Users/rogier/git/rm-rails"
       "/Users/rogier/git/rm-oidc-engine"
+      "/Users/rogier/git/rm-masterdata-engine"
+      "/Users/rogier/git/rm-data-admin"
+      "/Users/rogier/git/rm-api"
     ]
   }
   {
@@ -17,7 +20,6 @@
     title: "rm-data-admin"
     paths: [
       "/Users/rogier/git/rm-data-admin"
-      "/Users/rogier/git/rm-masterdata-engine"
     ]
   }
   {


:atom: Add `f` snippet for `() => {}` in JavaScript


Fix delete function.

Fix delete function.

--- app/assets/javascripts/knockout_cells/context.js.coffee
+++ app/assets/javascripts/knockout_cells/context.js.coffee
@@ -27,16 +27,16 @@
       @token = $("form").first().data("authenticity-token")
       @model = model
       @model_name = options.model_name
-      @deleteObject = =>
+      @deleteObject = (onSuccess)=>
         console.log @model.id()
         data = {}
         data[@model_name] = {id: @model.id()}
         $.ajax
           type: "DELETE"
-          url: @options.url
+          url: "#{@options.url}/#{@model.id()}"
           data: data
           success: (data) =>
-            return false
+            onSuccess

       @submit = (object)=>
         data = {}
@@ -56,7 +56,6 @@
             console.log "error"

       @textField = (root, model_name) ->
-        console.log root.model
         model = root.model[model_name]
         errors = ko.observableArray([])
         if root.model.errors


Add grid-lines to PivotChart

Add grid-lines to PivotChart

--- app/assets/javascripts/notebook/magic/pivotChart.coffee
+++ app/assets/javascripts/notebook/magic/pivotChart.coffee
@@ -60,6 +60,11 @@
             # make sure x labels fit
             right: 50
           },
+          grid: {
+            y: {
+              show: true
+            }
+          },
           axis: {
             x: {
               tick: {


Fixed record not found error when using address id

--- app/assets/javascripts/sprangular/models/address.coffee
+++ app/assets/javascripts/sprangular/models/address.coffee
@@ -28,7 +28,6 @@
       @address1

   serialize: ->
-    id: @id
     firstname: @firstname
     lastname: @lastname
     address1: @address1


Indicate when a result isn't public domain / viewable (fixes #18)

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -48,6 +48,12 @@
           if callback?
             callback(data)

+ht_rights = (usRightsString) ->
+  if usRightsString? and (usRightsString.toLowerCase() == 'full view')
+    return ''
+  else
+    return " \uD83D\uDD12"
+
 ht_biblio_query = (ht_id, score = 0) ->
   if $("##{html_id(ht_id)}").length == 0
     console.log "Adding HT: #{ht_id}"
@@ -69,7 +75,7 @@
         if $("##{html_id(ht_id)}").length == 0
           $('#table').DataTable().row.add([
             '',
-            "#{ht_id}",
+            "#{ht_id}" + ht_rights(data.items[0]['usRightsString']),
             _.values(data.records)[0].titles[0],
             _.values(data.records)[0].publishDates[0],
             ht_object.enumcron || '',
@@ -266,6 +272,12 @@
               if ebook.preview_url.match(IA_REGEX)
                 process_ia(ebook.preview_url)

+gb_rights = (accessViewStatus) ->
+  if accessViewStatus? and (accessViewStatus == 'FULL_PUBLIC_DOMAIN')
+    return ''
+  else
+    return " \uD83D\uDD12"
+
 gb_biblio_query = (gb_id, score = 0) ->
   if $("##{html_id(gb_id)}").length == 0
     console.log("Adding GB: #{gb_id}")
@@ -286,7 +298,7 @@
         if $("##{html_id(gb_id)}").length == 0
           $('#table').DataTable().row.add([
             '',
-            "#{gb_id}",
+            "#{gb_id}" + gb_rights(data.accessInfo.accessViewStatus),
             data.volumeInfo.title,
             data.volumeInfo.publishedDate,
             '',


Groups: populateMembers method is divided for readability

--- client/Dashboard/views/groupsmemberpermissionsview.coffee
+++ client/Dashboard/views/groupsmemberpermissionsview.coffee
@@ -83,39 +83,53 @@
       @getData().fetchMembers selector, options, (err, members)=> @populateMembers err, members

   populateMembers:(err, members)->
+    instantiateItems = (err) =>
+      return warn err  if err
+      @listController.instantiateListItems members
+      @emit 'teasersLoaded' if members.length is 10
+
     return warn err if err
     @listController.hideLazyLoader()
     @skip += members.length
+    if members.length > 0
+      @fetchUserRoles members, (err) =>
+        return warn err  if err
+        #no need to fetch user status for all group admins
+        if @getData().slug is "koding"
+          @fetchUserStatus members, instantiateItems
+        else
+          instantiateItems()

-    if members.length > 0
-      @timestamp = new Date members.last.timestamp_
-      ids = (member._id for member in members)
-      @getData().fetchUserRoles ids, (err, userRoles)=>
-        return warn err if err
-        userRolesHash = {}
-        for userRole in userRoles
-          userRolesHash[userRole.targetId] ?= []
-          userRolesHash[userRole.targetId].push userRole.as
+  fetchUserRoles:(members, callback) ->
+    ids = (member._id for member in members)
+    @getData().fetchUserRoles ids, (err, userRoles)=>
+      return callback err  if err
+      userRolesHash = {}
+      for userRole in userRoles
+        userRolesHash[userRole.targetId] ?= []
+        userRolesHash[userRole.targetId].push userRole.as

+      list = @listController.getListView()
+      list.getOptions().userRoles ?= {}
+      {userRoles} = list.getOptions()
+      userRoles = _.extend(
+        userRoles, userRolesHash
+      )
+      callback null

-        list = @listController.getListView()
-        list.getOptions().userRoles ?= []
-        list.getOptions().userRoles = _.extend(
-          list.getOptions().userRoles, userRolesHash
-        )
+  fetchUserStatus: (members, callback) ->
+    nicknames = (member.profile.nickname for member in members)
+    @getData().fetchUserStatus nicknames, (err, users)=>
+      return callback err  if err
+      userStatusHash = {}
+      for user in users
+        userStatusHash[user.username] = user.status

-        nicknames = (member.profile.nickname for member in members)
-        @getData().fetchUserStatus nicknames, (err, users)=>
-          userStatusHash = {}
-          for user in users
-            userStatusHash[user.username] = user.status
-
-          listOptions = list.getOptions()
-          listOptions.userStatus ?= []
-          listOptions.userStatus = _.extend(listOptions.userStatus, userStatusHash)
-          @listController.instantiateListItems members
-          if members.length < 30
-            @continueLoadingTeasers()
+      list = @listController.getListView()
+      list.getOptions().userStatus ?= {}
+      {userStatus} = list.getOptions()
+      userStatus = _.extend(userStatus, userStatusHash)
+      callback null

   refresh:->
     @listController.removeAllItems()


UpdateCustomVariable: share custom variables with the group [completes #103184312]

--- client/admin/lib/views/stacks/updatecustomvariable.coffee
+++ client/admin/lib/views/stacks/updatecustomvariable.coffee
@@ -1,3 +1,4 @@
+kd     = require 'kd'
 remote = require('app/remote').getInstance()

@@ -7,8 +8,15 @@
   { credentials }    = stackTemplate
   credentials.custom = [credential.identifier]

-  stackTemplate.update { credentials }, (err) ->
-    callback err, stackTemplate
+  # After adding custom variable, we are sharing it with the current
+  # group, so anyone in this group can reach these custom variables ~ GG
+  { slug } = kd.singletons.groupsController.getCurrentGroup()
+
+  credential.shareWith { target: slug }, (err) ->
+    console.warn 'Failed to share credential:', err  if err
+
+    stackTemplate.update { credentials }, (err) ->
+      callback err, stackTemplate

 createAndUpdate = (options, callback) ->


buckets more link WIP

buckets more link WIP

--- client/app/CommonViews/LinkViews.coffee
+++ client/app/CommonViews/LinkViews.coffee
@@ -84,6 +84,7 @@
       @loadFromOrigins options.group

   loadFromOrigins:(group)->
+
     three = group.slice(-3)
     bongo.cacheable three, (err, bucketContents)=>
       @setData bucketContents
@@ -91,9 +92,11 @@
       @render()

   itemClass:(options, data)->
+
     new (@getOptions().subItemClass) options, data

   createParticipantSubviews:->
+
     participants = @getData()
     for participant, index in participants
       @["participant#{index}"] = @itemClass {}, participant
@@ -101,6 +104,7 @@
     @template.update()

   pistachio:->
+
     participants = @getData()
     {hasMore, totalCount} = @getOptions()
     tmpl = switch participants.length
@@ -110,26 +114,37 @@
         sep = if @participant0.getData() instanceof bongo.api.JAccount then ', ' else ' '
         "{{> @participant0}}#{sep}{{> @participant1}}#{if hasMore then sep else ' and'} {{> @participant2}}"
     tmpl += " and #{totalCount-3} more" if hasMore
-    super tmpl
+    return tmpl

 class ActivityChildViewTagGroup extends LinkGroup
-
+
   render:->
+
     @createParticipantSubviews()
-    # super

   pistachio:->

     participants = @getData()
     {hasMore, totalCount} = @getOptions()

-    tmpl = switch participants.length
+    @more = new KDCustomHTMLView
+      tagName     : "a"
+      cssClass    : "more"
+      partial     : "#{participants.length-3} more"
+      attributes  :
+        href      : "#"
+        title     : "Click to view..."
+      click       : ->
+        new FollowedModalView {}, participants
+
+    switch participants.length
       when 0 then ""
       when 1 then "in {{> @participant0}}"
       when 2 then "in {{> @participant0}}{{> @participant1}}"
-      else "in {{> @participant0}}{{> @participant1}}{{> @participant2}}"
-    tmpl += "and #{totalCount-3} more" if hasMore
-    return tmpl
+      when 3 then "in {{> @participant0}}{{> @participant1}}{{> @participant2}}"
+      when 4 then "in {{> @participant0}}{{> @participant1}}{{> @participant2}}{{> @participant3}}"
+      else "in {{> @participant0}}{{> @participant1}}{{> @participant2}}and {{> @more}}"
+

 class FollowedModalView extends KDModalView

@@ -158,18 +173,24 @@
     options.height = "auto"

     super
+
+  # viewAppended:->
+  #
+  #   @putList()

   putList:->

     participants = @getData()
-    log participants
-
-    controller = new listControllerMap()[@type]
-      subItemClass : listItemMap()[@type]
-    , items : participants
-
-    # log controller.getView()
-    # @addSubView controller.getView(), ".kdmodal-content"
+
+    # controller = new listControllerMap()[@type]
+    # subclass of KDListViewController throws an error !!!
+    controller = new KDListViewController
+      view            : new KDListView
+        subItemClass  : listItemMap()[@type]
+    ,
+      items           : participants
+
+    @addSubView controller.getView(), ".kdmodal-content"

 class AvatarView extends LinkView
   constructor:(options,data)->


Save a draft before publishing it

Save a draft before publishing it

--- cms/static/coffee/src/views/unit.coffee
+++ cms/static/coffee/src/views/unit.coffee
@@ -124,7 +124,8 @@

   publishDraft: (event) ->
     @wait(true)
-
+    @saveDraft()
+
     $.post('/publish_draft', {
       id: @$el.data('id')
     }, =>


Fixing issue with mood polling

Fixing issue with mood polling

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -32,6 +32,45 @@
     for room, board of robot.brain.data._private.board
       robot.messageRoom room, "Hello " + robot.brain.data._private.team[room].name + "!"
       robot.messageRoom room, "Are there any achievements from the week that someone @here needs to add to your board '" + board.name + "'?"
+  , null, true, 'America/New_York'
+
+  moodPoll = new CronJob '00 05 20 * * 1-5', ->
+    for room, board of robot.brain.data._private.board
+      buttonName = "mood_" + robot.brain.data._private.team[room]._id
+      robot.emit 'slack.attachment',
+        message: robot.messageRoom
+        content:
+          text: "How was your day today?"
+          fallback: "Oh no! Something went horribly wrong!"
+          callback_id: "dab_mood"
+          color: "#3AA3E3"
+          attachment_type: "default"
+          actions: [{
+            name: buttonName
+            text: ":bolt-ecstatic:"
+            type: "button"
+            value: "ecstatic"
+          },{
+            name: buttonName
+            text: ":bolt-happy:"
+            type: "button"
+            value: "happy"
+          },{
+            name: buttonName
+            text: ":bolt-indifferent:"
+            type: "button"
+            value: "indifferent"
+          },{
+            name: buttonName
+            text: ":bolt-disappointed:"
+            type: "button"
+            value: "disappointed"
+          },{
+            name: buttonName
+            text: ":bolt-sad:"
+            type: "button"
+            value: "sad"
+          }]
   , null, true, 'America/New_York'

   robot.hear /what is the team's mood in the past (.*) days/i, (msg) ->
@@ -224,42 +263,3 @@
         msg.send "Here are our achievements so far:"
         for achievement in JSON.parse body
           msg.send achievement.description
-
-  moodPoll = new CronJob '00 00 20 * * 1-5', ->
-    for room, board of robot.brain.data._private.board
-      buttonName = "mood_" + robot.brain.data._private.team[room]._id
-      robot.emit 'slack.attachment',
-        message: robot.messageRoom
-        content:
-          text: "How was your day today?"
-          fallback: "Oh no! Something went horribly wrong!"
-          callback_id: "dab_mood"
-          color: "#3AA3E3"
-          attachment_type: "default"
-          actions: [{
-            name: buttonName
-            text: ":bolt-ecstatic:"
-            type: "button"
-            value: "ecstatic"
-          },{
-            name: buttonName
-            text: ":bolt-happy:"
-            type: "button"
-            value: "happy"
-          },{
-            name: buttonName
-            text: ":bolt-indifferent:"
-            type: "button"
-            value: "indifferent"
-          },{
-            name: buttonName
-            text: ":bolt-disappointed:"
-            type: "button"
-            value: "disappointed"
-          },{
-            name: buttonName
-            text: ":bolt-sad:"
-            type: "button"
-            value: "sad"
-          }]
-  , null, true, 'America/New_York'


Remove space after closing curly braces in snippets

I originally added the space so I could conveniently type an `else if`,
`else`, or `finally` without having to type the space manually. However,
for projects without an editorconfig that trims trailing whitespace
automatically, this added convenience ends up being more of a nuisance.


url grabber regexp fixed, so it won't pick up numbers (4.7ghz) accidentally

--- core/utils.coffee
+++ core/utils.coffee
@@ -126,7 +126,8 @@
     # @expandWwwDotDomains @expandUrls @expandUsernames @expandTags text
     text = text.replace /
/g, ' '
     text = __utils.putShowMore text if shorten
-    @expandWwwDotDomains @expandUrls @expandUsernames text
+    @expandUrls @expandUsernames text
+    # @expandWwwDotDomains @expandUrls @expandUsernames text

   expandWwwDotDomains: (text) ->
     return null unless text
@@ -171,15 +172,19 @@

   expandUrls: (text) ->
     return null unless text
-    text.replace /([A-Za-z]+:\/\/)?[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&#\+\?\/.=]+/g, (url) ->
+    text.replace /([A-Za-z]+:\/\/)?([A-Za-z0-9-_]\.)?[A-Za-z0-9-_]+\.[A-Za-z][A-Za-z0-9-_:%&#\+\?\/.=]+/g, (url) ->
       originalUrl = url
       visibleUrl = url.replace(/(ht|f)tp(s)?\:\/\//,"").replace(/\/.*/,"")

       if not /[A-Za-z]+:\/\//.test url
         # url has no protocol
         url = '//'+url
-
       "#{visibleUrl}/…"
+      # unless url in ["//more...","//less..."]
+      #   "#{visibleUrl}/…"
+      # else
+      #   originalUrl
+
       # new KDView
       #   partial :   "#{visibleUrl}"
       #   tooltip :


Fix invalid args for messageRoom

Fix invalid args for messageRoom

--- scripts/esa.coffee
+++ scripts/esa.coffee
@@ -93,7 +93,7 @@

   unless options.just_emit
     robot.on 'esa.webhook', (kind, data) ->
-      robot.messageRoom options.room = switch kind
+      robot.messageRoom options.room, switch kind
         when 'post_create'
            "#{data.user.screen_name} created a new post: #{if data.post.wip then '(WIP) ' else ''}#{data.post.name}\n>#{data.post.message}\n#{data.post.url}"
         when 'post_update'


Reactive wrapping bugfix and jRemove bugfix

--- models.coffee
+++ models.coffee
@@ -281,7 +281,7 @@
                 else
                     # On unattached instances, denormed reactives behave like
                     # non-denormed reactives.
-                    reactiveSpec.val.call @
+                    J.Var(reactiveSpec.val.call @).get()

         else
             fieldName = fieldOrReactiveName
@@ -996,13 +996,14 @@
         if not doc?
             return 0

+        ret = modelClass.collection.remove instanceId
+
+        if not _reserved
+            oldFields = _.clone doc
+            delete oldFields._reactives
+            J.denorm.resetWatchers modelName, instanceId, oldFields, {}
+
         instance = modelClass.fromDoc doc
-
-        ret = modelClass.collection.remove instanceId
-
-        if not _reserved
-            J.denorm.resetWatchers modelName, instanceId, doc, {}
-
         instance.onRemove?()

         ret


bumping version to 1.1.2

bumping version to 1.1.2

--- src/sc.coffee
+++ src/sc.coffee
@@ -1,5 +1,5 @@
 window.SC = SC.Helper.merge SC || {},
-  _version: "1.1.1"
+  _version: "1.1.2"
   _baseUrl: "http://connect.soundcloud.com"
   options:
     site: "soundcloud.com"


Don't close delete button on mouse leave when it is opened by default

--- core/app/backbone/views/buttons/delete_button_view.coffee
+++ core/app/backbone/views/buttons/delete_button_view.coffee
@@ -6,7 +6,7 @@

   events:
     'click .js-first': '_toggleButton'
-    'mouseleave': '_closeButton'
+    'mouseleave': '_closeButtonOnMouseLeave'

   triggers:
     'click .js-second': 'delete'
@@ -18,5 +18,7 @@
   _toggleButton: ->
     @$el.toggleClass 'delete-button-open'

-  _closeButton: ->
+  _closeButtonOnMouseLeave: ->
+    return if @options.opened
+
     @$el.removeClass 'delete-button-open'


Removed unused class

Removed unused class

--- core/app/backbone/views/wheel/base_fact_wheel_view.coffee
+++ core/app/backbone/views/wheel/base_fact_wheel_view.coffee
@@ -185,7 +185,7 @@
     Backbone.Factlink.makeTooltipForView @,
       positioning:
         side: 'top'
-        popover_className: 'translucent-dark-popover fact-wheel-tooltip'
+        popover_className: 'translucent-dark-popover'
         margin: @maxStrokeWidth()/2 - 7
       selector: '.authority'
       tooltipViewFactory: =>  new TextView
@@ -195,7 +195,7 @@
     Backbone.Factlink.makeTooltipForView @,
       positioning:
         side: @_tooltipSideForPath(@opinionTypeRaphaels[name])
-        popover_className: 'translucent-dark-popover fact-wheel-tooltip'
+        popover_className: 'translucent-dark-popover'
         margin: @maxStrokeWidth()/2 - 3
       selector: selector
       tooltipViewFactory: => new TextView


Refactor to Binding and dependencies

Refactor to Binding and dependencies

--- lib/Task/ActivityTask/BindingTask/Read/LimitOffset.coffee
+++ lib/Task/ActivityTask/BindingTask/Read/LimitOffset.coffee
@@ -17,6 +17,7 @@

   execute: ->
     Promise.bind(@)
+    .then @acquireCredential
     .then @getTotal
     .then @progressInit
     .then @readChapter


lib -> interface

lib -> interface

--- natal.coffee
+++ natal.coffee
@@ -25,12 +25,12 @@
 podMinVersion   = '0.38.2'
 process.title   = 'natal'

-reactLibs =
+reactInterfaces =
   om:        'org.omcljs/om "0.9.0"'
   'om-next': 'org.omcljs/om "1.0.0-alpha11"'

-libNames   = Object.keys reactLibs
-defaultLib = 'om'
+interfaceNames   = Object.keys reactInterfaces
+defaultInterface = 'om'

 log = (s, color = 'green') ->
@@ -144,7 +144,7 @@
     logErr message

-init = (projName, lib) ->
+init = (projName, interfaceName) ->
   projNameHyph = projName.replace(camelRx, '$1-$2').toLowerCase()
   projNameUs   = toUnderscored projName

@@ -175,12 +175,15 @@
     exec "cp #{resources}project.clj project.clj"
     edit \
       'project.clj',
-      [[projNameHyphRx, projNameHyph], [/\$CLJS_REACT_LIB\$/, reactLibs[lib]]]
+      [
+        [projNameHyphRx, projNameHyph]
+        [/\$REACT_INTERFACE\$/, reactInterfaces[interfaceName]]
+      ]

     corePath = "src/#{projNameUs}/core.clj"
     fs.unlinkSync corePath
     corePath += 's'
-    exec "cp #{resources}#{lib}.cljs #{corePath}"
+    exec "cp #{resources}#{interfaceName}.cljs #{corePath}"
     edit corePath, [[projNameHyphRx, projNameHyph], [projNameRx, projName]]

     log 'Compiling ClojureScript'
@@ -414,15 +417,15 @@

 cli.command 'init <name>'
   .description 'create a new ClojureScript React Native project'
-  .option "-l, --lib [#{libNames.join ' '}]", 'specify React wrapper library'
+  .option "-i, --interface [#{interfaceNames.join ' '}]", 'specify React interface'
   .action (name, cmd) ->
     if cmd
-      lib = cmd.lib or defaultLib
+      interfaceName = cmd['interface'] or defaultInterface
     else
-      lib = defaultLib
-
-    unless reactLibs[lib]
-      logErr "Unsupported React wrapper library: #{lib}"
+      interfaceName = defaultInterface
+
+    unless reactInterfaces[interfaceName]
+      logErr "Unsupported React interface: #{interfaceName}"

     if typeof name isnt 'string'
       logErr '''
@@ -431,7 +434,7 @@
              natal init HelloWorld
              '''

-    ensureFreePort -> init name, lib
+    ensureFreePort -> init name, interfaceName

 cli.command 'launch'


made tabbin in the text pane insert tabs rather than tab through to the console

--- assets/js/client.coffee
+++ assets/js/client.coffee
@@ -6,6 +6,17 @@
 socket.on 'connect', (data) ->
     $("#connecting").animate {color:'#FFFFFF'}, 2500

+
+$("#scratch").keydown ->
+    if e.keyCode == 9
+        start: this.selectionStart;
+        end: this.selectionEnd;
+        $this: $(this)
+        value: $this.val()
+        $this.val (value.substring 0, start)+"\t"+(value.substring end)
+        this.selectionStart = this.selectionEnd = start + 1
+        e.preventDefault()
+        false

 $('#scratch').keyup ->
     socket.emit 'edit', {text:$(this).val()}


Add loadPacket()

Add loadPacket()

--- views/index.cjsx
+++ views/index.cjsx
@@ -8,12 +8,14 @@
 BattleDetailArea = require './battle-detail-area'

 # constant
-MAX_PACKET_NUMBER = 128
+MAX_PACKET_NUMBER = 64
 APPDATA = path.join(window.APPDATA_PATH, 'battle-detail');
 fs.mkdir APPDATA, (error) ->
   return if !error
   return if error.code == 'EEXIST'
   console.error(error)
+FS_RW_OPTIONS =
+  encoding: 'UTF-8'

 updateNonce = (nonce) ->
   if typeof nonce == "number" and nonce > 0
@@ -61,12 +63,26 @@

 savePacket = (packet) ->
   setTimeout (->
-    return unless packet? and packet.poi_timestamp?
-    filename = packet.poi_timestamp
-    file = path.join(APPDATA, "#{filename}.json")
-    data = JSON.stringify(packet)
-    fs.writeFile file, data, (error) ->
-      return unless error
+    try
+      return unless packet? and packet.poi_timestamp?
+      filename = packet.poi_timestamp
+      file = path.join(APPDATA, "#{filename}.json")
+      data = JSON.stringify(packet)
+      fs.writeFileSync(file, data, FS_RW_OPTIONS)
+    catch error
+      console.error error
+  ), 0
+
+# filename = "#{packet.poi_timestamp}.json"
+loadPacket = (filename, callback) ->
+  setTimeout (->
+    try
+      return unless filename? and callback?
+      file = path.join(APPDATA, filename)
+      data = fs.readFileSync(file, FS_RW_OPTIONS)
+      packet = JSON.parse(data)
+      callback(packet)
+    catch error
       console.error error
   ), 0


cleanup

cleanup

--- components/feed/client/feed_item.coffee
+++ components/feed/client/feed_item.coffee
@@ -1,5 +1,6 @@
 _                       = require 'underscore'
 Backbone                = require 'backbone'
+Artworks                = require '../../../models/artwork.coffee'
 sd                      = require('sharify').data
 ShareView               = require '../../share/view.coffee'
 AcquireArtwork          = require('../../acquire/view.coffee').acquireArtwork
@@ -10,8 +11,12 @@

 module.exports.FeedItemView = class FeedItemView extends Backbone.View

+  events:
+    "click .purchase" : "purchase"
+    'click .see-more' : 'fetchMoreArtworks'
+    'click .feed-item-contact-gallery': 'contactGallery'
+
   artworksPage: 1
-
   artworksPageSize: 8

   initialize: (options) ->
@@ -25,6 +30,16 @@
   moreArtworksClick: (event) =>
     analytics.track.click "Clicked show all artworks on feed item"
     @fetchMoreArtworks $(event.target)
+
+  fetchMoreArtworks: ->
+    @$seeMore ?= @$('.see-more')
+    @$seeMore.addClass 'is-loading'
+    @model.toChildModel().fetchArtworks
+      success: (artworks) =>
+        @$seeMore.remove()
+        @$('.feed-large-artworks-columns').html artworkColumns artworkColumns: artworks.groupByColumnsInOrder(4)
+        @setupArtworkSaveControls artworks
+    false

   setupArtworkSaveControls: (artworks=@model.artworks().models)->
     listItems =
@@ -44,11 +59,6 @@
     el = if @$('.feed-item-share-section').length > 0 then @$('.feed-item-share-section') else @$('.post-actions')
     new ShareView el: el

-  events:
-    "click .purchase": "purchase"
-    'click .see-more': 'fetchMoreArtworks'
-    'click .feed-item-contact-gallery': 'contactGallery'
-
   purchase: (event) =>
     $target = $(event.target)
     id = $target.attr('data-id')
@@ -61,16 +71,5 @@
         AcquireArtwork artwork, $target
     false

-
-  fetchMoreArtworks: ->
-    @$seeMore ?= @$('.see-more')
-    @$seeMore.addClass 'is-loading'
-    @model.toChildModel().fetchArtworks
-      success: (artworks) =>
-        @$seeMore.remove()
-        @$('.feed-large-artworks-columns').html artworkColumns artworkColumns: artworks.groupByColumnsInOrder(4)
-        @setupArtworkSaveControls artworks
-    false
-
-  contactGallery: (e) ->
+  contactGallery: ->
     new ShowInquiryView show: @model


call set on inheritable

call set on inheritable

--- src/views/base/index.coffee
+++ src/views/base/index.coffee
@@ -31,12 +31,17 @@
     if type(data) isnt "object"
       throw new Error "data passed in view must be an object"

-
+    # pass data to super - will get set to this view controller
     super data

+    # have reference back to this view controller - useful for templates
     @this = @
+
+    # must have an ID
+    # TODO - should not be familiar with models
     @_id  = data._id ? data.model?.get?("_id") ? data.model?._id ? generateId()

+    # initialize
     @init()

   ###


pricing: plans order is changed to highest->lowest

--- client/landing/site.landing/coffee/pricing/plans.coffee
+++ client/landing/site.landing/coffee/pricing/plans.coffee
@@ -34,19 +34,34 @@

   plans: [
-    title        : 'Free'
-    monthPrice   : '0'
-    reducedMonth : '0'
-    yearPrice    : '0'
-    discount     : '0'
-    description  : 'Best for tinkering with and learning new technologies'
-    cssClass     : 'free'
+    title        : 'Professional'
+    monthPrice   : '49.50'
+    reducedMonth : '39.95'
+    yearPrice    : '479.40'
+    discount     : 955
+    description  : 'Great for managing and delivering client work'
+    cssClass     : 'professional'
     planFeatures : [
-      { partial: '1 VM'           , cssClass: 'vm-count' }
-      { partial: '1 Core'         , cssClass: 'cpu' }
-      { partial: '1GB RAM'        , cssClass: 'ram' }
-      { partial: '3GB Total Disk' , cssClass: 'storage' }
-      { partial: '0 Always on VM' , cssClass: 'always-on disabled' }
+      { partial: '5 VMs '          , cssClass: 'vm-count' }
+      { partial: '1 Core each'     , cssClass: 'cpu' }
+      { partial: '1GB RAM each'    , cssClass: 'ram' }
+      { partial: '50GB Total Disk' , cssClass: 'storage' }
+      { partial: '2 Always on VMs' , cssClass: 'always-on' }
+    ]
+  ,
+    title        : 'Developer'
+    monthPrice   : '24.50'
+    reducedMonth : '19.95'
+    yearPrice    : '239.40'
+    discount     : 455
+    description  : 'Great for developers who work with multiple environments'
+    cssClass     : 'developer'
+    planFeatures : [
+      { partial: '3 VMs '          , cssClass: 'vm-count' }
+      { partial: '1 Core each'     , cssClass: 'cpu' }
+      { partial: '1GB RAM each'    , cssClass: 'ram' }
+      { partial: '25GB Total Disk' , cssClass: 'storage' }
+      { partial: '1 Always on VM'  , cssClass: 'always-on' }
     ]
   ,
     title        : 'Hobbyist'
@@ -64,34 +79,19 @@
       { partial: '1 Always on VM' , cssClass: 'always-on disabled' }
     ]
   ,
-    title        : 'Developer'
-    monthPrice   : '24.50'
-    reducedMonth : '19.95'
-    yearPrice    : '239.40'
-    discount     : 455
-    description  : 'Great for developers who work with multiple environments'
-    cssClass     : 'developer'
+    title        : 'Free'
+    monthPrice   : '0'
+    reducedMonth : '0'
+    yearPrice    : '0'
+    discount     : '0'
+    description  : 'Best for tinkering with and learning new technologies'
+    cssClass     : 'free'
     planFeatures : [
-      { partial: '3 VMs '          , cssClass: 'vm-count' }
-      { partial: '1 Core each'     , cssClass: 'cpu' }
-      { partial: '1GB RAM each'    , cssClass: 'ram' }
-      { partial: '25GB Total Disk' , cssClass: 'storage' }
-      { partial: '1 Always on VM'  , cssClass: 'always-on' }
-    ]
-  ,
-    title        : 'Professional'
-    monthPrice   : '49.50'
-    reducedMonth : '39.95'
-    yearPrice    : '479.40'
-    discount     : 955
-    description  : 'Great for managing and delivering client work'
-    cssClass     : 'professional'
-    planFeatures : [
-      { partial: '5 VMs '          , cssClass: 'vm-count' }
-      { partial: '1 Core each'     , cssClass: 'cpu' }
-      { partial: '1GB RAM each'    , cssClass: 'ram' }
-      { partial: '50GB Total Disk' , cssClass: 'storage' }
-      { partial: '2 Always on VMs' , cssClass: 'always-on' }
+      { partial: '1 VM'           , cssClass: 'vm-count' }
+      { partial: '1 Core'         , cssClass: 'cpu' }
+      { partial: '1GB RAM'        , cssClass: 'ram' }
+      { partial: '3GB Total Disk' , cssClass: 'storage' }
+      { partial: '0 Always on VM' , cssClass: 'always-on disabled' }
     ]
   ]


minor: code stylinf

minor: code stylinf

--- client/landing/site.landing/coffee/teams/AppView.coffee
+++ client/landing/site.landing/coffee/teams/AppView.coffee
@@ -19,6 +19,7 @@
         { title : 'Success Stories', href : '/Features',                name : 'success' }
         { title : 'SIGN IN',         href : '/Team/Login',              name : 'buttonized white login',  attributes : testpath : 'login-link' }
       ]
+
     @form = new TeamTeamsModal
       cssClass : 'TeamsModal--middle login-form'
       callback : (formData) ->


reset query when dialog is shown

reset query when dialog is shown

--- client/lanes/components/record-finder/RecordFinder.cjsx
+++ client/lanes/components/record-finder/RecordFinder.cjsx
@@ -17,6 +17,7 @@
             @props.model

     showFinder: ->
+        @props.query.reset()
         @context.viewport.displayModal(
             title: "Find #{@props.query.title}"
             buttons: [{title: 'Cancel'}]


Remove useless constant

Remove useless constant

Signed-off-by: Balthazar Rouberol <a9d237650e4d4cb3de4969f778ca372566bedbf9@mapado.com>

--- server/Game.coffee
+++ server/Game.coffee
@@ -4,9 +4,8 @@

 class Game
-    @MAX_PLAYERS: 5 # 5
-    @NB_CHARACTERS_PER_PLAYER: 5 # 20
     @MAX_GAME_TIME = 5000
+    @MAX_PLAYERS: 2 # 5

     constructor: ()->
         @playerList = []


Removed WIP code

Removed WIP code

--- client/app/Applications/Environments.kdapplication/views/VMs.coffee
+++ client/app/Applications/Environments.kdapplication/views/VMs.coffee
@@ -16,7 +16,7 @@
     @vmListView = @vmListController.getView()

     @on "Clicked", (item)=>
-      @graphView.update item
+      @graphView.update? item
       @vmListController.deselectAllItems()
       @vmListController.selectSingleItem item

@@ -27,8 +27,6 @@
         kiteName  : 'os',
         vmName    : item.data.name,
         method    : cmd
-      , ->
-        console.log arguments

     @graphView = new VMDetailView
     @splitView = new KDSplitView
@@ -88,21 +86,6 @@
 class VMDetailView extends KDView
   constructor: (options, data) ->
     super options, data
-    @addSubView @netView = new KDCustomHTMLView
-      tagName: 'img'
-      attributes:
-        src: "http://chart.googleapis.com/chart?chxl=0:|%2B0.50%25|%2B0.25%25|0.00%25|-0.25%25&chxt=r&chs=400x200&cht=ls&chco=DA3B15,F7A10A,4582E7&chd=s:abdegedbageba,somedabelprmnr,PRTSPNQYWROQTXYRQ_&chls=1|1|1"
-
-    @addSubView new KDCustomHTMLView
-      tagName: 'br'
-
-    @addSubView @cpuView = new KDCustomHTMLView
-      tagName: 'img'
-      attributes:
-        src: "http://chart.googleapis.com/chart?chs=150x100&cht=gm&chco=000000,00FF00|FFCC33|FF0000&chd=t:70&chma=|0,4&chtt=CPU+Utilization&chts=676767,14"
-
-  update:(item)->
-    console.log item

 class VMListItemView extends KDListItemView
   constructor: (options, data) ->


Just confirm the token all the time

Just confirm the token all the time

--- backend/ide-init.coffee
+++ backend/ide-init.coffee
@@ -33,7 +33,9 @@
           resolve false
   )

-if !atom.config.get('integrated-learn-environment.oauthToken')
+existingToken = atom.config.get('integrated-learn-environment.oauthToken')
+
+if !existingToken
   oauthPrompt = document.createElement 'div'
   oauthPrompt.setAttribute 'style', 'width:100%; text-align: center;'

@@ -77,3 +79,4 @@
           invalidLabel.setAttribute 'style', 'color: red; opacity: 100;'
 else
   atom.commands.dispatch(workspaceView, 'integrated-learn-environment:toggleTerminal')
+  confirmOauthToken(existingToken)


Render immediately

Render immediately

--- core/app/backbone/controllers/profile_controller.coffee
+++ core/app/backbone/controllers/profile_controller.coffee
@@ -5,20 +5,20 @@

   showProfile: (username) ->
     user = new User(username: username)
-    user.fetch
-      success: ->
-        if user.get('deleted')
-          FactlinkApp.mainRegion.show new ReactView component:
-            _div [],
-              'This profile has been deleted.'
-        else
-          FactlinkApp.mainRegion.show new ReactView component:
-            ReactProfile model: user
+    user.fetch()
+
+    if user.get('deleted')
+      FactlinkApp.mainRegion.show new ReactView component:
+        _div [],
+          'This profile has been deleted.'
+    else
+      FactlinkApp.mainRegion.show new ReactView component:
+        ReactProfile model: user

   showNotificationSettings: (username) ->
     user = new User(username: username)
-    user.fetch
-      success: ->
-        FactlinkApp.mainRegion.show new ReactView
-          component: ReactNotificationSettings
-            model: user
+    user.fetch()
+
+    FactlinkApp.mainRegion.show new ReactView
+      component: ReactNotificationSettings
+        model: user


check file size and filename

check file size and filename

--- client.iced
+++ client.iced
@@ -94,7 +94,10 @@
           'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36'
           'Accept-Language': 'zh-CN,zh;q=0.8,it-IT;q=0.6,it;q=0.4,en-US;q=0.2,en;q=0.2'
         proxy: process.env['http_proxy']
-      writer = fs.createWriteStream path.join cwd, task.name, file.name
+      dest_path = path.join cwd, task.name, file.name.replace /[\/\\]/g, path.sep
+      await mkdirp (path.dirname dest_path), defer e
+      return cb e if e
+      writer = fs.createWriteStream dest_path
       await req.on 'response', defer res
       fileSize = Number res.headers['content-length']
       return cb new Error "Invalid Content-Length" if isNaN fileSize
@@ -111,6 +114,9 @@
       await writer.on 'close', defer()
       statusBar.cancel()
       return cb new Error '任务已删除' if req._aborted
+      await fs.stat dest_path, defer e, dest_stats
+      return cb e if e
+      return cb new Error "文件大小不符 #{dest_stats.size} != #{fileSize}" unless dest_stats.size == fileSize

     await queue.execute 'deleteTask', task.id, defer e


make "close and save" blue if on last pane

--- scripts/views/layouts/workspace/bookshelf.coffee
+++ scripts/views/layouts/workspace/bookshelf.coffee
@@ -50,8 +50,10 @@
       modal.find('a[data-toggle="tab"]').on('shown', (e) ->
         if $(e.target).parents('li').next().length
           modal.find('[data-tab-next]').show()
+          modal.find('[data-save]').removeClass('btn-primary')
         else
           modal.find('[data-tab-next]').hide()
+          modal.find('[data-save]').addClass('btn-primary')
       )

       modal.find('[data-cancel]').off('click').click ->


Server: Tests: refactor register handler test file

--- servers/lib/server/handlers/register.test.coffee
+++ servers/lib/server/handlers/register.test.coffee
@@ -1,6 +1,5 @@
 koding                            = require './../bongo'
-
-{ daisy
+{ async
   expect
   request
   querystring
@@ -20,39 +19,37 @@

   it 'should send HTTP 404 if method is not allowed', (done) ->

-    queue       = []
-    methods     = ['put', 'patch', 'del']
-    postParams  = generateRegisterRequestParams()
-
-    addRequestToQueue = (queue, method) -> queue.push ->
+    queue      = []
+    methods    = ['put', 'patch', 'del']
+    postParams = generateRegisterRequestParams()
+
+    addRequestToQueue = (queue, method) -> queue.push (next) ->
       postParams.method = method
       request.del postParams, (err, res, body) ->
-        expect(err)             .to.not.exist
-        expect(res.statusCode)  .to.be.equal 404
-        queue.next()
+        expect(err).to.not.exist
+        expect(res.statusCode).to.be.equal 404
+        next()

     for method in methods
       addRequestToQueue queue, method

-    queue.push -> done()
-
-    daisy queue
+    async.series queue, done

   it 'should send HTTP 200 if GET request sent to Register hadler url', (done) ->

     requestParams = generateRegisterRequestParams()
     request.get requestParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 200
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 200
       done()

   it 'should send HTTP 400 if username is not specified', (done) ->

     postParams = generateRegisterRequestParams
-      body        :
-        username  : ''
+      body       :
+        username : ''

     request.post postParams, (err, res, body) ->
       expect(err)             .to.not.exist
@@ -63,12 +60,12 @@
   it 'should send HTTP 400 if password is not specified', (done) ->

     postParams = generateRegisterRequestParams
-      body        :
-        password  : ''
-
-    request.post postParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 400
+      body       :
+        password : ''
+
+    request.post postParams, (err, res, body) ->
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 400
       done()

@@ -80,8 +77,8 @@
         passwordConfirm : 'anotherPassword'

     request.post postParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 400
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 400
       done()

@@ -89,28 +86,26 @@

     randomString = generateRandomString()
     postParams   = generateRegisterRequestParams
-      body        :
-        username  : randomString
+      body       :
+        username : randomString

     queue = [

-      ->
-        request.post postParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 200
-          queue.next()
-
-      ->
-        request.post postParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 400
-          queue.next()
-
-      -> done()
+      (next) ->
+        request.post postParams, (err, res, body) ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 200
+          next()
+
+      (next) ->
+        request.post postParams, (err, res, body) ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 400
+          next()

     ]

-    daisy queue
+    async.series queue, done

   it 'should send HTTP 400 if email is in use', (done) ->
@@ -122,23 +117,21 @@

     queue = [

-      ->
-        request.post postParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 200
-          queue.next()
-
-      ->
-        request.post postParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 400
-          queue.next()
-
-      -> done()
+      (next) ->
+        request.post postParams, (err, res, body) ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 200
+          next()
+
+      (next) ->
+        request.post postParams, (err, res, body) ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 400
+          next()

     ]

-    daisy queue
+    async.series queue, done

   it 'should send HTTP 400 if dotted gmail address is in use', (done) ->
@@ -153,20 +146,20 @@
     registerParams = generateRegisterRequestParams { body: { email } }

     request.post registerParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 400
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 400
       done()

   it 'should send HTTP 400 if agree is set as off', (done) ->

     postParams = generateRegisterRequestParams
-      body        :
-        agree     : 'off'
-
-    request.post postParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 400
+      body    :
+        agree : 'off'
+
+    request.post postParams, (err, res, body) ->
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 400
       done()

@@ -178,36 +171,34 @@

     queue = [

-      ->
+      (next) ->
         # expecting HTTP 200 response
         request.post postParams, (err, res, body) ->
-          expect(err)             .to.not.exist
-          expect(res.statusCode)  .to.be.equal 200
-          queue.next()
-
-      ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 200
+          next()
+
+      (next) ->
         # expecting user to be saved on mongodb
         params = { username : username }

         JUser.one params, (err, { data : { email, registeredFrom } }) ->
-          expect(err)               .to.not.exist
-          expect(email)             .to.be.equal email
-          queue.next()
-
-      ->
+          expect(err).to.not.exist
+          expect(email).to.be.equal email
+          next()
+
+      (next) ->
         #expecting acount to be created
         params = { 'profile.nickname' : username }

         JAccount.one params, (err, { data : { profile } }) ->
-          expect(err)               .to.not.exist
-          expect(profile.nickname)  .to.be.equal username
-          queue.next()
-
-      -> done()
+          expect(err).to.not.exist
+          expect(profile.nickname).to.be.equal username
+          next()

     ]

-    daisy queue
+    async.series queue, done

   it 'should send HTTP 301 if request is not XHR',  (done) ->
@@ -217,8 +208,8 @@
         'x-requested-with' : 'this is not an XHR'

     request.post postParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 301
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 301
       done()


fixed subchannel initialization

fixed subchannel initialization

first argument of a collection is a list of models, the second argument the options

--- core/app/backbone/models/channel.coffee
+++ core/app/backbone/models/channel.coffee
@@ -20,7 +20,7 @@
     @cache[field] = (@cache[field] || retrieval())

   subchannels: ->
-    @cached 'subchannels', => fetched(new SubchannelList(channel: this))
+    @cached 'subchannels', => fetched(new SubchannelList([], channel: this))

   relatedChannels: ->
     @cached 'relatedchannels', => fetched(new RelatedChannels [], forChannel: this)


Toggle the panel only when the same option is selected again. Else show the panel and set the new option.

--- lib/atom-cscope.coffee
+++ lib/atom-cscope.coffee
@@ -75,6 +75,14 @@
     @atomCscopeView.onResultClick (result) =>
       atom.workspace.open(result.fileName, {initialLine: (result.lineNumber - 1)})

+  togglePanelOption: (option) ->
+    if @atomCscopeView.inputView.getSelectedOption() is option
+      @toggle()
+    else
+      @show()
+      @atomCscopeView.inputView.autoFill(option, '')
+      @atomCscopeView.listView.clearItems()
+
   setUpBindings: ->
     @subscriptions = new CompositeDisposable
     @subscriptions.add atom.commands.add 'atom-workspace',
@@ -85,32 +93,23 @@

     @subscriptions.add atom.commands.add 'atom-workspace',
       'atom-cscope:toggle-symbol': =>
-        @atomCscopeView.inputView.setSelectedOption(0)
-        @toggle()
+        @togglePanelOption(0)
       'atom-cscope:toggle-definition': =>
-        @atomCscopeView.inputView.setSelectedOption(1)
-        @toggle()
+        @togglePanelOption(1)
       'atom-cscope:toggle-functions-called-by': =>
-        @atomCscopeView.inputView.setSelectedOption(2)
-        @toggle()
+        @togglePanelOption(2)
       'atom-cscope:toggle-functions-calling': =>
-        @atomCscopeView.inputView.setSelectedOption(3)
-        @toggle()
+        @togglePanelOption(3)
       'atom-cscope:toggle-text-string': =>
-        @atomCscopeView.inputView.setSelectedOption(4)
-        @toggle()
+        @togglePanelOption(4)
       'atom-cscope:toggle-egrep-pattern': =>
-        @atomCscopeView.inputView.setSelectedOption(6)
-        @toggle()
+        @togglePanelOption(6)
       'atom-cscope:toggle-file': =>
-        @atomCscopeView.inputView.setSelectedOption(7)
-        @toggle()
+        @togglePanelOption(7)
       'atom-cscope:toggle-files-including': =>
-        @atomCscopeView.inputView.setSelectedOption(8)
-        @toggle()
+        @togglePanelOption(8)
       'atom-cscope:toggle-assignments-to': =>
-        @atomCscopeView.inputView.setSelectedOption(9)
-        @toggle()
+        @togglePanelOption(9)

     @subscriptions.add atom.commands.add 'atom-workspace',
       'atom-cscope:find-this-symbol': =>


Fix check for Topic in FavouriteTopicCollection

Can't this be done nicer?

favourite_topics.contains @model doesn't seem to work (I guess because the @model has more attributes than the Topics in FavTopicCollection).

@mark @janpaul123

--- core/app/backbone/views/channels/favourite_topic_button_view.coffee
+++ core/app/backbone/views/channels/favourite_topic_button_view.coffee
@@ -13,7 +13,8 @@
     enabled_label:  Factlink.Global.t.favourited.capitalize()

   buttonEnabled: ->
-    @user.favourite_topics.contains @model
+    # @model.id is the Topic slug_title
+    @model.id in @user.favourite_topics.pluck('slug_title')

   primaryAction: (e) ->
     @model.favourite()


Updating nodes.coffee to make use of new temporary variables code generators.

--- nodes.coffee
+++ nodes.coffee
@@ -70,7 +70,7 @@
     else if this instanceof ValueNode and options.assignment
       this.cacheIndexes(o)
     else
-      reference = literal o.scope.freeVariable()
+      reference = literal o.scope.freeVariable 'cache'
       compiled  = new AssignNode reference, this
       [compiled, reference]
     return [pair[0].compile(o), pair[1].compile(o)] if options.precompile
@@ -374,7 +374,7 @@
       @source = baseline
       if prop.soakNode
         if @base.containsType(CallNode) and i is 0
-          temp = o.scope.freeVariable()
+          temp = o.scope.freeVariable 'cache'
           complete = "(#{ baseline = temp } = (#{complete}))"
         complete = if i is 0
           "(typeof #{complete} === \"undefined\" || #{baseline} === null) ? undefined : "
@@ -488,7 +488,7 @@
     meth = @meth or @superReference(o)
     obj  = @variable and @variable.source or 'this'
     if obj.match(/\(/)
-      temp = o.scope.freeVariable()
+      temp = o.scope.freeVariable 'cache'
       obj  = temp
       meth = "(#{temp} = #{ @variable.source })#{ @variable.last }"
     if @isNew
@@ -496,14 +496,14 @@
       for arg in @args
         arg.contains (n) -> mentionsArgs or= n instanceof LiteralNode and (n.value is 'arguments')
       utility 'extends'
-      a = o.scope.freeVariable()
-      b = o.scope.freeVariable()
-      c = o.scope.freeVariable()
+      a = o.scope.freeVariable 'klass'
+      b = o.scope.freeVariable 'cache'
+      c = o.scope.freeVariable 'cache'
       """
       #{@first}(function() {
       #{@idt(1)}var ctor = function(){};
       #{@idt(1)}__extends(ctor, #{a} = #{meth});
-      #{@idt(1)}return typeof (#{b} = #{a}.apply(#{c} = new ctor, #{ @compileSplatArguments(o) })) === "object" ? #{b} : #{c};
+      #{@idt(1)}return typeof (#{c} = #{a}.apply(#{b} = new ctor, #{ @compileSplatArguments(o) })) === "object" ? #{c} : #{b};
       #{@tab}}).#{ if mentionsArgs then 'apply(this, arguments)' else 'call(this)'}#{@last}
       """
     else
@@ -624,8 +624,8 @@
       range = [+@fromNum..+@toNum]
       range.pop() if @exclusive
       return "[#{ range.join(', ') }]"
-    i = o.scope.freeVariable()
-    result = o.scope.freeVariable()
+    i = o.scope.freeVariable 'index'
+    result = o.scope.freeVariable 'result'
     pre    = "\n#{idt}#{result} = []; #{vars}"
     if @fromNum and @toNum
       o.index = i
@@ -743,7 +743,7 @@
   # equivalent syntax tree and compile that, in pieces. You can see the
   # constructor, property assignments, and inheritance getting built out below.
   compileNode: (o) ->
-    @variable  = literal o.scope.freeVariable() if @variable is '__temp__'
+    @variable  = literal o.scope.freeVariable 'klass' if @variable is '__temp__'
     extension  = @parent and new ExtendsNode(@variable, @parent)
     props      = new Expressions
     o.top      = true
@@ -775,7 +775,7 @@
         else
           func.bound = false
           constScope or= new Scope(o.scope, constructor.body, constructor)
-          me or= constScope.freeVariable()
+          me or= constScope.freeVariable 'self'
           pname = pvar.compile(o)
           constructor.body.push    new ReturnNode literal 'this' if constructor.body.empty()
           constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#{me}, arguments); }"
@@ -851,7 +851,7 @@
   # See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
   # for details.
   compilePatternMatch: (o) ->
-    valVar        = o.scope.freeVariable()
+    valVar        = o.scope.freeVariable 'cache'
     value         = if @value.isStatement(o) then ClosureNode.wrap(@value) else @value
     assigns       = ["#{@tab}#{valVar} = #{ value.compile(o) };"]
     o.top         = true
@@ -937,7 +937,7 @@
       else
         if param.attach
           {value} = param
-          [param, param.splat] = [literal(o.scope.freeVariable()), param.splat]
+          [param, param.splat] = [literal(o.scope.freeVariable 'arg'), param.splat]
           @body.unshift new AssignNode new ValueNode(literal('this'), [new AccessorNode value]), param
         if param.splat
           splat           = new SplatNode param.value
@@ -1011,15 +1011,15 @@
     o.scope.find name
     end = ''
     if @trailings.length
-      len = o.scope.freeVariable()
+      len = o.scope.freeVariable 'cache'
       o.scope.assign len, "arguments.length"
-      variadic = o.scope.freeVariable()
+      variadic = o.scope.freeVariable 'result'
       o.scope.assign variadic, len + ' >= ' + @arglength
       end = if @trailings.length then ", #{len} - #{@trailings.length}"
       for trailing, idx in @trailings
         if trailing.attach
           assign        = trailing.assign
-          trailing      = literal o.scope.freeVariable()
+          trailing      = literal o.scope.freeVariable 'arg'
           assign.value  = trailing
         pos = @trailings.length - idx
         o.scope.assign(trailing.compile(o), "arguments[#{variadic} ? #{len} - #{pos} : #{@index + idx}]")
@@ -1091,7 +1091,7 @@
     cond     =  @condition.compile(o)
     set      =  ''
     unless top
-      rvar  = o.scope.freeVariable()
+      rvar  = o.scope.freeVariable 'result'
       set   = "#{@tab}#{rvar} = [];\n"
       @body = PushNode.wrap(rvar, @body) if @body
     pre     = "#{set}#{@tab}while (#{cond})"
@@ -1227,7 +1227,7 @@

   compileLoopTest: (o) ->
     [@arr1, @arr2] = @array.compileReference o, precompile: yes
-    [i, l] = [o.scope.freeVariable(), o.scope.freeVariable()]
+    [i, l] = [o.scope.freeVariable('index'), o.scope.freeVariable('cache')]
     prefix = if @obj1 isnt @obj2 then @obj1 + '; ' else ''
     "(function(){ #{prefix}for (var #{i}=0, #{l}=#{@arr1}.length; #{i}<#{l}; #{i}++) { if (#{@arr2}[#{i}] === #{@obj2}) return true; } return false; }).call(this)"

@@ -1385,12 +1385,12 @@
     source        = if range then @source.base else @source
     codeInBody    = @body.contains (n) -> n instanceof CodeNode
     scope         = o.scope
-    name          = (@name and @name.compile(o)) or scope.freeVariable()
+    name          = (@name and @name.compile(o)) or scope.freeVariable 'index'
     index         = @index and @index.compile o
     scope.find(name,  immediate: yes) if name and not @pattern and (range or not codeInBody)
     scope.find(index, immediate: yes) if index
-    rvar          = scope.freeVariable() unless topLevel
-    ivar          = if codeInBody then scope.freeVariable() else if range then name else index or scope.freeVariable()
+    rvar          = scope.freeVariable 'result' unless topLevel
+    ivar          = if codeInBody then scope.freeVariable 'index' else if range then name else index or scope.freeVariable 'index'
     varPart       = ''
     guardPart     = ''
     body          = Expressions.wrap([@body])
@@ -1398,14 +1398,14 @@
       sourcePart  = source.compileVariables(o)
       forPart     = source.compile merge o, index: ivar, step: @step
     else
-      svar        = scope.freeVariable()
+      svar        = scope.freeVariable 'cache'
       sourcePart  = "#{svar} = #{ @source.compile(o) };"
       if @pattern
         namePart  = new AssignNode(@name, literal("#{svar}[#{ivar}]")).compile(merge o, {indent: @idt(1), top: true}) + '\n'
       else
         namePart  = "#{name} = #{svar}[#{ivar}]" if name
       unless @object
-        lvar      = scope.freeVariable()
+        lvar      = scope.freeVariable 'cache'
         stepPart  = if @step then "#{ivar} += #{ @step.compile(o) }" else "#{ivar}++"
         forPart   = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
     sourcePart    = (if rvar then "#{rvar} = []; " else '') + sourcePart


Never return from a constructor.

Never return from a constructor.

--- nodes.coffee
+++ nodes.coffee
@@ -862,7 +862,7 @@
     if not @ctor
       @ctor = new Code
       @ctor.body.push new Literal "#{name}.__super__.constructor.apply(this, arguments)" if @parent
-      @ctor.body.push new Literal "return #{@externalCtor}.apply(this, arguments)" if @externalCtor
+      @ctor.body.push new Literal "#{@externalCtor}.apply(this, arguments)" if @externalCtor
       @body.expressions.unshift @ctor
     @ctor.ctor     = @ctor.name = name
     @ctor.klass    = null


Merging in Stephank's fix for #692.

Merging in Stephank's fix for #692.

--- nodes.coffee
+++ nodes.coffee
@@ -454,14 +454,15 @@
     if @exist
       if @variable instanceof ValueNode and @variable.properties[@variable.properties.length - 1] instanceof AccessorNode
         methodAccessor = @variable.properties.pop()
-        [@first, @meth] = @variable.compileReference o
-        @first = new ValueNode(@first, [methodAccessor]).compile o
-        @meth = new ValueNode(@meth, [methodAccessor]).compile o
+        [first, meth] = @variable.compileReference o
+        @first = new ValueNode(first, [methodAccessor]).compile o
+        @meth = new ValueNode(meth, [methodAccessor]).compile o
       else
         [@first, @meth] = @variable.compileReference o, precompile: yes
       @first = "(typeof #{@first} === \"function\" ? "
       @last  = " : undefined)"
-    else if @variable then @meth = @variable.compile o
+    else if @variable
+      @meth = @variable.compile o
     for arg in @args when arg instanceof SplatNode
       code = @compileSplat(o)
     if not code


safer splats with __slice where necessary.

--- nodes.coffee
+++ nodes.coffee
@@ -78,7 +78,7 @@
   # Construct a node that returns the current node's result.
   # Note that this is overridden for smarter behavior for
   # many statement nodes (eg If, For)...
-  makeReturn: ->
+  makeReturn: ->
     new Return this

   # Does this node, or any of its children, contain a node of a certain kind?
@@ -130,7 +130,7 @@
       return false if (arg = func child, arg) is false
       child.traverseChildren crossScope, func, arg

-  invert: ->
+  invert: ->
     new Op '!', this

   unwrapAll: ->
@@ -174,7 +174,7 @@
     this

   # Remove and return the last expression of this expression list.
-  pop: ->
+  pop: ->
     @expressions.pop()

   # Add an expression at the beginning of this expression list.
@@ -184,11 +184,11 @@

   # If this Expressions consists of just a single node, unwrap it by pulling
   # it back out.
-  unwrap: ->
+  unwrap: ->
     if @expressions.length is 1 then @expressions[0] else this

   # Is this an empty block of code?
-  isEmpty: ->
+  isEmpty: ->
     not @expressions.length

   # An Expressions node does not return its entire body, rather it
@@ -256,26 +256,26 @@

   constructor: (@value) ->

-  makeReturn: ->
+  makeReturn: ->
     if @isStatement() then this else super()

   # Break and continue must be treated as pure statements -- they lose their
   # meaning when wrapped in a closure.
-  isPureStatement: ->
+  isPureStatement: ->
     @value in ['break', 'continue', 'debugger']

-  isAssignable: ->
+  isAssignable: ->
     IDENTIFIER.test @value

   isComplex: NO

-  assigns: (name) ->
+  assigns: (name) ->
     name is @value

-  compile: ->
+  compile: ->
     if @value.reserved then "\"#{@value}\"" else @value

-  toString: ->
+  toString: ->
     ' "' + @value + '"'

 #### Return
@@ -321,7 +321,7 @@
     @properties.push prop
     this

-  hasProperties: ->
+  hasProperties: ->
     !!@properties.length

   # Some boolean checks for the benefit of other nodes.
@@ -338,12 +338,12 @@
   isStatement : (o)    -> not @properties.length and @base.isStatement o
   assigns     : (name) -> not @properties.length and @base.assigns name

-  makeReturn: ->
+  makeReturn: ->
     if @properties.length then super() else @base.makeReturn()

   # The value can be unwrapped as its inner node, if there are no attached
   # properties.
-  unwrap: ->
+  unwrap: ->
     if @properties.length then this else @base

   # A reference has base part (`this` value) and name part.
@@ -405,7 +405,7 @@

   makeReturn: THIS

-  compileNode: (o) ->
+  compileNode: (o) ->
     @tab + '/*' + multident(@comment, @tab) + '*/'

 #### Call
@@ -472,8 +472,8 @@
   # Compile a vanilla function call.
   compileNode: (o) ->
     @variable?.front = @front
-    for arg in @args when arg instanceof Splat
-      return @compileSplat o
+    if code = Splat.compileSplattedArray o, @args, true
+      return @compileSplat o, code
     args = (arg.compile o, LEVEL_LIST for arg in @args).join ', '
     if @isSuper
       @compileSuper args, o
@@ -489,9 +489,8 @@
   # `.apply()` call to allow an array of arguments to be passed.
   # If it's a constructor, then things get real tricky. We have to inject an
   # inner constructor in order to be able to pass the varargs.
-  compileSplat: (o) ->
-    splatargs = Splat.compileSplattedArray @args, o
-    return "#{ @superReference o }.apply(this, #{splatargs})" if @isSuper
+  compileSplat: (o, splatArgs) ->
+    return "#{ @superReference o }.apply(this, #{splatArgs})" if @isSuper
     unless @isNew
       base = new Value @variable
       if (name = base.properties.pop()) and base.isComplex()
@@ -500,14 +499,14 @@
       else
         fun = ref = base.compile o, LEVEL_ACCESS
         fun += name.compile o if name
-      return "#{fun}.apply(#{ref}, #{splatargs})"
+      return "#{fun}.apply(#{ref}, #{splatArgs})"
     idt = @idt 1
     """
     (function(func, args, ctor) {
     #{idt}ctor.prototype = func.prototype;
     #{idt}var child = new ctor, result = func.apply(child, args);
     #{idt}return typeof result === "object" ? result : child;
-    #{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatargs}, function() {})
+    #{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
     """

 #### Extends
@@ -555,7 +554,7 @@
   compile: (o) ->
     (if @proto then '.prototype' else '') + "[#{ @index.compile o, LEVEL_PAREN }]"

-  isComplex: ->
+  isComplex: ->
     @index.isComplex()

 #### Obj
@@ -631,8 +630,7 @@

   compileNode: (o) ->
     o.indent = @idt 1
-    for obj in @objects when obj instanceof Splat
-      return Splat.compileSplattedArray @objects, o
+    return code if code = Splat.compileSplattedArray o, @objects
     objects = []
     for obj, i in @objects
       code = obj.compile o, LEVEL_LIST
@@ -750,7 +748,7 @@
   assigns: (name) ->
     @[if @context is 'object' then 'value' else 'variable'].assigns name

-  unfoldSoak: (o) ->
+  unfoldSoak: (o) ->
     If.unfoldSoak o, this, 'variable'

   # Compile an assignment, delegating to `compilePatternMatch` or
@@ -931,7 +929,7 @@

   constructor: (@name, @value, @splat) ->

-  compile: (o) ->
+  compile: (o) ->
     @name.compile o, LEVEL_LIST

   asReference: (o) ->
@@ -941,7 +939,7 @@
     node = new Splat node if @splat
     @reference = node

-  isComplex: ->
+  isComplex: ->
     @name.isComplex()

 #### Splat
@@ -957,30 +955,31 @@
   constructor: (name) ->
     @name = if name.compile then name else new Literal name

-  assigns: (name) ->
+  assigns: (name) ->
     @name.assigns name

-  compile: (o) ->
+  compile: (o) ->
     if @index? then @compileParam o else @name.compile o

   # Utility function that converts arbitrary number of elements, mixed with
-  # splats, to a proper array
-  @compileSplattedArray: (list, o) ->
-    args = []
-    end  = -1
-    for arg, i in list
-      code = arg.compile o, LEVEL_LIST
-      prev = args[end]
-      if arg not instanceof Splat
-        if prev and starts(prev, '[') and ends(prev, ']')
-          args[end] = "#{prev.slice 0, -1}, #{code}]"
-          continue
-        if prev and starts(prev, '.concat([') and ends(prev, '])')
-          args[end] = "#{prev.slice 0, -2}, #{code}])"
-          continue
-        code = "[#{code}]"
-      args[++end] = if i is 0 then code else ".concat(#{code})"
-    args.join ''
+  # splats, to a proper array.
+  @compileSplattedArray: (o, list, apply) ->
+    index = -1
+    continue while (node = list[++index]) and node not instanceof Splat
+    return '' if index >= list.length
+    if list.length is 1
+      code = list[0].compile o, LEVEL_LIST
+      return code if apply
+      return "#{ utility 'slice' }.call(#{code})"
+    args = list.slice index
+    for node, i in args
+      code = node.compile o, LEVEL_LIST
+      args[i] = if node instanceof Splat
+      then "#{ utility 'slice' }.call(#{code})"
+      else "[#{code}]"
+    return args[0] + ".concat(#{ args.slice(1).join ', ' })" if index is 0
+    base = (node.compile o, LEVEL_LIST for node in list.slice 0, index)
+    "[#{ base.join ', ' }].concat(#{ args.join ', ' })"

 #### While

@@ -1056,12 +1055,12 @@
     @second   = second
     @flip     = !!flip

-  isUnary: ->
+  isUnary: ->
     not @second

   # Am I capable of
   # [Python-style comparison chaining](http://docs.python.org/reference/expressions.html#notin)?
-  isChainable: ->
+  isChainable: ->
     @operator in ['<', '>', '>=', '<=', '===', '!==']

   invert: ->
@@ -1113,7 +1112,7 @@
     parts.reverse() if @flip
     parts.join ''

-  toString: (idt) ->
+  toString: (idt) ->
     super idt, @constructor.name + ' ' + @operator

 #### In
@@ -1197,7 +1196,7 @@
   # A **Throw** is already a return, of sorts...
   makeReturn: THIS

-  compileNode: (o) ->
+  compileNode: (o) ->
     @tab + "throw #{ @expression.compile o };"

 #### Existence
@@ -1447,7 +1446,7 @@
     code = "#{cond} ? #{body} : #{alt}"
     if o.level >= LEVEL_COND then "(#{code})" else code

-  unfoldSoak: ->
+  unfoldSoak: ->
     @soak and this

   # Unfold a node's child if soak, then tuck the node under created `If`
@@ -1568,5 +1567,5 @@
   Scope.root.assign ref, UTILITIES[name]
   ref

-multident = (code, tab) ->
+multident = (code, tab) ->
   code.replace /\n/g, '$&' + tab


Keep a handle to the exports so that the code can invoke functions it exports

--- tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pageinit.coffee
+++ tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pageinit.coffee
@@ -27,34 +27,34 @@
       fn = if functionName? then moduleLib[functionName] else moduleLib
       fn.apply null, initArguments

+  exports =
+    # Passed a list of initializers, executes each initializer in order. Due to asynchronous loading
+    # of modules, the exact order in which initializer functions are invoked is not predictable.
+    initialize: (inits) ->
+      # apply will split the first value from the rest for us, implicitly.
+      invokeInitializer.apply null, init for init in inits

-  # Passed a list of initializers, executes each initializer in order. Due to asynchronous loading
-  # of modules, the exact order in which initializer functions are invoked is not predictable.
-  initialize: (inits) ->
-    # apply will split the first value from the rest for us, implicitly.
-    invokeInitializer.apply null, init for init in inits
+    # Pre-loads a number of scripts in order. When the last script is loaded,
+    # invokes the callback (with no parameters).
+    loadScripts: (scripts, callback) ->
+      reducer = (callback, script) -> ->
+        console.debug "Loading script #{script}"
+        require [script], callback

-  # Pre-loads a number of scripts in order. When the last script is loaded,
-  # invokes the callback (with no parameters).
-  loadScripts: (scripts, callback) ->
-    reducer = (callback, script) -> ->
-      console.debug "Loading script #{script}"
-      require [script], callback
+      finalCallback = _.reduceRight scripts, reducer, callback

-    finalCallback = _.reduceRight scripts, reducer, callback
+      finalCallback.call(null)

-    finalCallback.call(null)
+    # Loads all the scripts, in order. It then executes the immediate initializations.
+    # After that, it waits for the DOM to be ready and executes the other initializations.
+    loadScriptsAndInitialize: (scripts, immediateInits, otherInits) ->
+      exports.loadScripts scripts, ->
+        exports.initialize immediateInits
+        require ["core/domReady!"], -> exports.initialize otherInits

-  # Loads all the scripts, in order. It then executes the immediate initializations.
-  # After that, it waits for the DOM to be ready and executes the other initializations.
-  loadScriptsAndInitialize: (scripts, immediateInits, otherInits) ->
-    this.loadScripts scripts, ->
-      this.initialize immediateInits
-      require ["core/domReady!"], -> this.initialize otherInits
-
-  evalJavaScript: (js) ->
-    console.debug "Evaluating: #{js}"
-    eval js
+    evalJavaScript: (js) ->
+      console.debug "Evaluating: #{js}"
+      eval js


one small change

one small change

--- lib/connections.coffee
+++ lib/connections.coffee
@@ -41,11 +41,12 @@
     return

   # None of the above, throw them into the assignment mechanism
-  TurkServer.assignUser(userId)
+  if Batches.findOne(active: true).lobby
+    TurkServer.addToLobby(userId)
+  else
+    TurkServer.assignUser(userId)

 TurkServer.assignUser = (userId) ->

-  # TODO temporary; let's do something better organized later; this was causing dup key issues
-  # Add user to the lobby
-  TurkServer.addToLobby(userId)

+


If there is nothing to embed in data provided from form do not add link.

--- workers/social/lib/social/models/messages/statusupdate/index.coffee
+++ workers/social/lib/social/models/messages/statusupdate/index.coffee
@@ -25,12 +25,13 @@

   @create = secure (client, data, callback)->
-    statusUpdate=
+    statusUpdate  =
       meta        : data.meta
       title       : data.title
       body        : data.body

-      link                      :
+    if data.link_url and data.link_embed
+      statusUpdate.link         =
         link_url                : data.link_url
         link_embed              : data.link_embed
         link_embed_hidden_items : data.link_embed_hidden_items
@@ -44,7 +45,8 @@
       title       : data.title
       body        : data.body

-      link                      :
+    if data.link_url and data.link_embed
+      statusUpdate.link         =
         link_url                : data.link_url
         link_embed              : data.link_embed
         link_embed_hidden_items : data.link_embed_hidden_items


tutorial list: ISTM this needs a group slug

--- workers/social/lib/social/models/messages/tutoriallist/index.coffee
+++ workers/social/lib/social/models/messages/tutoriallist/index.coffee
@@ -57,6 +57,7 @@
       repliesCount  :
         type        : Number
         default     : 0
+      group         : String

     relationships   :
       tutorial      :


将判断是否是系统管理员的操作进一步抽象 req.isAdmin 用来标记是否是管理员

--- lib/helper/rest.coffee
+++ lib/helper/rest.coffee
@@ -96,7 +96,7 @@
       cols = cols or Model.editableCols or Model.writableCols
       # 当设置了只有管理员才可以修改的字段，并且当前用户不是管理员
       # 则去掉那些只有管理员才能修改的字段
-      if Model.onlyAdminCols and req.user.role isnt 'admin'
+      if Model.onlyAdminCols and (req.isAdmin is yes)
         cols = _.filter(cols, (x) -> x not in Model.onlyAdminCols)
       attr = utils.pickParams(req, cols, Model)
       delete attr.id


moving towards bootstrappified view

moving towards bootstrappified view

--- source/javascripts/src/view_detail_mixins.coffee
+++ source/javascripts/src/view_detail_mixins.coffee
@@ -4,15 +4,21 @@
   VX.label = VX.hint =
     html: ->
       """
-

-        #{@model.get("value")}
+

+

+          #{@model.get("value")}
+

       """
-    insertInDOM: (rowView)->
-      rowView.$el.prepend(@el)
+    insertInDOM: (rowView$el)->
+      rowView$el.find(".row-content").eq(0).prepend(@$el)
+
     afterRender: ->
-      @$el.find("div").eq(0).editInPlace
+      @$el.find("blockquote").eq(0).editInPlace
         save_if_nothing_changed: true
+        field_type: "textarea"
+        textarea_cols: 50
+        textarea_rows: 3
         callback: (uu, ent)=>
           @model.set("value", ent)
           if ent is "" then "..." else ent
@@ -73,61 +79,9 @@
       # type: #{typeId}
       # """
       if rtp.specifyChoice
-        # listName = @model.get("list")?.get("name")
-        # numChoices = @model.parentRow._parent.choices.models.length
-        # if listName and numChoices is 1
-        #   # in this case, there is nothing to choose from, so no dropdown.
-        #   htmlStr += """ from
-        #       #{listName}
-        #       <button class="edit-list" data-list-name="#{listName}">Edit</button>
-        #       or
-        #       <button class="create-new-list">create a list</button>
-        #     """
-        # else if listName
-        #   htmlStr += """ from
-        #       #{listName}
-        #       <button class="edit-list" data-list-name="#{listName}">Edit</button>
-        #       or
-        #       <button class="create-new-list">create a list</button>
-        #     """
-        # else if numChoices is 0
-        #   htmlStr += """ from
-        #       <button class="create-new-list choice-list-anchor">create a list</button>
-        #     """
-        # else
-        #   htmlStr += """ from choose a list"""
-        @$el.append new XLF.ManageListView({rowView: @rowView}).render().$el
+        @mlv = new XLF.ManageListView({rowView: @rowView})
+        @$el.find("form").eq(0).append @mlv.render().$el

-      typeSelectCb = (unused, entered)=>
-        @model.set("value", entered)
-      # $sel = $("<select>", class: "inplace_field")
-      # $og = $("<optgroup>", label: "Field types")
-      # for [label, name] in type_select_options
-      #   $og.append $("<option>", value: name, text: label)
-      # $sel.append $og
-
-      # eipOpts =
-      #   field_type: "select"
-      #   select_elem: $sel
-      #   callback: typeSelectCb
-
-      # @$(".select-tp").editInPlace(eipOpts)
-
-      # survey = @model.parentRow._parent
-      # $lsel = $("<select>", class: "inplace_field")
-      # for c in survey.choices.models
-      #   $lsel.append $("<option>", text: c.get("name"), value: c.get("name"))
-
-      changeListCb = (un, ent)=>
-        list = @model.parentRow._parent.choices.get(ent)
-        @model.set("list", list)
-        "#{ent}"
-      # selectListOpts =
-      #   field_type: "select"
-      #   select_elem: $lsel
-      #   callback: changeListCb
-
-      # @$(".select-list").editInPlace(selectListOpts)
       @$el

   VX


Update models/list.coffee

Update models/list.coffee

--- lib/models/list.coffee
+++ lib/models/list.coffee
@@ -8,36 +8,36 @@

 module.exports =
 class List extends Collection
-  selected_index: 0
-  is_sublist: false
+  selectedIndex: 0
+  isSublist: false

   leaf: ->
     if @selection()
       @selection().leaf()

   selection: ->
-    @at @selected_index
+    @at @selectedIndex

   select: (i) ->
-    old_selection = @selected_index
+    old_selection = @selectedIndex
     if @selection()
       @selection().deselect()

-    if @is_sublist and i < 0
-      @selected_index = -1
+    if @isSublist and i < 0
+      @selectedIndex = -1
       return false

-    @selected_index = Math.max(Math.min(i, @length - 1), 0)
+    @selectedIndex = Math.max(Math.min(i, @length - 1), 0)

     if @selection()
       @selection().select()

-    old_selection != @selected_index
+    old_selection != @selectedIndex

   next: ->
     return false if @selection() and not @selection().allowNext()
-    @select (@selected_index + 1)
+    @select (@selectedIndex + 1)

   previous: ->
     return false if @selection() and not @selection().allowPrevious()
-    @select(@selected_index - 1)
+    @select(@selectedIndex - 1)


Destroy moduleInfo on process destroy (#75)

--- lib/module-info.coffee
+++ lib/module-info.coffee
@@ -19,7 +19,8 @@
       @disposables = new CompositeDisposable
       @disposables.add @emitter = new Emitter
       @update rootPath, done
-      @timeout = setTimeout (=> @destroy), @invalidateInterval
+      @timeout = setTimeout (=> @destroy()), @invalidateInterval
+      @disposables.add @process.onDidDestroy => @destroy()

     destroy: =>
       return unless @symbols?


Wrap commit insert with try/catch block

Wrap commit insert with try/catch block

Thanks  @qejk !

--- source/server/infrastructure/commit_store.coffee
+++ source/server/infrastructure/commit_store.coffee
@@ -54,7 +54,13 @@

       # insert commit with next version
       @log.debug(@_logMsg("Inserting commit"), commit)
-      commitId = @commits.insert commit
+      try
+        commitId = @commits.insert commit
+      catch error
+        throw new Error "While inserting:\n
+          #{JSON.stringify(commit)}\n
+          error:#{error.message}\n
+          stack:#{error.stack}"

       @commitPublisher.publishCommit
         _id: commitId,


Fix feature specs

Fix feature specs

--- spec/feature/example_bounded_context.spec.coffee
+++ spec/feature/example_bounded_context.spec.coffee
@@ -12,7 +12,7 @@
     beforeEach ->
       exampleContext = eventric.boundedContext()
       exampleContext.set 'store', eventStoreMock
-      exampleContext.addAggregate 'Example', {}
+      exampleContext.addAggregate 'Example', class Example

     describe 'when we command the bounded context to create an aggregate', ->
@@ -41,7 +41,7 @@
             name: 'Example'
         ]

-        exampleContext.addAggregate 'Example',
+        exampleContext.addAggregate 'Example', class Example
           doSomething: sinon.stub()

         exampleContext.addCommands


better error handling on http request

better error handling on http request

--- lib/phabricator.coffee
+++ lib/phabricator.coffee
@@ -33,12 +33,32 @@
       .path("api/#{endpoint}")
       .get(body) (err, res, payload) ->
         json_body = null
-        switch res.statusCode
-          when 200 then json_body = JSON.parse(payload)
-          else
-            console.log res.statusCode
-            console.log payload
-            json_body = { message: 'Fail' }
+        if res?
+          switch res.statusCode
+            when 200
+              if res.headers['content-type'] is 'application/json'
+                json_body = JSON.parse(payload)
+              else
+                json_body = {
+                  result: {
+                    error_code: 'NOTJSON',
+                    error_info: 'api did not deliver json'
+                  }
+                }
+            else
+              json_body = {
+                result: {
+                  error_code: res.statusCode,
+                  error_info: msg.http.STATUS_CODES[res.statusCode]
+                }
+              }
+        else
+          json_body = {
+            result: {
+              error_code: err.code,
+              error_info: err.message
+            }
+          }
         cb json_body


working commit.

working commit.

--- lib/ruby-turnip.coffee
+++ lib/ruby-turnip.coffee
@@ -47,13 +47,44 @@
     stepRegexp = /step\s+(.+)\s+do/
     scopeRegexp = /steps_for\s+(.+)\s+do/
     options = {paths: ["spec/steps/**/*.rb"]}
+
+    # 現在行を取得
+    row = atom.workspace.getActiveTextEditor().getCursorBufferPosition().row
+    currentLine = atom.workspace.getActiveTextEditor().lineTextForBufferRow(row)
+
+    # 対象文字列を取得
+    target = currentLine.match(/(\S+)(\s+)(.+)/)[3]
+
+    # タグを取得
+    tags = []
+    atom.workspace.getActiveTextEditor().scan /(@\S+)/g, ({matchText}) =>
+      tags.unshift(matchText.replace(/^@/, ""))
+    console.log(tags)
+
+    # スコープのリストを作成
     scopeList = []
-    promise = atom.workspace.scan scopeRegexp, options, (match) ->
+    promiseScope = atom.workspace.scan scopeRegexp, options, (match) ->
       for i, value of match.matches
-        scopeList.push({path: match.filePath, name: value.matchText, lineNo: value.range[0][0]})
+        name = value.matchText.match(/steps_for\s+(.+)\s+do/)[1]
+        scopeList.push({path: match.filePath, name: name.replace(/:/,""), lineNo: value.range[0][0]})

-    # Todo: search steps
-
-    Promise.all([promise]).then =>
-      for i, value of scopeList
-        console.log value
+    # ステップのリストを作成
+    stepList = []
+    promiseStep = atom.workspace.scan stepRegexp, options, (match) ->
+      for i, value of match.matches
+        step = value.matchText.match(/step\s+(.+)\s+do/)[1]
+        step = step.replace(/^'/g,'').replace(/^"/g,'').replace(/'$/g,'').replace(/"$/g,'')
+        step = step.replace(/:.+?\s/g,".+\s")
+        stepList.push({path: match.filePath, name: step, lineNo: value.range[0][0], scope: ""})
+
+    # 2つのリスト作成を待つ
+    Promise.all([promiseScope, promiseStep]).then =>
+      # スコープの設定を行う
+      # TODO: scopeListで回して、行番号以降を同じステップで塗り替える
+      for i, scope of scopeList
+        results = (step for step in stepList when step.path is scope.path && step.lineNo > scope.lineNo)
+        for j, result of results
+          result.scope = scope.name
+
+      for i, step of stepList
+        console.log step


Stop selecting the js output

Stop selecting the js output

--- demos/visualizer-lazy/visualizer.coffee
+++ demos/visualizer-lazy/visualizer.coffee
@@ -34,7 +34,9 @@
 	grid?.destroy()
 	grid = new viz.Grid playfield, lazyRuntime.pathSet, document.getElementById 'can'
 	grid.setListener (path) ->
-		editors.js.setValue path?.code ? ''
+		code = path?.code ? ''
+		editors.js.setValue code, -1
+		return

 do ->


adjust the user ctrl braodcast frequency

--- app/angular/controllers.coffee
+++ app/angular/controllers.coffee
@@ -64,7 +64,7 @@
 			angle: curAngle
 		Socket.ctrl.emit 'user_ctrl', ctrlData
 		console.log "broadcasting state", ctrlData
-		$timeout broadcastUserState, 1000
+		$timeout broadcastUserState, 250

 	resume = ->
 		paused = false


Escape project.name in invitation page

Escape project.name in invitation page

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -364,7 +364,7 @@
             $analytics.trackEvent("auth", "invitationAccept", "invitation accept with existing user", 1)
             $location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
             $confirm.notify("success", "You've successfully joined this project",
-                                       "Welcome to #{$scope.invitation.project_name}")
+                                       "Welcome to #{_.escape($scope.invitation.project_name)}")

         onErrorSubmitLogin = (response) ->
             $confirm.notify("light-error", "According to our Oompa Loompas, your are not registered yet or
@@ -393,7 +393,7 @@
             $analytics.trackEvent("auth", "invitationAccept", "invitation accept with new user", 1)
             $location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
             $confirm.notify("success", "You've successfully joined this project",
-                                       "Welcome to #{$scope.invitation.project_name}")
+                                       "Welcome to #{_.escape($scope.invitation.project_name)}")

         onErrorSubmitRegister = (response) ->
             $confirm.notify("light-error", "According to our Oompa Loompas, that


Correct typos in spec description

Correct typos in spec description

--- spec/spell-check-spec.coffee
+++ spec/spell-check-spec.coffee
@@ -71,7 +71,7 @@
         atom.config.set('spell-check.grammars', [])
         expect(editorView.find('.misspelling').length).toBe 0

-  describe "when the editor's grammar changes to one that is does now have spell check eanbled", ->
+  describe "when the editor's grammar changes to one that does not have spell check enabled", ->
     it "removes all the misspellings", ->
       editorView.setText('notaword')
       advanceClock(editorView.getEditor().getBuffer().stoppedChangingDelay)