Remove unused requires

Remove unused requires

--- src/cursor-view.coffee
+++ src/cursor-view.coffee
@@ -1,5 +1,4 @@
 {View} = require './space-pen-extensions'
-{Point, Range} = require 'text-buffer'
 _ = require 'underscore-plus'

 module.exports =


mod 2015.02.13

mod 2015.02.13

--- scripts/randompop.coffee
+++ scripts/randompop.coffee
@@ -1,8 +1,16 @@
+# 主に発表者をランダムで指定する
+# Description:
+# 発表者のリストを作成し、その中からランダムで発表者を選ぶ.
 #
-# 主に発表者をランダムで指定する
+# Commands:
+# presenter show - リストに登録されている発表者を表示する
+# presenter add - 発表者をリストに登録する
+# presenter del <num>- リストの<num>番目に登録している発表者をリストから削除する
+# presenter select - 発表者を選ぶ
+# presenter clear - リストを空にする
 #

-happyo = []
+presenterList = []

 {TextListener} = require 'hubot/src/listener'

@@ -11,26 +19,64 @@
     index = robot.listeners.indexOf listener
     robot.listeners.splice index, 1 if index != 1

+  waitForAnswer = (waitMsg, callback) ->
+    userId = waitMsg.envelope.user.id
+    room = waitMsg.envelope.room
+    listener = new TextListener robot, /.*/, (msg) ->
+      {envelope} = msg
+      if envelope.room is room and envelope.user.id is userId
+        callback.apply null, arguments
+        removeListener listener
+    robot.listeners.push listener
+
   waitForResponse = (waitMsg, callback) ->
     userId = waitMsg.envelope.user.id
     room = waitMsg.envelope.room
     listener = new TextListener robot, /.*/, (msg) ->
       {envelope} = msg
       if envelope.room is room and envelope.user.id is userId
-        removeListener listener
         if msg.envelope.message.text.toLowerCase() is '<end>'
-          waitMsg.reply '追加しました'
+          removeListener listener
         else
           callback.apply null, arguments
     robot.listeners.push listener

-  robot.respond /発表者\s+一覧/i, (msg) ->
-    msg.send '発表者一覧'
-    for name, idx in happyo
-      msg.send '  (#{idx})#{name}'
+  robot.respond /presenter\s+show/i, (msg) ->
+    msg.send '発表者リスト'
+    for name, idx in presenterList
+      msg.send " #{idx + 1}:#{name} さん"

-  robot.respond /発表者\s+追加/i, (msg) ->
-    msg.reply '発表者を追加します。終了する場合は <end> と入力してください。'
+  robot.respond /presenter\s+add/i, (msg) ->
+    msg.reply '発表者をリストに追加します。終了する場合は <end> と入力してください。'
     waitForResponse msg, (msg) ->
-      msg.reply '? '
+      presenter = msg.envelope.message.text
+      presenterList.push(presenter)
+      msg.reply "'#{presenter}' さんをリストに追加しました。"
+      msg.reply '続けて発表者をリストに追加します。終了する場合は <end> と入力してください。'

+  robot.respond /presenter\s+del\s+([0-9]+)/i, (msg) ->
+    idx = parseInt(msg.match[1]) - 1
+    presenter = presenterList[idx]
+    presenterList.splice idx, 1
+    msg.reply "'#{presenter}' さんをリストから削除しました。"
+
+  robot.respond /presenter\s+select/i, (msg) ->
+    if presenterList.length == 0
+      msg.reply "リストが空です"
+    else
+      idx = Math.floor(Math.random() * presenterList.length)
+      presenter = presenterList[idx]
+      presenterList.splice idx, 1
+      msg.reply "'#{presenter}'さん、お願いします！"
+
+  robot.respond /presenter\s+clear/i, (msg) ->
+    msg.reply "リストをクリアしますか？(Y/n)"
+    waitForAnswer msg, (msg) ->
+      ans = msg.envelope.message.text
+      if ans is "Y"
+        presenterList = []
+        msg.reply "リストをクリアしました"
+      else if ans is "y"
+        msg.reply "Y を入力してください"
+
+


fix classes for types with spaces in the name

--- ui.coffee
+++ ui.coffee
@@ -185,11 +185,12 @@
       new Blaze.Template =>
         owner ?= Template.currentData().owner
         _.map @panel.contains, (typeName) =>
+          typeId = typeName.replace /\s/g, '-'
           type = @rules.getComponentType typeName
           unless type.isCounter
             cursor = @getContainer(owner).find type: typeName
             HTML.DIV class: ['game-panel-component-type', ' ',
-                             "game-panel-component-type-#{typeName}"], [
+                             "game-panel-component-type-#{typeId}"], [
               Blaze.Each (=> cursor), =>
                 component = Template.currentData()
                 type.render component


Limiting watched file compilation to once every 25ms

--- command.coffee
+++ command.coffee
@@ -168,16 +168,20 @@
 watch = (source, base) ->

   prevStats = null
+  compileTimeout = null

   compile = ->
-    fs.stat source, (err, stats) ->
-      throw err if err
-      return if prevStats and (stats.size is prevStats.size and
-        stats.mtime.getTime() is prevStats.mtime.getTime())
-      prevStats = stats
-      fs.readFile source, (err, code) ->
+    clearTimeout compileTimeout
+    compileTimeout = setTimeout ->
+      fs.stat source, (err, stats) ->
         throw err if err
-        compileScript(source, code.toString(), base)
+        return if prevStats and (stats.size is prevStats.size and
+          stats.mtime.getTime() is prevStats.mtime.getTime())
+        prevStats = stats
+        fs.readFile source, (err, code) ->
+          throw err if err
+          compileScript(source, code.toString(), base)
+    , 25

   watchErr = (e) ->
     if e.code is 'ENOENT'


style in command watch

style in command watch

--- command.coffee
+++ command.coffee
@@ -157,9 +157,8 @@
   return unless opts.join
   unless sourceCode.some((code) -> code is null)
     clearTimeout joinTimeout
-    joinTimeout = setTimeout ->
+    joinTimeout = wait 100, ->
       compileScript opts.join, sourceCode.join('\n'), opts.join
-    , 100

 # Load files that are to-be-required before compilation occurs.
 loadRequires = ->
@@ -222,7 +221,7 @@
   try
     watcher = fs.watch source, ->
       clearTimeout readdirTimeout
-      readdirTimeout = setTimeout ->
+      readdirTimeout = wait 25, ->
         fs.readdir source, (err, files) ->
           if err
             throw err unless err.code is 'ENOENT'
@@ -234,7 +233,6 @@
             sources.push file
             sourceCode.push null
             compilePath file, no, base
-      , 25
   catch e
     throw e unless e.code is 'ENOENT'


note too many elements

note too many elements

--- query_runner.coffee
+++ query_runner.coffee
@@ -204,6 +204,11 @@
   showShareUrl: -> true

   work: ->
+
+    if @dashboard.elements.length > 1
+      @reply("Dashboards with more than one element aren't currently supported for Slack commands.")
+      return
+
     for element in @dashboard.elements
       @replyContext.looker.client.get("looks/#{element.look_id}", (look) =>
         queryDef = look.query


Empty string for CU_BOX_SERVER is acceptable.

--- server/code/model/box.coffee
+++ server/code/model/box.coffee
@@ -59,7 +59,7 @@

   @endpoint: (server, name) ->
     proto_server = "https://#{server}"
-    if process.env.CU_BOX_SERVER?
+    if process.env.CU_BOX_SERVER
       proto_server = "http://#{process.env.CU_BOX_SERVER}"
     return "#{proto_server}/#{name}"


= merge =

= merge =

--- src/bacon.model.coffee
+++ src/bacon.model.coffee
@@ -137,7 +137,7 @@
       copy[key] = value
     copy

-  Bacon.Model
+  Bacon

 if module?
   Bacon = require("baconjs")


fixed another issue with the main coffeescript

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -44,7 +44,7 @@

 # Initialize jQuery
-ready = () ->
+ready = ->

 	#Navbar collapse load
 	$(".button-collapse").sideNav()


Resolve our modules conflict

Resolve our modules conflict

--- CakeNodeModules.coffee
+++ CakeNodeModules.coffee
@@ -1,12 +1,7 @@
 module.exports =
   our_modules : [
     "bongo","bongo-client","jraphical","jsonh","jspath","kite","koding-dnode","koding-dnode-protocol"
-<<<<<<< HEAD
-    "microemitter","mongoop","pistachio","pistachio-compiler","scrubber","sinkrow","processes","inflector","traverse",
-    "broker"
-=======
-    "microemitter","mongoop","pistachio","pistachio-compiler","scrubber","sinkrow","processes","inflector","traverse","amqp"
->>>>>>> 8fbb78b740cfcf536b71a5f1f62855cae0678b54
+    "microemitter","mongoop","pistachio","pistachio-compiler","scrubber","sinkrow","processes","inflector","traverse","amqp", "broker"
   ]
   npm_modules :
     bitly           : '1.2.3'


Flexible source

Flexible source

--- apps/articles/routes.coffee
+++ apps/articles/routes.coffee
@@ -105,7 +105,7 @@
       "#{sd.SAILTHRU_MASTER_LIST}": 1
     name: req.body.name
     vars:
-      source: 'editorial'
+      source: req.body.source or 'editorial'
       receive_editorial_email: true
       email_frequency: 'daily'
   , (err, response) ->


Navigation fix.

Navigation fix.

--- app/assets/javascripts/keyboard.js.coffee
+++ app/assets/javascripts/keyboard.js.coffee
@@ -39,11 +39,11 @@
         @selectColumn('entry-content')
       return

-    $(document).on 'click', '[data-behavior~=open_next_entry]', =>
+    $(document).on 'click', '[data-behavior~=open_next_entry]', (event) =>
       @setEnvironment()
       @item = @next
       @selectItem()
-      return
+      event.preventDefault()

   navigateShareMenu: (combo) ->
     # If share menu is showing intercept up down


Improved performance on mobile

Improved performance on mobile

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -5,6 +5,7 @@
 $(".btn").each ->
   $(this).on "click", (e) ->
     e.preventDefault()
+    $(this).blur()
     $.post "/tv/action",
       action: $(this).data("action")


[bugfix] mailbox deletion was broken

[bugfix] mailbox deletion was broken

--- server/models/account.coffee
+++ server/models/account.coffee
@@ -36,6 +36,7 @@
 Message     = require './message'
 {AccountConfigError} = require '../utils/errors'
 log = require('../utils/logging')(prefix: 'models:account')
+_ = require 'lodash'

 Account::isTest = ->
     @accountType is 'TEST'


Finished rewrite of JS backend in CoffeeScript.

--- src/javascript/waxeye.coffee
+++ src/javascript/waxeye.coffee
@@ -265,19 +265,22 @@
       if res
         if @eofCheck and @inputPos < @inputLen
           # Create a parse error - Not all input consumed
-          new ParseError @error_pos, @error_line, @error_col, @error_nt
+          new ParseError @errorPos, @errorLine, @errorCol, @errorNT
         else
           res
       else
         # Create a parse error
-        new ParseError @error_pos, @error_line, @error_col, @error_nt
+        new ParseError @errorPos, @errorLine, @errorCol, @errorNT

     # Takes a set, an index into the set and an ordinal
+    # set - an array of single char strings and arrays of integer pairs
+    #       the integer pairs represent inclusive ranges of acceptable chars
+    #       the chars and ranges are in assending order
     withinSet: (set, index, c) ->
       if index == set.length
         false
       else
-        aa = set[0]
+        aa = set[index]
         if typeof aa == 'string'
           if (aa.charCodeAt 0) == c
             true


Split Terminal#restartCursorBlink into stop/start

--- app/assets/javascripts/terminal.js.coffee
+++ app/assets/javascripts/terminal.js.coffee
@@ -293,9 +293,14 @@
     else
       cursor.addClass "inverted"

-  restartCursorBlink: ->
+  startCursorBlink: ->
+    @cursorTimerId = setInterval(@blinkCursor.bind(this), 500)
+
+  stopCursorBlink: ->
     if @cursorTimerId
       clearInterval @cursorTimerId
       @cursorTimerId = null

-    @cursorTimerId = setInterval(@blinkCursor.bind(this), 500)
+  restartCursorBlink: ->
+    @stopCursorBlink()
+    @startCursorBlink()


Remove minimum highlight magic number

Remove minimum highlight magic number

--- app/coffeescripts/DemandVisualizer.coffee
+++ app/coffeescripts/DemandVisualizer.coffee
@@ -89,6 +89,7 @@
     width = 450
     height = 400
     padding = 50
+    minHighlight = 3
     @$el.dialog 'option', 'width', 510
     @$el.dialog 'option', 'height', 510

@@ -217,7 +218,7 @@
             else
               # Enforce coloring even if stddev. not present to distinguish
               # vehicle types
-              Math.max(maxHeight,3)
+              Math.max(maxHeight,minHighlight)

   initialize: (@demand) ->
     @profileSet = window.sirius.models.get('demandprofileset')


feat(web): reload app on logout;

feat(web): reload app on logout;

--- apps/actor-web/app/services/actorService.coffee
+++ apps/actor-web/app/services/actorService.coffee
@@ -28,6 +28,7 @@
     @isLoggedIn = false
     @$rootScope.$state.go 'login'
     @$rootScope.$broadcast 'actorLoggedOut'
+    location.reload()
   setCurrentPeer: (peer) ->
     console.log '[AW]ActorService setCurrentPeer'
     console.log '[AW]ActorService setCurrentPeer: peer:', peer


better scroll position calculation on comment/reply

--- www/src/views/comment/collection.coffee
+++ www/src/views/comment/collection.coffee
@@ -93,13 +93,20 @@

         composeComment: (opt) ->
             opt ?= {}
-            @$(".comment-add").show()
+            el = @$(".comment-add")
+            el.show()
             @textarea().reveal(if opt.reply then "@#{opt.reply}, " else "")
             @textarea
-            $('html, body').animate {
-                scrollTop: @$('.comment-add').offset().top
-            }, {
-                complete: => @textarea().focus()
-            }
+
+            newTop = el.offset().top + el.height() + 10 - window.innerHeight
+            if newTop > $('body').scrollTop()
+                $('html, body').animate {
+                    scrollTop: newTop
+                }, {
+                    complete: => @textarea().focus()
+                    duration: 250
+                }
+            else
+                @textarea().focus()

 # on success, 'add' will fire and form will be resetted


Make dynamically defined getters and setters enumerable.

--- src/environment.coffee
+++ src/environment.coffee
@@ -56,18 +56,21 @@
       value: (object, property, getter) ->
         unless getter
           [object, property, getter] = [@::, object, property]
-        Object.defineProperty object, property, configurable: true, get: getter
+        Object.defineProperty object, property,
+          configurable: true, enumerable: true, get: getter
     setter:
       value: (object, property, setter) ->
         unless setter
           [object, property, getter] = [@::, object, property]
-        Object.defineProperty object, property, configurable: true, set: getter
+        Object.defineProperty object, property,
+          configurable: true, enumerable: true, set: getter
     accessor:
       value: (object, property) ->
         unless property
           [object, property] = [@::, object]
         Object.defineProperty object, property,
           configurable: true,
+          enumerable: true,
           get: ->
             @["_#{property}"]
           set: (v) ->


Getting comfy with OOP style in CoffeeScript.

--- assets/js/lightcurve.coffee
+++ assets/js/lightcurve.coffee
@@ -1,22 +1,31 @@
 $ = jQuery
 $ ->
+    class LightCurve
+        constructor: (@selector) ->
+            @margin =
+                top: 20
+                right: 20
+                bottom: 20
+                left: 40
+            @width = $(@selector).width()
+            @height = 0.3 * @width
+            @width = @width - @margin.left - @margin.right
+            @height = @height - @margin.top - @margin.bottom
+
+        getSvg: ->
+            d3.select(@selector).append('svg')
+                .attr
+                    width: @width + @margin.left + @margin.right
+                    height: @height + @margin.top + @margin.bottom
+                .append('g')
+                .attr
+                    transform: "translate(#{@margin.left},#{@margin.top})"
+
     selector = '.lightcurve'
-    margin =
-        top: 20
-        right: 20
-        bottom: 20
-        left: 40
-    width = $(selector).width()
-    height = 0.3 * width
-    width = width - margin.left - margin.right
-    height = height - margin.top - margin.bottom
-    svg = d3.select(selector).append('svg')
-        .attr
-            width: width + margin.left + margin.right
-            height: height + margin.top + margin.bottom
-        .append('g')
-        .attr
-            transform: "translate(#{margin.left},#{margin.top})"
+    lc = new LightCurve(selector)
+    width = lc.width
+    height = lc.height
+    svg = lc.getSvg()
     xScale = d3.scale.linear().range([0, width]).nice()
     yScale = d3.scale.linear().range([0, height]).nice()
     csvUrl = $(selector).data 'csvSource'


Remove unnecessary space

Remove unnecessary space

--- assets/js/src/__init.coffee
+++ assets/js/src/__init.coffee
@@ -17,7 +17,7 @@
     return 'mobile' if (w <= 480)
     return 'tablet' if (w <= 1024)
     'desktop'
-
+
 $ ->
   $(feature_one_selector).attr('id', 'feature-one')
   $(feature_two_selector).attr('id', 'feature-two')


Update gruntfile for replacements

Update gruntfile for replacements

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -37,7 +37,7 @@
         options:
           patterns: [
             {
-              match: /[\t]+;\n/g,
+              match: /[\s]{2,};\n/g,
               replacement: () ->
                 return ''
             }


execute startUp function when data is successfully retrieved

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -2,7 +2,7 @@
   $("#question, #answer").hide()
   words = $.getJSON("spellings.json")
   $.when(words).done ((d)->
-    d['words']
+    startUp(d)
   )

   $.when(words).fail((data,status,error) ->


Changes on grunt file (now with uglify with mangle)

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -54,21 +54,24 @@

     uglify:
-      options: compress: false, banner: "<%= meta.banner %>"
+      options:
+        report: "gzip"
+        mangle: false
+        banner: "<%= meta.banner %>"
       core: files: '<%=meta.bower%>/<%=pkg.name%>.js': '<%=meta.build%>/<%=pkg.name%>.debug.js'
       organisms: files: '<%=meta.bower%>/<%=pkg.name%>.organisms.js': '<%=meta.build%>/<%=pkg.name%>.organisms.js'
       templates: files: '<%=meta.bower%>/<%=pkg.name%>.templates.js': '<%=meta.build%>/<%=pkg.name%>.templates.js'

     jasmine:
-        pivotal:
-          src: [
-            '<%=meta.build%>/<%=pkg.name%>.debug.js',
-            '<%=meta.build%>/<%=pkg.name%>.organisms.js',
-            '<%=meta.build%>/<%=pkg.name%>.templates.js']
-          options:
-            vendor: 'spec/components/jquery/jquery.min.js'
-            specs: '<%=meta.build%>/<%=pkg.name%>.spec.js',
+      pivotal:
+        src: [
+          '<%=meta.build%>/<%=pkg.name%>.debug.js',
+          '<%=meta.build%>/<%=pkg.name%>.organisms.js',
+          '<%=meta.build%>/<%=pkg.name%>.templates.js']
+        options:
+          vendor: 'spec/components/jquery/jquery.min.js'
+          specs: '<%=meta.build%>/<%=pkg.name%>.spec.js',

     stylus:
@@ -84,6 +87,7 @@
       icons:
         options: compress: false
         files: '<%=meta.bower%>_icons/<%=pkg.name%>.icons.css': '<%=source.stylus.icons%>'
+

     watch:
       core:
@@ -114,6 +118,7 @@
         files: ['<%= source.stylus.icons %>']
         tasks: ['stylus:icons']

+
   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-concat'
   grunt.loadNpmTasks 'grunt-contrib-uglify'


Reorder gruntfile

Reorder gruntfile

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -91,12 +91,12 @@
       organisms:
         files: ['<%= source.organisms %>']
         tasks: ['coffee:organisms']
+      templates:
+        files: ['<%= source.templates %>']
+        tasks: ['coffee:templates']
       example:
         files: ['<%= source.example %>']
         tasks: ['coffee:example']
-      templates:
-        files: ['<%= source.templates %>']
-        tasks: ['coffee:templates']
       stylus_base:
         files: ['<%= source.stylus.base %>']
         tasks: ['stylus:base']


Updated task order

Updated task order

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -85,4 +85,4 @@
     'mocha'
   ]

-  grunt.registerTask 'default', 'Default task', ['coffee:app', 'uglify', 'test']+  grunt.registerTask 'default', 'Default task', ['coffee', 'test', 'uglify']


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -52,5 +52,6 @@
     x: 25
     y: 0
     width: 10
+    height: 1
 module()


Also don't save window size when starting up.

--- src/ui/models/viewstate.coffee
+++ src/ui/models/viewstate.coffee
@@ -155,20 +155,19 @@
             later -> action 'updatewatermark'

     setSize: (size) ->
+        return if @state == STATES.STATE_STARTUP
         localStorage.size = JSON.stringify(size)
         @size = size
         # @updateView()

     setPosition: (pos) ->
-        if @state == STATES.STATE_STARTUP
-            return
-
+        return if @state == STATES.STATE_STARTUP
         localStorage.pos = JSON.stringify(pos)
         @pos = pos
         # @updateView()

     setLeftSize: (size) ->
-        return if @leftSize == size or size < 180
+        return if @state == STATES.STATE_STARTUP or @leftSize == size or size < 180
         @leftSize = localStorage.leftSize = size
         @updateView()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -18,7 +18,7 @@
   self.bind 'update', ->
     I.flickerDuration = I.flickerDuration.approach(0, 1)

-    if I.flickerDuration && I.age.mod(I.flickerFrequency) % 2
+    if I.flickerDuration  I.age.mod(I.flickerFrequency) is 0
       I.alpha = I.flickerAlpha
     else
       I.alpha = originalAlpha


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -18,7 +18,7 @@
   self.bind 'update', ->
     I.flickerDuration = I.flickerDuration.approach(0, 1)

-    if I.flickerDuration && I.age / I.flickerFrequency).floor() % 2
+    if I.flickerDuration && I.age.mod(I.flickerFrequency).floor() % 2
       I.alpha = I.flickerAlpha
     else
       I.alpha = originalAlpha


removed typescript test

removed typescript test

--- test/file-test.coffee
+++ test/file-test.coffee
@@ -17,10 +17,6 @@
 				done()
 		it 'should allow coffeescript files for a js type source', (done) ->
 			file 'js', 'src/main.coffee', 'src', {processors: processors.js}, (err, instance) ->
-				should.exist(instance)
-				done()
-		it 'should allow typescript files for a js type source', (done) ->
-			file 'js', 'src/main.ts', 'src', {processors: processors.js}, (err, instance) ->
 				should.exist(instance)
 				done()
 		it 'should allow css files for a css type source', (done) ->


Added onSubmitCallback for modal controller.

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -607,6 +607,8 @@

   onSubmit: (e) =>
     e.preventDefault()
+    if @onSubmitCallback
+      @onSubmitCallback()
     @log 'error', 'You need to implement your own "onSubmit" method!'

 class App.ErrorModal extends App.ControllerModal


Keep the caret when menu saves node

Keep the caret when menu saves node

--- app/assets/javascripts/ckeditor/plugins/effective_menus/plugin.js.coffee
+++ app/assets/javascripts/ckeditor/plugins/effective_menus/plugin.js.coffee
@@ -151,7 +151,7 @@

       # If I'm a top level dropdown
       if item.parent().hasClass('effective-menu')
-        item.children('a').append("")
+        item.children('a').append("")

       @menu.find('.open').removeClass('open')
       item.parentsUntil(@menu, 'li.dropdown').addClass('open')
@@ -180,7 +180,7 @@

       # And add it back if we're a top level node
       if item.parent().hasClass('effective-menu') && item.children('.dropdown-menu').length
-        item.children('a').append("")
+        item.children('a').append("")

       item.parentsUntil(@menu, 'li.dropdown').addClass('open')

@@ -306,7 +306,13 @@
                   this.setValue(element.children('.menu-item').children("input[name$='[title]']").val())
                 commit: (element) ->
                   element.children('.menu-item').children("input[name$='[title]']").val(this.getValue())
-                  element.children('a').text(this.getValue())
+
+                  if element.children('a').find('span.caret').length > 0
+                    element.children('a').text(this.getValue())
+                    element.children('a').append("")
+                  else
+                    element.children('a').text(this.getValue())
+
               },
               {type: 'html', html: '
'},
               {


Update test to prove that there's no collision in generated variables

--- test/functions.coffee
+++ test/functions.coffee
@@ -479,4 +479,6 @@
   foo = -> null
   bar = -> 33
   f = (a = foo() ? bar()) -> a
+  g = (a = foo() ? bar()) -> a + 1
   eq f(), 33
+  eq g(), 34


fix: end of light curve is cut off (account for smaller canvas plotting area due to padding)

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -189,7 +189,7 @@

     # plot points
     for i in [0...@dataLength]
-      x = ((+@data.x[i]-xMin)/(xMax-xMin)) * @canvas.width
+      x = ((+@data.x[i]-xMin)/(xMax-xMin)) * (@canvas.width-@leftPadding)
       y = ((+@data.y[i]-yMin)/(yMax-yMin)) * @canvas.height
       y = -y + @canvas.height # flip y-values
       @ctx.fillStyle = "#fff" #fc4541"
@@ -210,8 +210,8 @@
     tickMinorLength = 5
     tickMajorLength = 10
     tickWidth = 1
-    tickColor = '#323232'
-    textColor = '#323232'
+    tickColor = '#fc4541'#323232'
+    textColor = '#fc4541'#323232'
     textSpacing = 15

     # determine intervals
@@ -262,7 +262,7 @@

       # draw numbers (bottom)
       if (i % majorTickInterval) is 0 # zoomed out
-        @ctx.fillText( tick, @toCanvasXCoord(tick), @canvas.height - textSpacing )
+        @ctx.fillText( tick, @toCanvasXCoord(tick)+@leftPadding, @canvas.height - textSpacing )
       else if (i % majorTickInterval) is 0
         @ctx.fillText( tick, @toCanvasXCoord(tick)+@leftPadding, @canvas.height - textSpacing )
       else if (i % majorTickInterval) is 0
@@ -312,8 +312,8 @@
     tickMinorLength = 5
     tickMajorLength = 10
     tickWidth = 1
-    tickColor = '#323232' #'rgba(200,20,20,1)'
-    textColor = '#323232'
+    tickColor = '#fc454'#323232' #'rgba(200,20,20,1)'
+    textColor = '#fc4541'#'#323232'
     textSpacing = 15
     majorTickInterval = 2
     minorTickInterval = 1


clean up code

clean up code

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -131,12 +131,9 @@
     std = @std(data.y)

     for y, i in [data.y...]
-      if Math.sqrt( Math.pow( y - mean, 2 ) ) > nsigma * std
-        console.log 'removed outlier' if DEBUG
-        continue # skip (outlier)
-      else
-        data_new.x.push data.x[i]
-        data_new.y.push data.y[i]
+      continue if Math.sqrt( Math.pow( y - mean, 2 ) ) > nsigma * std # skip outlier
+      data_new.x.push data.x[i]
+      data_new.y.push data.y[i]

     return data_new


quickScale option for thangs to avoid elastic animation (#6562)

Co-authored-by: Valentin Briukhanov <1c97702c869e5316729111bfe70e91c2a0718c7e@Valentins-MacBook-Air.local>

--- app/lib/surface/Lank.coffee
+++ app/lib/surface/Lank.coffee
@@ -353,7 +353,7 @@

     newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1
     newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1
-    if @layer?.name is 'Land' or @thang?.isLand or @thang?.spriteName is 'Beam' or @isCinematicLank
+    if @layer?.name is 'Land' or @thang?.isLand or @thang?.spriteName is 'Beam' or @isCinematicLank or @thang?.quickScale
       @scaleFactorX = newScaleFactorX
       @scaleFactorY = newScaleFactorY
     else if @thang and (newScaleFactorX isnt @targetScaleFactorX or newScaleFactorY isnt @targetScaleFactorY)


Fix subscription to remote private channels

Before that, subscriptions to remote private channels were in a weird state
where not all nodes were created locally.

--- src/xmpp/backend_pubsub.coffee
+++ src/xmpp/backend_pubsub.coffee
@@ -188,7 +188,7 @@
                     @emit 'authorizationConfirm', { node, user, allow, actor, sender }

         # Which nodes' updates pertain our local cache?
-        async.filter(updates, (update, cb) =>
+        async.filter updates, (update, cb) =>
             user = getNodeUser(update.node)
             unless user
                 return cb false
@@ -196,12 +196,25 @@
             @authorizeFor sender, user, (err, valid) ->
                 cb not err and valid
         , (updates) =>
-            if updates? and updates.length > 0
-                updates.sender = sender
-                updates.actor = sender
-                # Apply pushed updates
-                @emit 'notificationPush', updates
-        )
+            # Add a listener to each subscription update
+            async.map updates, (update, cb2) =>
+                if update.type is 'subscription'
+                    @disco.findService update.user, (err, service) =>
+                        if err
+                            cb2 err, null
+                        else
+                            # If remote user, listener = sender. If local user, listener = user.
+                            update.listener = if @getMyJids().indexOf(service) >= 0 then update.user else sender
+                            cb2 null, update
+                else
+                    cb2 null, update
+            , (err, updates) =>
+                # FIXME: don't ignore err
+                if updates? and updates.length > 0
+                    updates.sender = sender
+                    updates.actor = sender
+                    # Apply pushed updates
+                    @emit 'notificationPush', updates

 class BuddycloudDiscovery


Allow editing science case

Allow editing science case

--- app/pages/edit-project.cjsx
+++ app/pages/edit-project.cjsx
@@ -47,6 +47,9 @@
             Introduction

             <MarkdownEditor name="introduction" value={@props.project.introduction} onChange={handleProjectChange} />

+            Science case

+            <MarkdownEditor name="science_case" value={@props.project.science_case} onChange={handleProjectChange} />

+
             Workflows

               {for workflow in @state.workflows


timeout incr to 30s for the first code run in a project

--- src/smc-hub/test/api/advanced.coffee
+++ src/smc-hub/test/api/advanced.coffee
@@ -25,7 +25,7 @@
                 done(err)

     it "does shell built-in", (done) ->
-        @timeout 10000
+        @timeout 30000
         api.call
             event : 'project_exec'
             body  :
@@ -77,7 +77,7 @@
                 done(err)

     it "sets execution timeout", (done) ->
-        @timeout 5000
+        @timeout 10000
         api.call
             event : 'project_exec'
             body  :


Fix stupid typo

Fix stupid typo

--- js/inner.coffee
+++ js/inner.coffee
@@ -485,7 +485,7 @@

   @sidebarSort: (l, r) ->
-    if l.followed() and r.followed() and l.latest_build()? and r.lastest_build()?
+    if l.followed() and r.followed() and l.latest_build()? and r.latest_build()?
       if l.latest_build().build_num > r.latest_build().build_num then -1 else 1
     else if l.followed() and l.latest_build()?
       -1


fix: use JSON.parse(JSON.stringify()) instead of lodash.cloneDeep

--- report.cjsx
+++ report.cjsx
@@ -257,7 +257,7 @@
 whenMapStart = (_ships)->
   combined = false
   _path = []
-  __ships = if _.cloneDeep isnt undefined then _.cloneDeep _ships else JSON.parse JSON.stringify _ships
+  __ships = JSON.parse JSON.stringify _ships

 whenBattleResult = (_decks, _ships) ->
   decks = []


Use consistent @'s

Use consistent @'s

--- src/code/views/value-slider-view.coffee
+++ src/code/views/value-slider-view.coffee
@@ -78,11 +78,11 @@
     window.removeEventListener 'resize', @handleUpdate

   handleUpdate: ->
-    { orientation } = this.props
+    { orientation } = @props
     dimension = constants.orientation[orientation].dimension
     dimension = dimension.charAt(0).toUpperCase() + dimension.substr(1)
-    sliderPos = this.slider["offset#{dimension}"]
-    handlePos = this.handle?["offset#{dimension}"] or 0
+    sliderPos = @slider["offset#{dimension}"]
+    handlePos = @handle?["offset#{dimension}"] or 0

     @setState
       limit: sliderPos - handlePos
@@ -121,8 +121,8 @@
     document.addEventListener 'mouseup',   @handleEnd

   handleEnd: ->
-    document.removeEventListener 'mousemove', this.handleDrag
-    document.removeEventListener 'mouseup',   this.handleEnd
+    document.removeEventListener 'mousemove', @handleDrag
+    document.removeEventListener 'mouseup',   @handleEnd

   handleDrag: (e) ->
     @handleNoop(e)
@@ -136,8 +136,8 @@
     Math.min(Math.max(value, min), max)

   getValueFromPosition: (pos) ->
-    { limit } = this.state
-    { orientation, min, max, stepSize } = this.props
+    { limit } = @state
+    { orientation, min, max, stepSize } = @props
     percentage = (@clamp(pos, 0, limit) / (limit || 1))
     baseVal = stepSize * Math.round(percentage * (max - min) / stepSize)

@@ -159,7 +159,7 @@
     direction = node.getBoundingClientRect()[directionStyle]

     pos = coordinate - direction - grab
-    value = this.getValueFromPosition(pos)
+    value = @getValueFromPosition(pos)

     return value

@@ -212,10 +212,10 @@
       return if not isEditable
       # first copy state value to model if we were editing
       if @state["editing-#{property}"]
-        newValue = parseInt ReactDOM.findDOMNode(this.refs.focusable)?.value
+        newValue = parseInt ReactDOM.findDOMNode(@refs.focusable)?.value
         if newValue? then @updateRange property, newValue
       @setState "editing-#{property}": not @state["editing-#{property}"], ->
-        this.refs.focusable?.focus()
+        @refs.focusable?.focus()

     keyDown = (evt) ->
       if evt.key is 'Enter'


Use centralized platform check

Use centralized platform check

--- spec/Spring.coffee
+++ spec/Spring.coffee
@@ -1,5 +1,6 @@
-socket = require('noflo').internalSocket
-if typeof process is 'object' and process.title is 'node'
+noflo = require 'noflo'
+socket = noflo.internalSocket
+unless noflo.isBrowser()
   chai = require 'chai' unless chai
   spring = require '../components/Spring.coffee'
 else


Update search-result.cjsx to new CommentLink expected props

--- app/talk/search-result.cjsx
+++ app/talk/search-result.cjsx
@@ -10,8 +10,10 @@
   displayName: "TalkSearchResult"

   render: ->
+    discussion = @props.data.discussion
+
     <div className="talk-search-result">
-      <CommentLink {...@props.data} />
+      <CommentLink comment={@props.data}>Comment #{discussion.links.comments.indexOf(@props.data.id.toString()) + 1} in {discussion.title}</CommentLink>
       <CommentPreview content={@props.data.body} header={null} />
-      <DiscussionPreview data={@props.data.discussion} />
+      <DiscussionPreview data={discussion} />
     </div>


Change setDocumentListParams call

Change setDocumentListParams call

--- js/views/VizView.coffee
+++ js/views/VizView.coffee
@@ -92,7 +92,7 @@

     window.parent.postMessage({
       call: 'setDocumentListParams'
-      args: [ { name: "in document set" } ]
+      args: [ {} ]
     }, @server)

   _onClickText: (el) ->
@@ -150,7 +150,7 @@

     window.parent.postMessage({
       call: 'setDocumentListParams'
-      args: [ { q: token.text, name: "with word “#{token.text}”" } ]
+      args: [ { q: token.text } ]
     }, @server)

   _drawFromLayout: (data) ->


handle errors during jiraPattern definition

--- src/jira-issues.coffee
+++ src/jira-issues.coffee
@@ -40,6 +40,10 @@
   robot.http(jiraUrl + "/rest/api/2/project")
     .auth(auth)
     .get() (err, res, body) ->
+      if err
+        errmsg = "Unable to initialize hubot-jira:\n    #{err}"
+        robot.logger.error errmsg
+        return
       json = JSON.parse(body)
       jiraPrefixes = ( entry.key for entry in json )
       reducedPrefixes = jiraPrefixes.reduce (x,y) -> x + "-|" + y


e

e

spelling is hard, let's go shopping

--- spec/atom/editor-spec.coffee
+++ spec/atom/editor-spec.coffee
@@ -512,7 +512,7 @@
       expect(editor.clipPosition(row: 1, column: -5)).toEqual(row: 1, column: 0)

   describe "cut, copy & paste", ->
-    describe "when a past event is triggered", ->
+    describe "when a paste event is triggered", ->
       beforeEach ->
         atom.native.writeToPasteboard('first')
         expect(atom.native.readFromPasteboard()).toBe 'first'


Use a dashed outline for selected elements in preview

A little less intrusive

--- app/assets/javascripts/alchemy/alchemy.preview.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.preview.js.coffee
@@ -15,11 +15,11 @@
           "outline-offset": ""
           cursor: ""
         hover:
-          outline: "3px solid #F0B437"
+          outline: "2px dashed #f0b437"
           "outline-offset": "4px"
           cursor: "pointer"
         selected:
-          outline: "3px solid #90B9D0"
+          outline: "2px dashed #90b9d0"
           "outline-offset": "4px"

       init: ->


Reuse existing promise var

Reuse existing promise var

--- spec/github-file-spec.coffee
+++ spec/github-file-spec.coffee
@@ -324,4 +324,4 @@
       atom.commands.dispatch(atom.views.getView(atom.workspace), 'open-on-github:file')

     waitsForPromise ->
-      atom.packages.activatePackage('open-on-github')
+      activationPromise


Rename window.onerror spec.

Rename window.onerror spec.

--- spec/honeybadger.spec.coffee
+++ spec/honeybadger.spec.coffee
@@ -265,7 +265,7 @@
     it 'drops undefined values', ->
       expect(Honeybadger._serialize({foo: undefined, bar: 'baz'})).toEqual('bar=baz')

-  describe '._windowOnErrorHandler', ->
+  describe 'window.onerror callback', ->
     beforeEach () ->
       spyOn Honeybadger, 'notify'


Make archive group work again

Make archive group work again

--- lineman/app/models/group_records_interface.coffee
+++ lineman/app/models/group_records_interface.coffee
@@ -7,5 +7,5 @@
         path: "#{parentGroup.id}/subgroups"
         cacheKey: "subgroupsFor#{parentGroup.key}"

-    archive: (group) ->
-      @restfulClient.postMember group.id, "archive"+    archive: (group) =>
+      @restfulClient.patchMember group.id, "archive"


Add descriptions for all package settings


refactoring specs

refactoring specs

--- spec/test-runner-spec.coffee
+++ spec/test-runner-spec.coffee
@@ -11,6 +11,25 @@
     "../../node_modules/jasmine-node/bin/jasmine-node --junitreport --output #{reportPath} #{testFilePath}"

 describe "TestRunner", ->
+
+  trigger = (action) ->
+    atom.workspaceView.getActiveView().trigger action
+
+  waitForTestToBeFinished = ->
+    waitsFor ->
+      !atom.workspaceView.find('.quick-test-result span').hasClass('icon-clock')
+
+  expectStatusBarToShowRunningIcon = ->
+    expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-clock')
+
+  expectStatusBarToShowSuccessIcon = ->
+    expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-check')
+
+  expectStatusBarToShowFailureIcon = ->
+    expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-stop')
+
+  expectStatusBarToShow = (text) ->
+    expect(atom.workspaceView.find('.quick-test-result').text()).toEqual(text)

   beforeEach ->
     testRunner.handlerRegistry.addBefore new TestJasmineHandler, /_spec\.js/
@@ -35,43 +54,39 @@
       waitsForPromise ->
         atom.workspace.open('success_spec.js')
       runs ->
-        atom.workspaceView.getActiveView().trigger 'test-runner:run-all'
+        trigger 'test-runner:run-all'

-        expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-clock')
-        expect(atom.workspaceView.find('.quick-test-result').text()).toEqual('running')
-      waitsFor ->
-        !atom.workspaceView.find('.quick-test-result span').hasClass('icon-clock')
+        expectStatusBarToShowRunningIcon()
+        expectStatusBarToShow('running')
+      waitForTestToBeFinished()

     it 'shows a success message', ->
       waitsForPromise ->
         atom.workspace.open('success_spec.js')
       runs ->
-        atom.workspaceView.getActiveView().trigger 'test-runner:run-all'
-      waitsFor ->
-        !atom.workspaceView.find('.quick-test-result span').hasClass('icon-clock')
-
+        trigger 'test-runner:run-all'
+      waitForTestToBeFinished()
       runs ->
-        expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-check')
-        expect(atom.workspaceView.find('.quick-test-result').text()).toEqual('All tests in success_spec.js have been successful')
+        expectStatusBarToShowSuccessIcon()
+        expectStatusBarToShow('All tests in success_spec.js have been successful')

     it 'shows a failure message', ->
       waitsForPromise ->
         atom.workspace.open('fail_spec.js')
       runs ->
-        atom.workspaceView.getActiveView().trigger 'test-runner:run-all'
-      waitsFor ->
-        !atom.workspaceView.find('.quick-test-result span').hasClass('icon-clock')
+        trigger 'test-runner:run-all'
+      waitForTestToBeFinished()

       runs ->
-        expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-stop')
-        expect(atom.workspaceView.find('.quick-test-result').text()).toEqual('fail_spec.js:4 # jasmine test suite a failing test')
+        expectStatusBarToShowFailureIcon()
+        expectStatusBarToShow('fail_spec.js:4 # jasmine test suite a failing test')

     it 'shows a message when no appropriate handler has been found', ->
       waitsForPromise ->
         atom.workspace.open('example.b')
       runs ->
-        atom.workspaceView.getActiveView().trigger 'test-runner:run-all'
+        trigger 'test-runner:run-all'

       runs ->
-        expect(atom.workspaceView.find('.quick-test-result span')).toHaveClass('icon-stop')
-        expect(atom.workspaceView.find('.quick-test-result').text()).toEqual('Don\'t know how to run example.b')
+        expectStatusBarToShowFailureIcon()
+        expectStatusBarToShow('Don\'t know how to run example.b')


Use status image helper to determine the URL.

--- assets/scripts/app/views/repo/show.coffee
+++ assets/scripts/app/views/repo/show.coffee
@@ -126,7 +126,7 @@
     currentUserBinding: 'controller.currentUser'

     statusImageUrl: (->
-      "/#{@get('slug')}.png"
+      Travis.Urls.statusImage(@get('slug'))
     ).property('slug')

     displayStatusImages: (->


Update snippets.

Update snippets.

--- snippets/js-classes.cson
+++ snippets/js-classes.cson
@@ -1,7 +1,7 @@
 '.source.js, .source.jsx':
   'Js class create':
     'prefix': 'class'
-    'body': 'class ${1:className}${2:extends class}{\n\t${3:body}\n}$0'
+    'body': 'class ${1:className}${2: extends class}{\n\t${3:body}\n}$0'
   'Js constructor()':
     'prefix': 'construct'
     'body': 'constructor (${1:params}) {\n\t${2:body}\n}'


Rename tapSize to hitArea

Rename tapSize to hitArea

--- framer/Components/SliderComponent.coffee
+++ framer/Components/SliderComponent.coffee
@@ -74,8 +74,8 @@
 		@knob.draggable.propagateEvents = false
 		@knob.borderRadius = @knobOverlay.borderRadius = @knobSize / 2

-		# If tapSize isn't defined, remove the overlays
-		if not @tapSize
+		# If hitArea isn't defined, remove the overlays
+		if not @hitArea
 			@knobOverlay.destroy()
 			@sliderOverlay.destroy()

@@ -178,20 +178,20 @@
 			@knob.height = @_knobSize
 			@_updateFrame()

-	@define "tapSize",
-		get: -> @_tapSize
+	@define "hitArea",
+		get: -> @_hitArea

 		set: (value) ->

-			@_tapSize = value
+			@_hitArea = value

 			@knobOverlay.props =
-				width: @knobSize or 30 + @_tapSize
-				height:	@knobSize or 30 + @_tapSize
+				width: @knobSize or 30 + @_hitArea
+				height:	@knobSize or 30 + @_hitArea

 			@sliderOverlay.props =
-				width: @width + @_tapSize
-				height:	@height + @_tapSize
+				width: @width + @_hitArea
+				height:	@height + @_hitArea

 	@define "min",
 		get: -> @_min or 0


Fix slider calculations

Fix slider calculations

--- framer/Components/SliderComponent.coffee
+++ framer/Components/SliderComponent.coffee
@@ -96,11 +96,11 @@
 		if @width > @height
 			touchX = Events.touchEvent(event).clientX - Screen.canvasFrame.x
 			scaleX = @canvasScaleX()
-			@value = @valueForPoint(touchX / scaleX - @x)
+			@value = @valueForPoint(touchX / scaleX - @screenFrame.x)
 		else
 			touchY = Events.touchEvent(event).clientY - Screen.canvasFrame.y
 			scaleY = @canvasScaleY()
-			@value = @valueForPoint(touchY / scaleY - @y)
+			@value = @valueForPoint(touchY / scaleY - @screenFrame.y)

 		@knob.draggable._touchStart(event)
 		@_updateValue()


Parse item query filters in request.

Parse item query filters in request.

--- src/model.coffee
+++ src/model.coffee
@@ -302,6 +302,13 @@

         if entry.params.hasOwnProperty(key)
           data[underscore key] = value
+        # Check for search filters (example: `event__id`)
+        else if key.indexOf '__'
+          filteredOn = key.split '__'
+          itemCollection = teamsnap.getCollectionForItem(filteredOn[0])
+          # Ensure valid param on filtered item type's search query
+          if itemCollection.queries.search.params.hasOwnProperty filteredOn[1]
+            data[underscore(key)] = value

     request(method, entry.href, data).then (xhr) ->
       items = if xhr.data?.collection?.items


Add support for x and y parameters to be passed as arrays vice strings coupled with a data source.

--- bokehjs/src/coffee/models/transforms/interpolator.coffee
+++ bokehjs/src/coffee/models/transforms/interpolator.coffee
@@ -16,30 +16,51 @@

   props: ->
     return _.extend {}, super(), {
-      x: [ p.String, '']
-      y: [ p.String, '']
+      x: [ p.Any, '']
+      y: [ p.Any, '']
       data: [ p.Any, null]
     }

   sort: (descending = false) ->
     # Verify that all necessary objects exist...
-    if @get('x').length == 0
+    if typeof(@get('x')) != typeof(@get('y'))
+      # ToDo: Throw a resonable error here
       return

-    if @get('y').length == 0
-      return
+    if typeof(@get('x')) == 'object'
+      if @get('x').length != @get('y').length
+        # ToDo: Throw a resonable error here
+        return
+    else
+      if @get('x').length == 0
+        return

-    if @get('data') == null
-      return
+      if @get('y').length == 0
+        return
+
+      if @get('data') == null
+        # ToDo: Throw a resonable error here.
+        return

     # Stop processing this if the dirty flag is not set
     if(@_sorted_dirty == false)
       return

-    data = @get('data')
+    console.log('Sorting...')

-    tsx = data.get_column(@get('x'))
-    tsy = data.get_column(@get('y'))
+    tsx = []
+    tsy = []
+
+    # Populate the tsx and tsy variables correctly depending on the method by which the user populated the interpolation
+    # data.
+    if typeof(@get('x')) == 'string'
+      data = @get('data')
+
+      tsx = data.get_column(@get('x'))
+      tsy = data.get_column(@get('y'))
+    else
+      tsx = @get('x')
+      tsy = @get('y')

     # The following sorting code is referenced from:
     # http://stackoverflow.com/questions/11499268/sort-two-arrays-the-same-way


atom: disable snippets

atom: disable snippets

--- .atom/config.cson
+++ .atom/config.cson
@@ -17,6 +17,8 @@
       "linter-pep257"
       "spell-check"
       "linter-shellcheck"
+      "autocomplete-snippets"
+      "snippets"
     ]
   editor:
     fontFamily: "inconsolata"


usePreviewTabs

usePreviewTabs

--- .atom/config.cson
+++ .atom/config.cson
@@ -13,11 +13,10 @@
       ".svn"
       ".DS_Store"
     ]
-    themes: [
-      "atom-dark-ui"
-      "atom-dark-syntax"
-    ]
   editor:
     invisibles: {}
     normalizeIndentOnPaste: false
     autoIndentOnPaste: false
+    fontSize: 17
+  tabs:
+    usePreviewTabs: true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_fade.coffee
+++ src/engine_fade.coffee
@@ -1,10 +1,54 @@
+###*
+The Fade module provides convenience methods for accessing common Engine.Flash presets.
+
+@name Fade
+@fieldOf Engine
+@module
+@param {Object} I Instance variables
+@param {Object} self Reference to the engine
+@see Engine.Flash
+###
 Engine.Fade = (I, self) ->
+  ###*
+  A convenient way to set the flash effect instance variables. This provides a shorthand for fading the screen in
+  from a given color over a specified duration.
+
+

+  engine.fadeIn()
+  # => Sets the effect variables to their default state. This will the screen to fade to black over the next 30 frames.
+
+  engine.fadeIn('blue', 50)
+  # => This effect will start off blue and fade to transparent over 50 frames.
+

+
+  @name fadeIn
+  @methodOf Engine#
+  @param {Number} [duration=30] How long the effect lasts
+  @param {Color} [color="black"] The color to fade to
+  ###
   fadeIn: (duration = 30, color = 'black') ->
     I.flashColor = Color(color)
     I.flashCooldown = (duration)
     I.flashDuration = (duration)
     I.flashTargetAlpha = 0

+  ###*
+  A convenient way to set the flash effect instance variables. This provides a shorthand for fading
+  the screen to a given color over a specified duration.
+
+

+  engine.fadeOut()
+  # => Sets the effect variables to their default state. This will the screen to fade from black to transparent over the next 30 frames.
+
+  engine.fadeOut('blue', 50)
+  # => This effect will start off transparent and change to blue over 50 frames.
+

+
+  @name fadeIn
+  @methodOf Engine#
+  @param {Number} [duration=30] How long the effect lasts
+  @param {Color} [color="black"] The color to fade from
+  ###
   fadeOut: (duration = 30, color = 'transparent') ->
     I.flashColor = Color(color)
     I.flashCooldown = (duration)


use passed array as List prototype for [] access

--- src/monad.coffee
+++ src/monad.coffee
@@ -38,9 +38,7 @@
 Maybe = (value) -> if value then Just value else None

 # Haskell-style List with indeterminate binding!
-List = (items) ->
-  items = if items then items.slice() else [] # Nasty Mutability, begone!
-  Trait.create Object.prototype, (Trait.override (Trait {
+List = (items...) -> Trait.create items, (Trait.override (Trait {
   unit : List
   bind : (fn) ->
     @unit (Array.prototype.concat (


Removed 'keypress' from default event list.

--- src/event_lists.coffee
+++ src/event_lists.coffee
@@ -4,7 +4,6 @@
 exports.defaultEvents =
     'click' : true
     'change' : true
-    'keypress' : true

 # These are events that are eligible for listening on the client.  We need
 # this because we need to know which events we should ignore inside our


[data] make l10n switch in more compact

[data] make l10n switch in more compact

--- source/data/index.coffee
+++ source/data/index.coffee
@@ -51,10 +51,6 @@
     data:
       currentYear: new Date().getFullYear()

-  return (locale) ->
-
-    switch locale
-
-      when 'ru-RU' then merge {}, data,
-
-      else data+  return (locale) -> switch locale
+    when 'ru-RU' then merge {}, data
+    else data


Number#even and Number#odd

Number#even and Number#odd

--- src/number_extensions.coffee
+++ src/number_extensions.coffee
@@ -140,7 +140,10 @@
 @returns true if this number is an odd integer, false otherwise.
 ###
 Number::odd = ->
-  this % 2 == 1
+  if this > 0
+    this % 2 == 1
+  else
+    this % 2 == -1

 ###*
 Calls iterator the specified number of times, passing in the number of the


Implemented XAA instruction.

Implemented XAA instruction.

--- emulator/core/units/cpu.coffee
+++ emulator/core/units/cpu.coffee
@@ -608,6 +608,9 @@
     RLA: (address) =>
         @$storeValueIntoAccumulator @accumulator & @ROL address

+    XAA: (address) => # Also known as ANE
+        @$storeValueIntoAccumulator @registerX & @AND address
+
     RRA: (address) =>
         @$addValueToAccumulator @ROR address

@@ -1058,6 +1061,8 @@
         @registerOperation 0x23, @RLA, @indirectXMode, 0, 1, 1 # 8 cycles (undocumented operation)
         @registerOperation 0x33, @RLA, @indirectYMode, 0, 1, 1 # 8 cycles (undocumented operation)

+        @registerOperation 0x8B, @XAA, @immediateMode, 0, 0, 0 # 2 cycles (undocumented operation)
+
         @registerOperation 0x67, @RRA, @zeroPageMode,  0, 0, 1 # 5 cycles (undocumented operation)
         @registerOperation 0x77, @RRA, @zeroPageXMode, 0, 1, 1 # 6 cycles (undocumented operation)
         @registerOperation 0x6F, @RRA, @absoluteMode,  0, 0, 1 # 6 cycles (undocumented operation)


fix regexp

fix regexp

--- scripts/dark.coffee
+++ scripts/dark.coffee
@@ -16,7 +16,7 @@
   robot.respond /poem$/i, (res) ->
     res.send util.shuffle json.poem

-  robot.hear /なん(.|。|・)*だと(.|。|・)*/i, (res) ->
+  robot.hear /なん(\.|。|・)*だと(\.|。|・)*$/i, (res) ->
     res.send "http://dic.nicovideo.jp/oekaki/219454.png"

   robot.respond /炎上 (.+) (.+)/i, (res) ->


raspberrypi.coffee: Set yocto version to thud

Signed-off-by: Andrei Gherzan <8dc7c09356db0b1762fb140617d7375c0a5a4131@balena.io>

--- raspberrypi.coffee
+++ raspberrypi.coffee
@@ -29,7 +29,7 @@
 		machine: 'raspberrypi'
 		image: 'resin-image'
 		fstype: 'resinos-img'
-		version: 'yocto-sumo'
+		version: 'yocto-thud'
 		deployArtifact: 'resin-image-raspberrypi.resinos-img'
 		compressed: true


Refactor core's noConflict support with safeSet.

--- src/js/jquery.extension.hlf.core.coffee
+++ src/js/jquery.extension.hlf.core.coffee
@@ -62,7 +62,7 @@
         @
       plugin

-    noConflicts: [],
+    noConflicts: []
     noConflict: -> (fn() for fn in @noConflicts).length

     debug: on # Turn this off when going to production.
@@ -73,9 +73,12 @@

   _.bindAll $.hlf, 'createPlugin'

-  _createPlugin = $.createPlugin
-  $.createPlugin = $.hlf.createPlugin
-  $.hlf.noConflicts.push -> $.createPlugin = _createPlugin
+  safeSet = (key, toContext=$, fromContext=$.hlf) ->
+    _oldValue = toContext[key]
+    toContext[key] = fromContext[key]
+    $.hlf.noConflicts.push -> toContext[key] = _oldValue
+
+  safeSet 'createPlugin'

   return $.hlf
 )


Fix aloha-editor's improper paths

Fix aloha-editor's improper paths

--- scripts/configs/aloha.coffee
+++ scripts/configs/aloha.coffee
@@ -27,6 +27,12 @@
         # jquery-ui and bootstrap conflict in a few cases (buttons,
         # tooltip) our copy has those removed.
         jqueryui: '../../oerpub/js/jquery-ui-1.9.0.custom-aloha'
+        contenthandler: '../plugins/common/contenthandler/lib'
+        semanticblock: '../plugins/oer/semanticblock'
+
+      map:
+        '*':
+          'semanticblock/semanticblock-plugin': 'semanticblock/lib/semanticblock-plugin'

     errorhandling: true
     plugins:


Remove reset flag

Remove reset flag

--- lib/BehaviorTree.coffee
+++ lib/BehaviorTree.coffee
@@ -138,7 +138,7 @@
       choice.state = State.REJECTED
       @resolve node.id

-  resolve: (id, reset = false) ->
+  resolve: (id) ->
     node = @nodes[id]
     return unless node
     return unless node.choice
@@ -147,7 +147,7 @@
     throw val if node.type is 'finally' and node.choice.state is State.REJECTED
     dests = @findDestinations node, node.choice
     dests.forEach (d) =>
-      if not reset and d.choice and d.choice.state isnt State.PENDING
+      if d.choice and d.choice.state isnt State.PENDING
         return
       @executeNode node.choice, d.id, val


New line spacing fixed, Background label removed

--- cakes.coffee
+++ cakes.coffee
@@ -4,8 +4,8 @@
 	#  exp. Feature 'new feature', 'in order to do good', 'as a user', 'I want to do good', ->
 	# message = "Feature: #{feature} \n\n\t#{benefit}\n\t#{who}\n\t#{desire}"

-	message = "Feature: #{feature} \n".green.underline
-	(message += '\n\t'+part for part in story)
+	message = "Feature: #{feature} \n\n".green.underline
+	(message += '\t'+part+'\n' for part in story)

 	describe(message, callback)
 	return
@@ -18,7 +18,7 @@
 	describe label.replace('%s', args[0]), args[1]

 exports.Background = (action, callback)->
-	depict "\n   Background: #{action}".magenta, arguments
+	depict action.magenta, arguments

 exports.Scenario = ->
 	depict "\n    Scenario: %s".green, arguments


refactor: better error handling and tag parsing

--- src/giphy-gifme.coffee
+++ src/giphy-gifme.coffee
@@ -1,40 +1,76 @@
 # Description:
-# Pulls a random gif (optionally by tag[s]) from Giphy
+# Pulls a random gif (optionally limited by tag[s]) from Giphy
 #
 # Dependencies:
 # none
 #
 # Configuration:
 # process.env.HUBOT_GIPHY_API_KEY = <your giphy API key>
-# process.env.HUBOT_GIPHY_RATING = 'pg' (y,g, pg, pg-13 or r)
+# process.env.HUBOT_GIPHY_RATING = 'pg' (y, g, pg, pg-13 or r)
 #
 # Commands:
-# hubot gif me (tag 1, tag 2) - Get a random gif (tagged with "tag 1" and "tag 2")
+# hubot gif me - Get a completely random GIF
+# hubot gif me tag 1, "tag 2" - Search for a GIF tagged with "tag 1" and "tag 2"
+# hubot giphy - Get a completely random GIF
+# hubot giphy tag 1, "tag 2" - Search for a GIF tagged with "tag 1" and "tag 2"
 #
 # Author:
 # Ben Centra

-api_key = process.env.HUBOT_GIPHY_API_KEY or 'dc6zaTOxFJmzC' # <== Giphy's public API key, please request your own!
-rating_limit = process.env.HUBOT_GIPHY_RATING or 'pg'
+# Key for Giphy API
+# Default value is the demo key; please request your own!
+GIPHY_API_KEY = process.env.HUBOT_GIPHY_API_KEY or 'dc6zaTOxFJmzC'

-Array::empty = -> @.length == 0
+# Content rating to prevent NSFW responses
+# Possible values: y, g, pg, pg-13 or r
+CONTENT_RATING_LIMIT = process.env.HUBOT_GIPHY_RATING or 'pg'

-getRandomGiphyGif = (msg, tags) ->
-  url = "http://api.giphy.com/v1/gifs/random?api_key=#{api_key}&rating=#{rating_limit}"
-  if tags and tags[0] != ''
-    url += '&tag=' + tags[0]
-    for i in [1...tags.length]
-      url += ('+' + tags[i]) if tags[i].length > 0
+# Base URL of Giphy API "random" endpoint
+# API Docs: https://github.com/Giphy/GiphyAPI#random-endpoint
+ENDPOINT_URL_RANDOM = "http://api.giphy.com/v1/gifs/random?api_key=#{GIPHY_API_KEY}&rating=#{CONTENT_RATING_LIMIT}"
+
+# Enable console output for development
+DEBUG = process.env.DEBUG or false
+
+_debug = ->
+  console.log.apply(this, arguments) if DEBUG
+
+_createTagsParam = (tagString) ->
+  ignoredCharactersRegex = /['",]/g
+  whitespaceRegex = /\s/g
+  tagString = tagString.trim()
+  tagString = tagString.replace ignoredCharactersRegex, ''
+  tagString = tagString.replace whitespaceRegex, '+'
+  _debug 'tagString', tagString
+  tagString
+
+_makeApiCall = (msg, url, callback) ->
   msg.http(url).get() (err, res, body) ->
-    response = JSON.parse(body);
-    if response.data.empty()
-      message = "No gifs match your tags :("
+    if err or res.statusCode isnt 200
+      msg.send 'Apologies -- something went wrong looking for your GIF.'
     else
-      message = response.data.image_url
+      response = JSON.parse(body).data
+      _debug 'response', response
+      callback response

-    msg.send(message)
+getRandomGif = (msg, tags) ->
+  url = ENDPOINT_URL_RANDOM
+  if tags
+    tagsParam = _createTagsParam tags
+    url += "&tag=#{tagsParam}"
+  _debug 'url', url
+  _makeApiCall msg, url, (response) ->
+    if response.image_url
+     msg.send response.image_url
+    else
+      searchedWithTags = msg.match.length is 1
+      if searchedWithTags
+        msg.send "Apologies -- I couldn't find any GIFs! This is very strange, indeed."
+      else
+        msg.send "Apologies -- I couldn't find any GIFs matching '#{msg.match[1]}'."

+# Commands to expose to Hubot
 module.exports = (robot) ->
-  robot.respond /gif me|giphy(.*)/i, (msg) ->
-    tags = msg.match[1].trim().split(', ')
-    getRandomGiphyGif(msg, tags)
+  robot.respond /gif me(.*)|giphy(.*)/i, (msg) ->
+    tags = msg.match[1]
+    getRandomGif msg, tags


Fix indentation

Fix indentation

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -40,26 +40,23 @@
   constructor: (@ast, @node, @document, @model, @controller) ->
     @element = @node.element
     self = this # work around a bug in coffeescript
-    callback = (e) ->
+    Serenade.bindEvent @element, @ast.name, (e) ->
       preventDefault(e) if self.ast.preventDefault
       self.controller[self.ast.value](e)
-    Serenade.bindEvent(@element, @ast.name, callback)

 class TwoWayBinding
-    constructor: (@ast, @node, @document, @model, @controller) ->
-      @node.ast.name in ["input", "textarea"] or throw SyntaxError "invalid node type #{@node.ast.name} for two way binding"
-      @element = @node.element
-      callback = (e) =>
-        set(@model, @ast.value, @element.value)
-      Serenade.bindEvent(@element, @ast.name, callback)
-      @update()
-      @model.bind? "change:#{@ast.value}", (value) => @update()
-
-    update: ->
-      val = get(@model, @ast.value)
-      val = "" if val == undefined
-      @element.value = val
-
+  constructor: (@ast, @node, @document, @model, @controller) ->
+    @node.ast.name in ["input", "textarea"] or throw SyntaxError "invalid node type #{@node.ast.name} for two way binding"
+    @element = @node.element
+    Serenade.bindEvent @element, @ast.name, (e) =>
+      set(@model, @ast.value, @element.value)
+    @update()
+    @model.bind? "change:#{@ast.value}", (value) => @update()
+
+  update: ->
+    val = get(@model, @ast.value)
+    val = "" if val == undefined
+    @element.value = val

 class Attribute
   constructor: (@ast, @node, @document, @model, @controller) ->


Farang day

Farang day

--- scripts/lockstep-cron.coffee
+++ scripts/lockstep-cron.coffee
@@ -19,14 +19,38 @@

   cronJob.start()

-  cronJob = new CronJob '00 17 * * 5', =>
-    robot.messageRoom '#general', '@channel Beer & Pokdeng Time!'
-  , null, false, 'Asia/Bangkok'
-
-  cronJob.start()
-
   cronJob = new CronJob '00 18 * * 1-5', =>
     robot.messageRoom '#general', '@here Hey, turn off air filter, NOW!!!'
   , null, false, 'Asia/Bangkok'

   cronJob.start()
+
+  cronJob = new CronJob '00 11 * * 1', =>
+    robot.messageRoom '#general', '@channel @mac is Farang! I bet you anything he will not understand Thai anymore!'
+  , null, false, 'Asia/Bangkok'
+
+  cronJob.start()
+
+  cronJob = new CronJob '00 11 * * 2', =>
+    robot.messageRoom '#general', '@channel @kirati_tripler is Farang! I bet you anything he will not understand Thai anymore!'
+  , null, false, 'Asia/Bangkok'
+
+  cronJob.start()
+
+  cronJob = new CronJob '00 11 * * 3', =>
+    robot.messageRoom '#general', '@channel @kantaphon_tripler is Farang! I bet you anything he will not understand Thai anymore!'
+  , null, false, 'Asia/Bangkok'
+
+  cronJob.start()
+
+  cronJob = new CronJob '00 11 * * 4', =>
+    robot.messageRoom '#general', '@channel @ekkapob is Farang! I bet you anything he will not understand Thai anymore!'
+  , null, false, 'Asia/Bangkok'
+
+  cronJob.start()
+
+  cronJob = new CronJob '00 11 * * 5', =>
+    robot.messageRoom '#general', '@channel @nut is Farang! I bet you anything he will not understand Thai anymore!'
+  , null, false, 'Asia/Bangkok'
+
+  cronJob.start()


added timeout for generate step

added timeout for generate step

--- test-integration/scores.coffee
+++ test-integration/scores.coffee
@@ -33,6 +33,7 @@

   @it 'generates export', ->
     @scores.el.generateExport().click()
+    @addTimeout(60)
     @scores.downloadExport()


bye jamie

bye jamie

--- scripts/name-expander.coffee
+++ scripts/name-expander.coffee
@@ -6,7 +6,6 @@
 cliff = "cdickerson"
 ivey = "michael.ivey"
 chong = "Chong Yan"
-jamie = "Jamie Winsor"
 jesse = "Jesse Howarth"
 kyle = "kallan"
 andrew = "Andrew Garson"
@@ -20,7 +19,7 @@

 noc_members = ["Carter1", "Cames1", "jdowns", "jdowns1", "jtramullas", "nstuhring", "ctrytten1", "ctrytten", "akim", "nsanches", "jkim", "ebednik", "nhooper"]

-dert_mermbers = ["CDERCKERSERN", "MAHCHERLERVER", "JKERL", "JERMAH WERNSER", "CHIN YERN", "KERLERN", "ERNDRERW GERSERN", "JERSTEN CERMPBELL"]
+dert_mermbers = ["CDERCKERSERN", "MAHCHERLERVER", "JKERL", "CHIN YERN", "KERLERN", "ERNDRERW GERSERN", "JERSTEN CERMPBELL"]

 build_members = ["mvuong", "mstewart", "jromigh", "pmckeown", "pmckeown2", "pmckeown_home2", "hzhang", "helen"]

@@ -32,7 +31,6 @@

 merlin_lax_members = [
   josiah,
-  jamie,
   chong,
   kyle,
   andrew,


fixed error when basic auth is not enabled

--- src/middleware/tlsAuthentication.coffee
+++ src/middleware/tlsAuthentication.coffee
@@ -70,5 +70,6 @@
         logger.info "Certificate Authentication Failed: the certificate's common name #{subject.CN} did not match any client's domain attribute"
         yield next
     else
-      this.response.status = "unauthorized"
+      this.authenticated = null
+      yield next
       logger.info "Request is NOT authenticated via TLS: #{this.req.client.authorizationError}"


LineWrapper uses SpanIndex to store wrapped lines.

--- src/atom/line-wrapper.coffee
+++ src/atom/line-wrapper.coffee
@@ -1,5 +1,6 @@
 _ = require 'underscore'
 EventEmitter = require 'event-emitter'
+SpanIndex = require 'span-index'
 Point = require 'point'
 Range = require 'range'

@@ -13,15 +14,20 @@
   setMaxLength: (@maxLength) ->
     oldRange = new Range
     oldRange.end.row = @screenLineCount() - 1
-    oldRange.end.column = _.last(_.last(@wrappedLines).screenLines).text.length
+    oldRange.end.column = _.last(@index.last().screenLines).text.length
     @buildWrappedLines()
     newRange = new Range
     newRange.end.row = @screenLineCount() - 1
-    newRange.end.column = _.last(_.last(@wrappedLines).screenLines).text.length
+    newRange.end.column = _.last(@index.last().screenLines).text.length
     @trigger 'change', { oldRange, newRange }

+  getSpans: (wrappedLines) ->
+    wrappedLines.map (line) -> line.screenLines.length
+
   buildWrappedLines: ->
-    @wrappedLines = @buildWrappedLinesForBufferRows(0, @buffer.lastRow())
+    @index = new SpanIndex
+    wrappedLines = @buildWrappedLinesForBufferRows(0, @buffer.lastRow())
+    @index.insert 0, @getSpans(wrappedLines), wrappedLines

   handleChange: (e) ->
     oldRange = new Range
@@ -29,13 +35,15 @@
     bufferRow = e.oldRange.start.row
     oldRange.start.row = @firstScreenRowForBufferRow(e.oldRange.start.row)
     oldRange.end.row = @lastScreenRowForBufferRow(e.oldRange.end.row)
-    oldRange.end.column = _.last(@wrappedLines[e.oldRange.end.row].screenLines).text.length
+    oldRange.end.column = _.last(@index.at(e.oldRange.end.row).screenLines).text.length

-    @wrappedLines[e.oldRange.start.row..e.oldRange.end.row] = @buildWrappedLinesForBufferRows(e.newRange.start.row, e.newRange.end.row)
+    { start, end } = e.oldRange
+    wrappedLines = @buildWrappedLinesForBufferRows(e.newRange.start.row, e.newRange.end.row)
+    @index.splice start.row, end.row, @getSpans(wrappedLines), wrappedLines

     newRange = oldRange.copy()
     newRange.end.row = @lastScreenRowForBufferRow(e.newRange.end.row)
-    newRange.end.column = _.last(@wrappedLines[e.newRange.end.row].screenLines).text.length
+    newRange.end.column = _.last(@index.at(e.newRange.end.row).screenLines).text.length

     @trigger 'change', { oldRange, newRange }

@@ -44,7 +52,7 @@

   lastScreenRowForBufferRow: (bufferRow) ->
     startRow = @screenPositionFromBufferPosition([bufferRow, 0]).row
-    startRow + (@wrappedLines[bufferRow].screenLines.length - 1)
+    startRow + (@index.at(bufferRow).screenLines.length - 1)

   buildWrappedLinesForBufferRows: (start, end) ->
     for row in [start..end]
@@ -83,13 +91,10 @@

   screenPositionFromBufferPosition: (bufferPosition, allowEOL=false) ->
     bufferPosition = Point.fromObject(bufferPosition)
-    row = 0
-    for wrappedLine in @wrappedLines[0...bufferPosition.row]
-      row += wrappedLine.screenLines.length
-
+    row = @index.spanForIndex(bufferPosition.row)
     column = bufferPosition.column

-    screenLines = @wrappedLines[bufferPosition.row].screenLines
+    screenLines = @index.at(bufferPosition.row).screenLines
     for screenLine, index in screenLines
       break if index == screenLines.length - 1
       if allowEOL
@@ -104,34 +109,29 @@

   bufferPositionFromScreenPosition: (screenPosition) ->
     screenPosition = Point.fromObject(screenPosition)
-    bufferRow = 0
-    currentScreenRow = 0
-    for wrappedLine in @wrappedLines
-      for screenLine in wrappedLine.screenLines
-        if currentScreenRow == screenPosition.row
-          return new Point(bufferRow, screenLine.startColumn + screenPosition.column)
-        currentScreenRow++
-      bufferRow++
+    { index, offset } = @index.indexForSpan(screenPosition.row)
+    row = index
+    column = @index.at(row).screenLines[offset].startColumn + screenPosition.column
+    new Point(row, column)

   screenLineForRow: (screenRow) ->
     @screenLinesForRows(screenRow, screenRow)[0]

   screenLinesForRows: (startRow, endRow) ->
     screenLines = []
-    currentScreenRow = 0
-    for wrappedLine in @wrappedLines
-      for screenLine in wrappedLine.screenLines
-        screenLines.push screenLine if currentScreenRow >= startRow
-        currentScreenRow++
-        return screenLines if currentScreenRow > endRow
+
+    { values, startOffset, endOffset } = @index.sliceBySpan(startRow, endRow)
+
+    screenLines.push(values[0].screenLines[startOffset..-1]...)
+    for wrappedLine in values[1...-1]
+      screenLines.push(wrappedLine.screenLines...)
+    screenLines.push(_.last(values).screenLines[0..endOffset]...)
+    screenLines

   screenLines: ->
     @screenLinesForRows(0, @screenLineCount() - 1)

   screenLineCount: ->
-    count = 0
-    for wrappedLine, i in @wrappedLines
-      count += wrappedLine.screenLines.length
-    count
+    @index.lengthBySpan()

 _.extend(LineWrapper.prototype, EventEmitter)


debug  proxy mocking in appliation operation tests

--- test/applications_tests.coffee
+++ test/applications_tests.coffee
@@ -110,12 +110,10 @@
             should.exist body.start.repository
             should.exist body.start.scripts
         @haibu.listen(9002)
-        @proxy = fakeServer msg: "ok", 201, (body) ->
-            should.exist body.route
-            should.exist body.port
-            body.route.should.equal "my-app"
-            body.port.should.equal 8001
-        @proxy.listen(4000)
+
+        @proxy = fakeServer msg: "ok", 200, (body) ->
+
+        @proxy.listen 9104

     after ->
         @haibu.close()
@@ -173,10 +171,9 @@
         @haibu = fakeServer { drone: { port: 8001 } }, 200, (body) ->

         @haibu.listen 9002
-        @proxy = fakeServer msg: "ok", 204, (body) ->
-            should.exist body.route
-            body.route.should.equal "/apps/my-app"
-        @proxy.listen 4000
+        @proxy = fakeServer msg: "ok", 200, (body) ->
+
+        @proxy.listen 9104

     after ->
         @haibu.close()
@@ -206,8 +203,8 @@
             should.exist body.repository
             should.exist body.scripts
         @haibu.listen 9002
-        @proxy = fakeServer msg: "ok", 204, (body) ->
-        @proxy.listen 4000
+        @proxy = fakeServer msg: "ok", 200, (body) ->
+        @proxy.listen 9104

     after ->
         @haibu.close()


Fix help command now that getMatchCommand() is async

--- lib/actions/help.coffee
+++ lib/actions/help.coffee
@@ -82,39 +82,42 @@

 	console.log()

-exports.command = (params) ->
-	command = capitano.state.getMatchCommand(params.command)
+exports.command = (params, options, done) ->
+	capitano.state.getMatchCommand params.command, (error, command) ->
+		return done(error) if error?

-	if not command? or command.isWildcard()
-		return capitano.defaults.actions.commandNotFound(params.command)
+		if not command? or command.isWildcard()
+			return capitano.defaults.actions.commandNotFound(params.command)

-	console.log("Usage: #{command.signature}")
+		console.log("Usage: #{command.signature}")

-	if command.help?
-		console.log("\n#{command.help}")
-	else if command.description?
-		console.log("\n#{_.str.humanize(command.description)}")
+		if command.help?
+			console.log("\n#{command.help}")
+		else if command.description?
+			console.log("\n#{_.str.humanize(command.description)}")

-	if not _.isEmpty(command.options)
-		console.log('\nOptions:\n')
+		if not _.isEmpty(command.options)
+			console.log('\nOptions:\n')

-		options = _.map command.options, (option) ->
-			option.signature = buildOptionSignatureHelp(option)
-			return option
+			options = _.map command.options, (option) ->
+				option.signature = buildOptionSignatureHelp(option)
+				return option

-		optionSignatureMaxLength = _.max _.map options, (option) ->
-			return option.signature.toString().length
+			optionSignatureMaxLength = _.max _.map options, (option) ->
+				return option.signature.toString().length

-		for option in options
-			console.log(getOptionHelp(option, optionSignatureMaxLength))
+			for option in options
+				console.log(getOptionHelp(option, optionSignatureMaxLength))

-		console.log()
+			console.log()
+
+		return done()

 exports.help =
 	signature: 'help [command...]'
 	description: 'show help'
-	action: (params) ->
+	action: (params, options, done) ->
 		if params.command?
-			exports.command(arguments...)
+			exports.command(params, options, done)
 		else
-			exports.general(arguments...)
+			exports.general(params, options, done)


replace sync reads with async

replace sync reads with async

--- lib/adapter_base.coffee
+++ lib/adapter_base.coffee
@@ -1,6 +1,8 @@
-fs = require 'fs'
-W = require 'when'
-_ = require 'lodash'
+fs        = require 'fs'
+W         = require 'when'
+_         = require 'lodash'
+nodefn    = require 'when/node'
+readFile  = _.partialRight(nodefn.lift(fs.readFile), 'utf8')

 class Adapter
   render: (str, opts = {}) ->
@@ -9,7 +11,7 @@
     @_render(str, opts)

   renderFile: (file, opts = {}) ->
-    @render(fs.readFileSync(file, 'utf8'), _.extend(opts, {filename: file}))
+    readFile(file).then _.partialRight(@render, _.extend(opts, {filename: file})).bind(@)

   compile: (str, opts = {}) ->
     if not @_compile
@@ -17,7 +19,7 @@
     @_compile(str, opts)

   compileFile: (file, opts = {}) ->
-    @compile(fs.readFileSync(file, 'utf8'), _.extend(opts, {filename: file}))
+    readFile(file).then _.partialRight(@compile, _.extend(opts, {filename: file})).bind(@)

   compileClient: (str, opts = {}) ->
     if not @_compileClient
@@ -25,9 +27,6 @@
     @_compileClient(str, opts)

   compileFileClient: (file, opts = {}) ->
-    @compileClient(
-      fs.readFileSync(file, 'utf8'),
-      _.extend(opts, {filename: file})
-    )
+    readFile(file).then _.partialRight(@compileClient, _.extend(opts, {filename: file})).bind(@)

 module.exports = Adapter


Updating description of a test

Updating description of a test

--- test/create-images.spec.coffee
+++ test/create-images.spec.coffee
@@ -20,7 +20,7 @@
     del.sync(["#{outputPath}/*.png"])
   )

-  it 'Has 3 images in the test-output folder', (done) ->
+  it 'Has 1 image in the test-output folder', (done) ->
     sut( pumlPath, outputPath, (err, results) ->
       results.should.have.lengthOf(1)
       should.not.exist(err)


Simplify forming the URL for HTTP requests

--- src/kissmetrics.coffee
+++ src/kissmetrics.coffee
@@ -141,8 +141,7 @@
   #   `path` are used, and `host` is required.

   _httpsRequest: (args) ->
-    args.path ?= ''
-    url = "https://#{args.host}/#{args.path}"
+    url = "https://#{args.host}/#{args.path || ''}"

     if NODEJS is on then https.get url else (new Image()).src = url


Call reloadStylesheets instead of reloadStylesheet in spec

--- spec/package-spec.coffee
+++ spec/package-spec.coffee
@@ -92,7 +92,7 @@

       it "reloads without readding to the stylesheets list", ->
         expect(theme.getStylesheetPaths().length).toBe 3
-        theme.reloadStylesheet(theme.getStylesheetPaths()[0])
+        theme.reloadStylesheets()
         expect(theme.getStylesheetPaths().length).toBe 3

     describe "events", ->


[sim] reject race condition when not logged in yet

--- server/index.coffee
+++ server/index.coffee
@@ -98,6 +98,7 @@
               return
             attachEvents(user, spark)
             server.join(spark)
+            spark.loggedIn = true
             spark.send('loginSuccess')
             lobby.add(spark)

@@ -109,6 +110,7 @@
     console.error(err.message, err.stack)

   primus.on 'disconnection', (spark) ->
+    return  unless spark.loggedIn
     server.leave(spark)
     spark.emit("cancelFindBattle")  unless spark.user.hasSparks()


Try to create @root if it doesn't exist

Try to create @root if it doesn't exist

--- src/pow/configuration.coffee
+++ src/pow/configuration.coffee
@@ -1,12 +1,28 @@
-fs     = require "fs"
-{join} = require "path"
-async  = require "async"
+fs    = require "fs"
+path  = require "path"
+async = require "async"

 getFilenamesForHost = (host) ->
   parts = host.split "."
   length = parts.length - 2
   for i in [0..length]
     parts.slice(i, length + 1).join "."
+
+mkdirp = (dirname, callback) ->
+  fs.lstat (p = path.normalize dirname), (err, stats) ->
+    if err
+      paths = [p].concat(p = path.dirname p until p in ["/", "."])
+      async.forEachSeries paths.reverse(), (p, next) ->
+        path.exists p, (exists) ->
+          if exists then next()
+          else fs.mkdir p, 0755, (err) ->
+            if err then callback err
+            else next()
+      , callback
+    else if stats.isDirectory()
+      callback()
+    else
+      callback "file exists"

 module.exports = class Configuration
   constructor: (options = {}) ->
@@ -15,7 +31,7 @@
     @dnsPort  = options.dnsPort  ? 20560
     @timeout  = options.timeout  ? 15 * 60 * 1000
     @domain   = options.domain   ? "test"
-    @root     = options.root     ? join process.env.HOME, ".pow"
+    @root     = options.root     ? path.join process.env.HOME, ".pow"

   findApplicationRootForHost: (host, callback) ->
     @gatherApplicationRoots (err, roots) =>
@@ -27,21 +43,23 @@

   gatherApplicationRoots: (callback) ->
     roots = {}
-    fs.readdir @root, (err, files) =>
+    mkdirp @root, (err) =>
       return callback err if err
-      async.forEach files, (file, next) =>
-        path = join @root, file
-        fs.lstat path, (err, stats) ->
-          return callback err if err
-          if stats.isSymbolicLink()
-            fs.readlink path, (err, resolvedPath) ->
-              return callback err if err
-              roots[file] = resolvedPath
+      fs.readdir @root, (err, files) =>
+        return callback err if err
+        async.forEach files, (file, next) =>
+          root = path.join @root, file
+          fs.lstat root, (err, stats) ->
+            return callback err if err
+            if stats.isSymbolicLink()
+              fs.readlink root, (err, resolvedPath) ->
+                return callback err if err
+                roots[file] = resolvedPath
+                next()
+            else if stats.isDirectory()
+              roots[file] = root
               next()
-          else if stats.isDirectory()
-            roots[file] = path
-            next()
-          else
-            next()
-      , (err) ->
-        callback err, roots
+            else
+              next()
+        , (err) ->
+          callback err, roots


ComputeProvider: use async.series instead of Bongo.daisy [#109801982]

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -1,7 +1,8 @@
-{ Base, secure, signature, daisy } = require 'bongo'
+{ Base, secure, signature } = require 'bongo'
 KodingError = require '../../error'

 { argv }    = require 'optimist'
+{ series }  = require 'async'
 KONFIG      = require('koding-config-manager').load("main.#{argv.c}")
 teamutils   = require './teamutils'
 konstraints = require 'konstraints'
@@ -232,7 +233,7 @@
       # provided in template's machines array ~ GG
       template.machines?.forEach (machineInfo) ->

-        queue.push ->
+        queue.push (next) ->

           # Set the stack as newly create JComputeStack
           machineInfo.stack         = stack
@@ -252,7 +253,7 @@
             ComputeProvider.create client, machineInfo, (err, machine) ->
               results.machines.push { err, obj: machine }

-              return queue.next()  unless machine
+              return next()  unless machine

               # Create default workspace for the machine
               JWorkspace.createDefault client, machine.uid, (err) ->
@@ -261,7 +262,7 @@
                   console.log \
                     'Failed to create default workspace', machine.uid, err

-                queue.next()
+                next()

           # This is optional, since for koding group for example
           # we don't want to add our admins into users machines ~ GG
@@ -284,11 +285,8 @@
             else
               create machineInfo

-      queue.push ->
-
-        callback null, { stack, results }
-
-      daisy queue
+
+      series queue, -> callback null, { stack, results }

   # Just takes the plan name and the stack template content generates rules
@@ -330,14 +328,16 @@

     queue = []

-    stackTemplates.forEach (stackTemplateId) -> queue.push ->
+    stackTemplates.forEach (stackTemplateId) -> queue.push (next) ->
       ComputeProvider.validateTemplate client, stackTemplateId, group, (err) ->
         return callback err  if err
-        queue.next()
-
-    queue.push -> callback null
-
-    daisy queue
+        next()
+
+    series queue, -> callback null
+
+
+
+

   # Auto create stack operations ###
@@ -415,17 +415,17 @@

     { template, account, group } = {}

-    daisy queue = [
-
-      ->
+    series [
+
+      (next) ->
         fetchGroupStackTemplate client, (err, _res) ->
           return callback err  if err

           { template, account, group } = res = _res

-          queue.next()
-
-      ->
+          next()
+
+      (next) ->
         instanceCount = template.machines?.length or 0
         change        = 'increment'

@@ -435,23 +435,23 @@

           return callback err  if err

-          queue.next()
-
-      ->
+          next()
+
+      (next) ->
         checkTemplateUsage template, account, (err) ->
           return callback err  if err

-          queue.next()
-
-      ->
+          next()
+
+      (next) ->
         account.addStackTemplate template, (err) ->
           return callback err  if err

           res.client = client

-          queue.next()
-
-      ->
+          next()
+
+      (next) ->
         ComputeProvider.generateStackFromTemplate res, options, (err, stack) ->
           if err
             # swallowing errors for followings since we need the real error ~GG


Ensure recalculation of split handles after splitting

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -271,15 +271,18 @@

     splitView.on 'ResizeDidStop', kd.utils.throttle 500, @bound 'doResize'

-    @recalculateCloseHandles()
-
-
-  recalculateCloseHandles: ->
-
-    if @ideViews.length > 1
-      @ideViews.forEach (i) -> i.holderView.showCloseHandle()
-    else
-      @ideViews.forEach (i) -> i.holderView.hideCloseHandle()
+    @recalculateHandles()
+
+
+  recalculateHandles: ->
+
+    closeHandleMethod = if @ideViews.length > 1
+    then 'showCloseHandle'
+    else 'hideCloseHandle'
+
+    @ideViews.forEach (view) ->
+      view.holderView[closeHandleMethod]()
+      view.ensureSplitHandlers()

   mergeSplitView: ->
@@ -310,7 +313,7 @@

     splitView.merge()

-    @recalculateCloseHandles()
+    @recalculateHandles()

   handleSplitMerge: (views, container, parentSplitView, panelIndexInParent) ->


ide: try showing onboarding on AppIsBeingShown if mounted machine is active

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -92,12 +92,12 @@
     @layout = ndpane(16)
     @layoutMap = new Array(16*16)

-    {windowController} = kd.singletons
+    {windowController, appManager} = kd.singletons
     windowController.addFocusListener @bound 'setActivePaneFocus'

     @workspace.once 'ready', => @getView().addSubView @workspace.getView()

-    kd.singletons.appManager.on 'AppIsBeingShown', (app) =>
+    appManager.on 'AppIsBeingShown', (app) =>

       return  unless app instanceof IDEAppController

@@ -106,9 +106,12 @@
       # Temporary fix for IDE is not shown after
       # opening pages which uses old SplitView.
       # TODO: This needs to be fixed. ~Umut
-      kd.singletons.windowController.notifyWindowResizeListeners()
+      windowController.notifyWindowResizeListeners()

       @resizeActiveTerminalPane()
+
+      {onboardingController} = kd.singletons
+      onboardingController?.runOnboarding 'IDE'  if appManager.frontApp is this and @isMachineRunning()

   prepareIDE: (withFakeViews = no) ->
@@ -324,7 +327,7 @@

   isMachineRunning: ->

-    return @mountedMachine.status.state is Running
+    return @mountedMachine?.status.state is Running

   createInitialView: (withFakeViews) ->
@@ -943,12 +946,12 @@
       else
         @addInitialViews()

-      { mainView, onboardingController } = kd.singletons
+      { mainView, onboardingController, appManager } = kd.singletons

       data = { machine, workspace: @workspaceData }
       mainView.activitySidebar.selectWorkspace data

-      onboardingController.runOnboarding 'IDE'
+      onboardingController.runOnboarding 'IDE'  if appManager.frontApp is this

       @emit 'IDEReady'


Change avatar resize to force sizes from settings

--- server/startup/avatar.coffee
+++ server/startup/avatar.coffee
@@ -18,7 +18,10 @@
 		height = RocketChat.settings.get 'Accounts_AvatarSize'
 		width = height

-		RocketChatFile.gm(readStream, file.fileName).background('#ffffff').resize(width, height+'^>').gravity('Center').extent(width, height).stream('jpeg').pipe(writeStream)
+		# - Resize the image using the width or height as minium value
+		# - Keep the image aspect ratio
+		# - Crop image to keep max size of width and height
+		RocketChatFile.gm(readStream, file.fileName).resize(width, height+'^').gravity('Center').crop(width, height).stream('jpeg').pipe(writeStream)

 	path = "~/uploads"


Replace all ChatMessage.upsert

Replace all ChatMessage.upsert

--- packages/rocketchat-lib/server/sendMessage.coffee
+++ packages/rocketchat-lib/server/sendMessage.coffee
@@ -16,7 +16,7 @@
 	message = RocketChat.callbacks.run 'beforeSaveMessage', message

 	if message._id? and options?.upsert is true
-		ChatMessage.upsert {_id: message._id}, message
+		RocketChat.models.Messages.upsert {_id: message._id}, message
 	else
 		message._id = RocketChat.models.Messages.insert message


Removed some test flakiness.

Removed some test flakiness.

--- test/src/ws_client_test.coffee
+++ test/src/ws_client_test.coffee
@@ -32,13 +32,13 @@
       @wsClient = new WsClient @wsUrl,
           silenceTimeoutMs: 10, pingSlackMs: 20, rttMs: 20

-    it 'sends 8-10 pings in 100ms', (done) ->
+    it 'sends 7-10 pings in 100ms', (done) ->
+      pingSpy = @sandbox.spy @wsClient, 'sendPing'
+      closeSpy = @sandbox.spy @wsClient, 'close'
       @wsClient.connected.then =>
-        pingSpy = @sandbox.spy @wsClient, 'sendPing'
-        closeSpy = @sandbox.spy @wsClient, 'close'
         onTimeout = =>
           expect(closeSpy.callCount).to.equal 0
-          expect(pingSpy.callCount).to.be.at.least 8
+          expect(pingSpy.callCount).to.be.at.least 7
           expect(pingSpy.callCount).to.be.at.most 10
           done()
         setTimeout onTimeout, 100