Add Cornerstone TO DO items.

Add Cornerstone TO DO items.

--- app/javascripts/image.coffee
+++ app/javascripts/image.coffee
@@ -8,14 +8,17 @@
         'stackScrollTool', 'exampleImageLoader'], (ng, _, _s) ->
   image = ng.module 'qiprofile.image', ['qiprofile.file', 'qiprofile.modeling', 'vr.directives.slider']

-
   # TO DO - Replace the Image Detail page renderer with the Cornerstone
   #   (https://github.com/chafey/cornerstone) StackScroll example
   #   (https://github.com/chafey/cornerstoneTools/blob/master/examples/stackScroll/index.html).
   #   For now, add the #dicomImage id tag to the image-detail.jade qi-image element,
   #   excise the example JavaScript into a static/javascripts file,
   #   and replace only the image.coffee renderer.render() call with a call to the javascript.
-
+  # FOLLOW UP TASKS:
+  #   1. Use Angular's jQuery Lite as the Cornerstone dependency. Post a message to Cornerstone if it does not work.
+  #   2. Implement a simpler Cornerstone example, e.g. display only one slice.
+  #   3. Convert to a simple CoffeeScript without the clip controls.
+  #   4. Create a Balsamiq demo user interaction model.

   image.factory 'Image', ['$rootScope', '$q', 'File', 'Modeling', ($rootScope, $q, File, Modeling) ->
     # The root scope {parent id: [Image objects]} cache.


use readUInt32BE (bye bye, 0.4.x!)

use readUInt32BE (bye bye, 0.4.x!)

--- png-guts.coffee
+++ png-guts.coffee
@@ -32,7 +32,7 @@
     readData stream, (data) =>
       pos = 8
       while pos < data.length
-        size = read_uint32be data, pos
+        size = data.readUInt32BE pos
         totalChunkSize = 4 + 4 + size + 4
         type = data.slice(pos + 4, pos + 8).toString('utf-8')
         raw = data.slice pos, (pos + totalChunkSize)
@@ -41,23 +41,6 @@
       @emit 'end'

-read_uint32be = (buf, pos) ->
-  (
-    (buf[pos + 0] << 24) +
-    (buf[pos + 1] << 16) +
-    (buf[pos + 2] << 8) +
-    (buf[pos + 3]))
-
-
-uint32be = (n) ->
-  new Buffer [
-    (n >> 24) % 256,
-    (n >> 16) % 256,
-    (n >> 8) % 256,
-    (n) % 256
-  ]
-
-
 module.exports = {BIN_PATH, main, PNGChunkReader, PNG_FILE_HEADER}
 if not module.parent
   main()


[fix] BaseAPI: constructor

[fix] BaseAPI: constructor

--- app/scripts/apis/base.coffee
+++ app/scripts/apis/base.coffee
@@ -5,8 +5,8 @@

   class BaseAPI

-    constructor: (args) ->
-      @initialize args
+    constructor: ->
+      @initialize.apply @, arguments

     baseUrl: ''


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera_fade.coffee
+++ src/camera_fade.coffee
@@ -10,7 +10,7 @@
 ###
 Camera.Fade = (I, self) ->
   Object.reverseMerge I,
-    flalpha: 0
+    flashTargalpha: 0

   defaultParams =
     duration: 30


Prevent duplicate submit by double click

--- public/assets/coffee/clothes-add.coffee
+++ public/assets/coffee/clothes-add.coffee
@@ -31,8 +31,9 @@
     $(@).addClass('active')
     $("#color").val(color)

-  $('.btn-discard:not(.disabled)').click (e) ->
+  $('.btn-discard').click (e) ->
     e.preventDefault()
+    return if $(@).hasClass('disabled')
     $(@).addClass('disabled')
     $('#status-id').val($(@).data('status-id'))
     $('#discard').val('1')


for now delte families on update

for now delte families on update

--- app/server/lib/gDrive.coffee
+++ app/server/lib/gDrive.coffee
@@ -135,8 +135,8 @@
         gDrive._families.push val

     # Might not want this for flight
-    #console.log("remove families")
-    #Families.remove({})
+    console.log("remove families")
+    Families.remove({})

     #console.log("Updating family list from spreadsheet", gDrive._families)
     for family in gDrive._families


remove debugging

remove debugging

--- src/listen.coffee
+++ src/listen.coffee
@@ -14,7 +14,6 @@
     return line.replace /(\n|\r)+$/, ""

   handleSave = (variables, data) ->
-    console.error "handleSave"
     file = variables.token
     if fs.existsSync file or fileIsWritable file
       try
@@ -40,7 +39,6 @@

   handleClose = (variables, data) ->
-    console.error "handleClose"
     file = variables.token
     console.error "Closed #{file}" if options.verbose

@@ -54,42 +52,28 @@
   # this is difficult to parse because we don't know in advance if we have the full message
   # or due to lack of unique delimiter, so we need to make this function reentrant
   handleCmd = () ->
-    console.error "handleCmd: #{strBuffer}"
-
-    console.error "foo1"
-
     unless cmdObj.cmd?
       [cmd, strBuffer] = readLine strBuffer
       return false unless cmd?
       cmdObj.cmd = cmd

-    console.error "foo2"
-
     cmdObj.variables = {}

     assignData = () ->
-      console.error "foo7.1"
       {size} = cmdObj
       return false unless strBuffer.length >= size
       cmdObj.data = strBuffer[..(size - 1)]
       strBuffer = strBuffer[size..]
-      console.error "foo7.2"
       return true
-
-    console.error "foo3"

     # in case we exited after we got data size, but not the whole data payload
     if cmdObj.size? and not cmdObj.data?
       return false unless assignData()

-    console.error "foo4"
-
     # read in variables
     [line, strBuffer] = readLine strBuffer
     return false unless line?
-    console.error "foo5"
     while line isnt "\n"
-      console.error "foo6"
       [name, value] = line.split(": ", 2)
       if name is "data"
         cmdObj.size = parseInt(value)
@@ -98,12 +82,6 @@
         cmdObj.variables[name] = value
       [line, strBuffer] = readLine strBuffer
       return false unless line?
-
-    console.error "foo8"
-
-    console.error "== debug =="
-    console.error cmdObj.cmd
-    console.dir cmdObj.variables

     switch cmdObj.cmd
       when "save" then handleSave(cmdObj.variables, cmdObj.data)
@@ -125,7 +103,6 @@
     return true

   tcp.on 'data', (data) ->
-    console.error "data: #{data}"
     handleData = () ->
       return unless strBuffer.length > 0
       if firstLine


Add bug moves

Add bug moves

--- src/client/game.coffee
+++ src/client/game.coffee
@@ -65,7 +65,7 @@
   bugs.grid[location.x][location.y] =
     move:      move
     name:      name
-    direciton: direction
+    direction: direction
     location:  location
     color:     color
     serial:    (bugs.next_serial += 1)
@@ -92,14 +92,71 @@
   for bug in bug_list
     move_bug bugs, bug

-  console.log 'end iteration'
+info_at_location = (grid, bug, x, y) ->
+  info = if x < 0 or y < 0 or x == 59 or y == 39
+    'WALL'
+  else if !grid[x][y]?
+    'EMPTY'
+  else if grid[x][y].name == bug.name
+    'SAME'
+  else
+    'OTHER'
+
+  info: info, x: x, y: y
+
+direction_name = (direction) ->
+  switch direction
+    when 0 then 'NORTH'
+    when 1 then 'EAST'
+    when 2 then 'SOUTH'
+    else        'WEST'

 move_bug = (bugs, bug) ->
   x = bug.location.x
   y = bug.location.y
+  d = bug.direction

-  bug.location.x = x + 1
-  bug.location.y = y + 1
+  surrounding_info = [
+    info_at_location bugs.grid, bug, x, y - 1   # North
+    info_at_location bugs.grid, bug, x + 1, y   # East
+    info_at_location bugs.grid, bug, x, y + 1   # South
+    info_at_location bugs.grid, bug, x - 1, y   # West
+  ]
+
+  while d-- > 0
+    surrounding_info.push(surrounding_info.shift())
+
+  info =
+    front:     surrounding_info[0].info
+    left:      surrounding_info[1].info
+    right:     surrounding_info[2].info
+    back:      surrounding_info[3].info
+    direction: bug.direciton
+
+  front = surrounding_info[0]
+  next  = bug.move info
+
+  switch next
+    when 'EAT'          then eat_bug  bugs, bug, front.info, front.x, front.y
+    when 'WALK_FORWARD' then walk_bug bugs, bug, front.info, front.x, front.y
+    when 'TURN_LEFT'    then turn_bug bug, -1
+    when 'TURN_RIGHT'   then turn_bug bug, +1
+
+eat_bug = (bugs, bug, info, x, y) ->
+  return unless info == 'OTHER'
+  bugs.grid[x][y].name  = bug.name
+  bugs.grid[x][y].move  = bug.move
+  bugs.grid[x][y].color = bug.color
+
+walk_bug = (bugs, bug, info, x, y) ->
+  return unless info == 'EMPTY'
+  bugs.grid[bug.location.x][bug.location.y] = null
+  bugs.grid[x][y] = bug
+  bug.location.x  = x
+  bug.location.y  = y
+
+turn_bug = (bug, offset) ->
+  bug.direction = (((bug.direction + offset) % 4) + 4) % 4

 render_game = (canvas, bugs) ->
   canvas.clear()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -269,7 +269,7 @@
     return scaleMatrix

   Matrix.skew = (skewX, skewY) ->
-    skewMatrix
+    skewMatrix = Matrix()
   ###*
   Returns a matrix that corresponds to a translation of tx, ty.
   @see Matrix#translate


make `document` dependency available

make `document` dependency available

--- src/client-last.coffee
+++ src/client-last.coffee
@@ -8,6 +8,7 @@

   lifetime =
     window: window
+    document: document

   source = hinoki.source [
     module.exports


:art: factor some duplicate code

:art: factor some duplicate code

and make behavior consistent again

--- src/list.coffee
+++ src/list.coffee
@@ -68,6 +68,17 @@
         packageLine
     console.log()

+  isPackageVisible: (options, manifest) ->
+    if options.argv.themes
+      if manifest.theme and not (options.argv.enabled and @isPackageDisabled(manifest.name))
+        return true
+    else if options.argv.packages
+      unless manifest.theme or (options.argv.enabled and @isPackageDisabled(manifest.name))
+        return true
+    else
+      unless options.argv.enabled and @isPackageDisabled(manifest.name)
+        return true
+
   listPackages: (directoryPath, options) ->
     packages = []
     for child in fs.list(directoryPath)
@@ -82,15 +93,9 @@
           manifest = CSON.readFileSync(manifestPath)
       manifest ?= {}
       manifest.name = child
-      if options.argv.themes
-        if manifest.theme and not (options.argv.enabled and @isPackageDisabled(manifest.name))
-          packages.push(manifest)
-      else if options.argv.packages
-        unless manifest.theme or (options.argv.enabled and @isPackageDisabled(manifest.name))
-          packages.push(manifest)
-      else
-        unless options.argv.enabled and @isPackageDisabled(manifest.name)
-          packages.push(manifest)
+
+      continue unless @isPackageVisible(options, manifest)
+      packages.push(manifest)

     packages

@@ -127,14 +132,7 @@
       packages = (metadata for packageName, {metadata} of _atomPackages)

       packages = packages.filter (metadata) =>
-        if options.argv.themes
-          metadata.theme
-        else if options.argv.packages
-          not metadata.theme
-        else if options.argv.enabled
-          not @isPackageDisabled(metadata.name)
-        else
-          true
+        @isPackageVisible(options, metadata)

       unless options.argv.bare or options.argv.json
         if options.argv.themes


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -144,7 +144,7 @@
     include: (modules...) ->
       for Module in modules
         if Module.isString?()
-        Module = Module.constantize()
+          Module = Module.constantize()
         self.extend Module(I, self)

       return self


Swap default foreground and background

Swap default foreground and background

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -4,8 +4,8 @@

   smilesMode = true

-  foregroundColor = '5f0000'
-  backgroundColor = 'ba5353'
+  foregroundColor = 'ba5353'
+  backgroundColor = '5f0000'
   compoundName = 'caffeine'

   previewElement = $ 'body'


seen is a Date object, not timestamp

seen is a Date object, not timestamp

--- src/collections.coffee
+++ src/collections.coffee
@@ -8,8 +8,8 @@
     return 1
   unless b.seen
     return -1
-  aSeen = new Date a.seen
-  bSeen = new Date b.seen
+  aSeen = if typeof a.seen is 'object' then a.seen else new Date a.seen
+  bSeen = if typeof b.seen is 'object' then b.seen else new Date b.seen
   if a.seen > b.seen
     return -1
   if b.seen > a.seen


chrome cookies: only remove on logout

chrome cookies: only remove on logout

- potentially fixes #547 after a logout/login cycle

--- src/main.coffee
+++ src/main.coffee
@@ -32,8 +32,6 @@
     cookiespath: paths.cookiespath
 )

-if fs.existsSync paths.chromecookie
-    fs.unlinkSync paths.chromecookie

 plug = (rs, rj) -> (err, val) -> if err then rj(err) else rs(val)

@@ -41,6 +39,7 @@
     promise = client.logout()
     promise.then (res) ->
       argv = process.argv
+      fs.unlinkSync(paths.chromecookie) if fs.existsSync(paths.chromecookie)
       spawn = require('child_process').spawn
       spawn argv.shift(), argv,
         cwd: process.cwd


Catch error event

Catch error event

--- src/main.coffee
+++ src/main.coffee
@@ -140,6 +140,8 @@
       componentConfig.jid = config.advertiseComponents[index]
       componentConfig.reconnect = true
       connection = new xmpp.Component(componentConfig)
+      connection.on "error", (e) ->
+          logger.error e
       connection.on "stanza", (stanza) =>
           # Just debug output:
           logger.trace "<< Extra connection request: #{stanza.toString()}"


use commonjs wrapper

use commonjs wrapper

--- src/main.coffee
+++ src/main.coffee
@@ -10,12 +10,16 @@
     reveal:
       exports: "Reveal"

-define ["underscore","reveal"], (_,Reveal) ->
+define (require) ->
+  _ = require "underscore"
+  Reveal = require "reveal"
+  $ = require "jquery"
   Reveal.initialize
     center: true
     control: true
     transition: "linear"
     history: true
   console.log("define main")
+
   {foo: true,"u":_}


correct test

correct test

--- proxy.coffee
+++ proxy.coffee
@@ -96,13 +96,20 @@
             \\n  >>> perform
             \\n  Send to client: :status
             \\n  <<<  [:response-time ms]'
-        if process.env.NODE_ENV is "production"
-            if not fs.existsSync './log/production.log'
+        if process.env.NODE_ENV is "development"
+            @app.use express.logger format
+        else
+            env = process.env.NODE_ENV
+            if not fs.existsSync './log'
                 fs.mkdirSync 'log'
-            logFile = fs.createWriteStream './log/production.log', {flags: 'a'}
+            logFile = fs.createWriteStream "./log/#{env}.log", {flags: 'w'}
             @app.use express.logger {stream: logFile, format: format}
-        else
-            @app.use express.logger format
+            if env is "production"
+                console.log = (text) ->
+                    logFile.write(text + '\n')
+
+                console.error = (text) ->
+                    logFile.write(text + '\n')

         @app.use (err, req, res, next) ->
             console.error err.stack


Age module

Age module

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -118,7 +118,6 @@
     active: true
     created: false
     destroyed: false
-
     includedModules: []
     excludedModules: []


Extract Default Modules

Extract Default Modules

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -156,7 +156,7 @@

   self

-GameObject.defaultModules =  = [Bindable, Ageable, Bounded, Clampable, Cooldown, Drawable, Expirable, Follow, Metered, Movable, Rotatable, TimedEvents, Tween]
+GameObject.defaultModules = [Bindable, Ageable, Bounded, Clampable, Cooldown, Drawable, Expirable, Follow, Metered, Movable, Rotatable, TimedEvents, Tween]

 ###*
 Construct an object instance from the given entity data.


auto-create userData dir when not present (first launch)

--- src/main.coffee
+++ src/main.coffee
@@ -14,15 +14,19 @@

 BrowserWindow = require('electron').BrowserWindow

+userData = path.normalize(app.getPath('userData'))
+fs.mkdirSync(userData) if not fs.existsSync userData
+
 paths =
-    rtokenpath:  path.normalize path.join app.getPath('userData'), 'refreshtoken.txt'
-    cookiespath: path.normalize path.join app.getPath('userData'), 'cookies.json'
-    chromecookie: path.normalize path.join app.getPath('userData'), 'Cookies'
-    configpath: path.normalize path.join app.getPath('userData'), 'config.json'
-
-client = new Client
-    rtokenpath:  paths.rtokenpath
+    rtokenpath: path.join(userData, 'refreshtoken.txt')
+    cookiespath: path.join(userData, 'cookies.json')
+    chromecookie: path.join(userData, 'Cookies')
+    configpath: path.join(userData, 'config.json')
+
+client = new Client(
+    rtokenpath: paths.rtokenpath
     cookiespath: paths.cookiespath
+)

 if fs.existsSync paths.chromecookie
     fs.unlinkSync paths.chromecookie


allow getentity to carry some custom data

--- src/main.coffee
+++ src/main.coffee
@@ -198,9 +198,9 @@
         client.renameconversation conv_id, newname # will trigger conversation_rename

     # no retries, just dedupe on the ids
-    ipc.on 'getentity', seqreq (ev, ids) ->
+    ipc.on 'getentity', seqreq (ev, ids, data) ->
         client.getentitybyid(ids).then (r) ->
-            ipcsend 'getentity:result', r
+            ipcsend 'getentity:result', r, data
     , false, (ev, ids) -> ids.sort().join(',')

     # no retry, just one single request


single line for logging error

single line for logging error

--- src/main.coffee
+++ src/main.coffee
@@ -366,8 +366,7 @@
     ipc.on 'quit', quit

     ipc.on 'errorInWindow', (ev, error) ->
-        console.log "Error on YakYak window:\n", error
-        console.log "--- End of error message in YakYak window."
+        console.log "Error on YakYak window:\n", error, "\n--- End of error message in YakYak window."

     # propagate these events to the renderer
     require('./ui/events').forEach (n) ->


Working, but not stable enough

Working, but not stable enough

--- src/molo.coffee
+++ src/molo.coffee
@@ -1,8 +1,9 @@
 'use strict'

 # ES 5 compability functions
-Object.keys or= (o) -> name for own name of o
-Array.isArray or= (a) -> a.push is Array.prototype.push and a.length?
+do ->
+  Object.keys ?= (o) -> name for own name of o
+  Array.isArray ?= (a) -> a.push is Array.prototype.push and a.length?

 # Check for potential server-side javascript
 hasModule = module?.exports?
@@ -13,8 +14,10 @@

   filename.lastIndexOf(extension) is filename.length - extension

+# Check if it's a JavaScript file
 isJavaScriptFile = (filename) -> hasExtension filename, '.js'

+# Path seperator
 pathSep = '/'

 # Wrapper function
@@ -70,6 +73,7 @@
   # Cache and queue definition
   cache = {}
   queue = {}
+  waitQueue = {}

   # Plugin object
   plugins = {}
@@ -110,7 +114,10 @@
       return

     queue[name] = {dependencies, factory}
-
+
+  # Compability flag
+  root.molo.define.amd = root.define.amd = true
+  #root.molo.define.umd = root.define.umd = false

   # Require function
   root.molo.require = root.require = (name, callback, context = root.molo.defaultContext) ->
@@ -120,6 +127,9 @@
     # Abort condition
     return unless Array.isArray name

+    # If no callback defined, single module and module in cache, return it directly
+    return cache[name] if name.length is 1 and not callback? and Object.hasOwnProperty.call(cache, name[0])
+
     reqArgs = []
     reqArgIndex = 0

@@ -129,64 +139,81 @@

     # Walk through all requires
     for i, num in name
-      do (i, num) ->
-        if cache[i]
-          reqArgs[num] = cache[i]
-          reqArgIndex++
-          executeCallback()
-          return
-
-        # Get the result of cached dependencies
-        cacheDeps = []
-
-        if queue[i]
-          depIndex = 0
-          depLength = queue[i].dependencies.length
-
-          depsLoaded = ->
-            if depIndex is depLength
-              cache[i] = queue[i].factory.apply context, cacheDeps
-              delete queue[i]
+      if cache[i]
+        reqArgs[num] = cache[i]
+        reqArgIndex++
+        executeCallback()
+        return
+
+      # Get the result of cached dependencies
+      cacheDeps = []

-          updateDeps = (item) ->
-            if item
-              cacheDeps.push item
-              depIndex++
-              depsLoaded()
-
-          for dep in queue[i].dependencies
-            # Need to check for hasOwnProperty
-            # Checking for cache[i] is not strict enough
-            # because a module could have undefined, null, 0 or an empty string
-            # as its export value
-            if Object.hasOwnProperty.call cache, dep
-              updateDeps cache[dep]
-            else
-              root.molo.require dep, updateDeps, context
-        else
-          loadScriptFile appendScriptPath(i), ->
-            root.molo.require i
-
-    # Resolve queue if possible
-    #console.log cache
-    #console.log queue
-
-    resolveDeps = []
-
-    if Object.keys(queue).length > 0
-      for key, value of queue
-        depLength = value.dependencies.length
-        for dep in value.dependencies
-          if cache[dep]
-            resolveDeps.push cache[dep]
-            if depLength is resolveDeps.length
-              cache[key] = value.factory.apply context, resolveDeps
-              delete queue[key]
-              root.molo.require key
-
-    #console.log cache
-    #console.log queue
-
+      if queue[i]
+        depIndex = 0
+        depLength = queue[i].dependencies.length
+
+        depsLoaded = ->
+          if depIndex is depLength
+            cache[i] = queue[i].factory.apply context, cacheDeps
+            delete queue[i]
+
+        updateDeps = (item) ->
+          if item
+            cacheDeps.push item
+            depIndex++
+            depsLoaded()
+
+        for dep in queue[i].dependencies
+          # Need to check for hasOwnProperty
+          # Checking for cache[i] is not strict enough
+          # because a module could have undefined, null, 0 or an empty string
+          # as its export value
+          if Object.hasOwnProperty.call cache, dep
+            updateDeps cache[dep]
+          else
+            root.molo.require dep, updateDeps, context
+      else
+        loadScriptFile appendScriptPath(i), -> root.molo.require i
+
+
+
+
+    walkThroughQueue = (queueObj) ->
+      # Resolve queue if possible
+      #resolveDeps = []
+
+      for key, value of queueObj
+        root.molo.require key
+
+      #if Object.keys(queueObj).length > 0
+      #  for key, value of queueObj
+      #    return unless key and value
+
+
+
+          #console.log 'walkThroughQueue'
+          #console.log key
+          #console.log value
+
+          #depLength = value.dependencies.length
+          #for dep in value.dependencies
+          #  if cache[dep]
+          #    resolveDeps.push cache[dep]
+          #    if depLength is resolveDeps.length
+          #      cache[key] = value.factory.apply context, resolveDeps
+          #      delete queueObj[key] if queueObj[key]
+          #      #delete waitQueue[key] if waitQueue[key]
+          #    #else
+          #    #  waitQueue[key] = queueObj[key]
+
+    walkThroughQueue queue
+    #walkThroughQueue waitQueue
+
+
+    console.log 'Queue & Cache & waitQueue'
+    console.log queue
+    console.log cache
+    console.log waitQueue

   # Additional export functions

@@ -227,4 +254,4 @@

     root.molo.define name, requires, defines

-  root.molo.define 'root', -> root
+  #root.molo.define 'root', -> root


Fix name.

Fix name.

--- test/jnoid_spec.coffee
+++ test/jnoid_spec.coffee
@@ -63,7 +63,7 @@
     it 'does not fmap', ->
       assert.deepEqual(new End().fmap((x) -> x + 5), new End)

-describe 'fromArray', ->
+describe 'fromList', ->
   it 'works', (done)->
     h.expectValues [1, 2, 3],
       Jnoid.fromList([1, 2, 3]),


fixing some broken tests

fixing some broken tests

--- test/middleware.coffee
+++ test/middleware.coffee
@@ -18,7 +18,7 @@
       middleware(options) req, res, () ->
         fs.readFile "#{__dirname}/../sample/public/test.js", 'utf8', (err, content) ->
           should.not.exist err
-          content.should.eql "(function() {\n\n  alert(\'welcome\');\n\n}).call(this);\n"
+          content.should.eql "(function() {\n  alert(\'welcome\');\n\n}).call(this);\n"
           next()

   it 'should compile with force option', (next) ->
@@ -41,7 +41,7 @@
                 mtime.should.be.below stat.mtime
                 fs.readFile "#{__dirname}/../sample/public/test.js", 'utf8', (err, content) ->
                   should.not.exist err
-                  content.should.eql "(function() {\n\n  alert(\'welcome\');\n\n}).call(this);\n"
+                  content.should.eql "(function() {\n  alert(\'welcome\');\n\n}).call(this);\n"
                   next()
           , 500

@@ -59,7 +59,7 @@
       middleware(options) req, res, () ->
         fs.readFile "#{__dirname}/../sample/public/test.js", 'utf8', (err, content) ->
           should.not.exist err
-          content.should.eql "\nalert(\'welcome\');\n"
+          content.should.eql "alert(\'welcome\');\n"
           next()

   it 'should strip path', (next) ->
@@ -76,7 +76,7 @@
       middleware(options) req, res, () ->
         fs.readFile "#{__dirname}/prefix/public/js/test.js", 'utf8', (err, content) ->
           should.not.exist err
-          content.should.eql "(function() {\n\n  alert(\'welcome\');\n\n}).call(this);\n"
+          content.should.eql "(function() {\n  alert(\'welcome\');\n\n}).call(this);\n"
           fs.unlink "#{__dirname}/prefix/public/js/test.js", next

   it 'should show filename on error', (next) ->


style test

style test

--- test/spec.style.coffee
+++ test/spec.style.coffee
@@ -11,6 +11,12 @@
         el.show()
         assert el.get(0).style.display is 'block', "Display set to block"

+    test 'show/hide preserve Rye', ->
+        el = $('#test')
+
+        assert el.hide() is el, "Display set to none"
+        assert el.show() is el, "Display set to block"
+
     test 'show/hide preserves display', ->
         el = $('.a')
         el.get(0).style.display = 'inline'


4241: Added loading indicator on event load

--- app/assets/javascripts/backbone/views/reservation_calendar_view.coffee
+++ app/assets/javascripts/backbone/views/reservation_calendar_view.coffee
@@ -11,22 +11,22 @@
     @ruleSet = options.ruleSet

     @calendar.fullCalendar
-      events: options.feedUrl,
+      events: options.feedUrl
       defaultView: @initialViewType(options.viewType)
-      height: 700,
-      allDaySlot: false,
-      eventOverlap: false,
-      selectable: @ruleSet.access_level != "read_only",
-      selectOverlap: false,
-      selectHelper: true,
-      header: {
-        left: 'title',
-        center: 'agendaDay,agendaWeek,month',
+      height: 700
+      allDaySlot: false
+      eventOverlap: false
+      selectable: @ruleSet.access_level != "read_only"
+      selectOverlap: false
+      selectHelper: true
+      header:
+        left: 'title'
+        center: 'agendaDay,agendaWeek,month'
         right: 'today prev,next'
-      },
-      select: @onSelect.bind(this),
-      windowResize: @onWindowResize.bind(this),
+      select: @onSelect.bind(this)
+      windowResize: @onWindowResize.bind(this)
       viewRender: @onViewRender.bind(this)
+      loading: @onLoading.bind(this)

     @calendar.fullCalendar('gotoDate', moment(options.focusDate)) if options.focusDate

@@ -77,6 +77,9 @@

   onViewRender: ->
     @updatePermalink()
+
+  onLoading: (isLoading) ->
+    Mess.loadingIndicator[if isLoading then 'show' else 'hide']()

   updatePermalink: ->
     @$('#permalink').attr('href', @permalink())


removed space removal, so powerset will work

--- src/trivia-game.coffee
+++ src/trivia-game.coffee
@@ -69,9 +69,9 @@
       checkGuess = guess.toLowerCase()
       # remove html entities (slack's adapter sends & as & now)
       checkGuess = checkGuess.replace /&.{0,}?;/, ""
-      # remove all punctuation and spaces, and see if the answer is in the guess.
-      checkGuess = checkGuess.replace /[\\'"\.,-\/#!$%\^&\*;:{}=\-_`~()\s]/g, ""
-      checkAnswer = @currentQ.validAnswer.toLowerCase().replace /[\\'"\.,-\/#!$%\^&\*;:{}=\-_`~()\s]/g, ""
+      # remove all punctuation and see if the answer is in the guess.
+      checkGuess = checkGuess.replace /[\\'"\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, ""
+      checkAnswer = @currentQ.validAnswer.toLowerCase().replace /[\\'"\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, ""
       checkAnswer = checkAnswer.replace /^(a(n?)|the)/g, ""
       if AnswerChecker(checkGuess, checkAnswer)
         resp.reply "YOU ARE CORRECT!!1!!!111!! The answer is #{@currentQ.answer}"


Reworked condition into a `toggleClass` call

--- app/assets/javascripts/backend/purchase_process/purchase_orders.coffee
+++ app/assets/javascripts/backend/purchase_process/purchase_orders.coffee
@@ -19,7 +19,9 @@
           $newParcelBtn.prop('href', "#{newParcelUrl}?mode=prefilled&purchase_order_ids=#{selectedOrdersIds}")
         else
           $newParcelBtn.prop('href', newParcelUrl)
-
-        if selectedOrders.length > 0 && reconciledOrders.length < 1 then $newParcelBtn.removeClass('disabled') else $newParcelBtn.addClass('disabled')
+
+        disabled = !selectedOrders.length || reconciledOrders.length
+        # !! so we're sure disabled is a Boolean and not just truthy/falsy — for jQuery
+        $newParcelBtn.toggleClass('disabled', !!disabled)

 ) ekylibre, jQuery


fix: getEventOffset returns {x,y}, not {ex,ey}

--- app/assets/javascripts/components/mark/tools/rectangle-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/rectangle-tool/index.cjsx
@@ -77,28 +77,30 @@

   handleMainDrag: (e) ->
     console.log "handleDrag (HD)"
-    return if @state.lockTool
-    { ex,ey } = @props.getEventOffset e
+    # return if @state.lockTool
+    console.log 'FOO'
+    { x,y } = @props.getEventOffset e
+    console.log 'BAR'
     mark = @state.mark
     markHeight = mark.yLower - mark.yUpper
     # why? in handleMarkClick clickOffset.x is mark.x - ex. Is not the same event?
     # does @props.clickOffset.x represent the mark station before the current drag event?
-    mark.x = ex + @props.clickOffset.x # add initial click offset
-    mark.y = ey + @props.clickOffset.y
+    mark.x = x + @props.clickOffset.x # add initial click offset
+    mark.y = y + @props.clickOffset.y

     # prevent dragging mark beyond image bounds
-    return if ( ey - markHeight/2 ) < 0
-    return if ( ey + markHeight/2 ) > @props.imageHeight
+    return if ( y - markHeight/2 ) < 0
+    return if ( y + markHeight/2 ) > @props.imageHeight

     @setState x: mark.x
     @setState y: mark.y
     @setState mark: mark
       # , => @forceUpdate()

-  handleMainDrag: (e, d) ->
-    @props.mark.x += d.x / @props.xScale
-    @props.mark.y += d.y / @props.yScale
-    @props.onChange e
+  # handleMainDrag: (e, d) ->
+  #   @props.mark.x += d.x / @props.xScale
+  #   @props.mark.y += d.y / @props.yScale
+  #   @props.onChange e

   handleX1Y1Drag: (e, d) ->
     @props.mark.x += d.x / @props.xScale
@@ -153,31 +155,28 @@

     console.log "render props.clickOffset", @props.clickOffset
     console.log "render props", @props
+
     <g
-      tool={this}
-      onMouseDown={@handleMouseDown}
+      className = {classString}
+      tool={this}
+      onMouseDown={@props.onSelect unless @props.disabled}
     >
-      <g
-        className = {classString}
-        onMouseDown={@props.onSelect unless @props.disabled}
-      >

-        <Draggable
-          onStart = {@props.handleMarkClick}
-          onDrag = {@handleMainDrag} >
-          <polyline points={points} strokeWidth="5" stroke="orange" fill="none"/>
-        </Draggable>
+      <Draggable
+        onStart = {@props.handleMarkClick}
+        onDrag = {@handleMainDrag} >
+        <polyline points={points} strokeWidth="5" stroke="orange" fill="none"/>
+      </Draggable>

-        { if @props.selected
-          <g>
-            <DeleteButton tool={this} x={@state.x + (width * DELETE_BUTTON_DISTANCE)} y={@state.y} />
-            <DragHandle x={@props.mark.x} y={@props.mark.y} onDrag={@handleX1Y1Drag} />
-            <DragHandle x={x2} y={y1} onDrag={@handleX2Y1Drag} />
-            <DragHandle x={x2} y={y2} onDrag={@handleX2Y2Drag} />
-            <DragHandle x={x1} y={y2} onDrag={@handleX1Y2Drag} />
-          </g>
-        }
-      </g>
+      { if @props.selected
+        <g>
+          <DeleteButton tool={this} x={@state.x + (width * DELETE_BUTTON_DISTANCE)} y={@state.y} />
+          <DragHandle x={@props.mark.x} y={@props.mark.y} onDrag={@handleX1Y1Drag} />
+          <DragHandle x={x2} y={y1} onDrag={@handleX2Y1Drag} />
+          <DragHandle x={x2} y={y2} onDrag={@handleX2Y2Drag} />
+          <DragHandle x={x1} y={y2} onDrag={@handleX1Y2Drag} />
+        </g>
+      }
     </g>


extracted to a new method so that others can override it

--- app/assets/javascripts/mixins/retencion_fuente_on_form_mixin.js.coffee
+++ app/assets/javascripts/mixins/retencion_fuente_on_form_mixin.js.coffee
@@ -3,9 +3,11 @@
     Bandango.conceptoRetencionFuenteHelper.comprobantes
   ).property()

+  newConceptoRetencion: (conceptoRetencionFuente) ->
+    @set "model.conceptoRetencionFuente", conceptoRetencionFuente
+
   conceptoSelectChanged: (e) ->
-    @get("controller.store").find("conceptoRetencionFuente", e.target.value).then (retencionFuente) =>
-      @set "model.conceptoRetencionFuente", retencionFuente
+    @get("controller.store").find("conceptoRetencionFuente", e.target.value).then $.proxy(@newConceptoRetencion, @)

   didInsertElement: ->
     @_super()


Add TODOs.

Add TODOs.

--- src/mock.coffee
+++ src/mock.coffee
@@ -1,3 +1,4 @@
+# TODO: add description
 class MethodSignature

   constructor: (method_name) ->
@@ -6,6 +7,7 @@
     @returns = undefined
     @called = false

+  # TODO: add description
   matches: (method_name, args...) ->
     (@method_name == method_name) and
       ( @args.length == args.length ) and
@@ -13,13 +15,16 @@

+# TODO: add description
 class Mock

+  # TODO: add description
   constructor: ->
-    @method_calls = []
-    @current_signature = undefined
+    @method_calls = []                # TODO: rename to @signatures
+    @current_signature = undefined    # TODO: not needed; use a method instead that returns: @method_calls[0] ? undefined
     @state = undefined

+  # TODO: add description
   expects: (method_name) ->
     @_check_expects_usage(method_name)
     @current_signature = new MethodSignature(method_name)
@@ -28,6 +33,7 @@
     @_set_state("expects")
     @

+  # TODO: add description
   args: (args...) ->
     @_check_args_usage(args...)
     @_check_for_duplicate_signature(@current_signature.method_name, args...)
@@ -35,18 +41,22 @@
     @_set_state("args")
     @

+  # TODO: add description
   returns: (value) ->
     @_check_returns_usage(value)
     @current_signature.returns = value
     @_set_state("returns")
     @

+  # TODO: add description
   check: ->
     @_check_for_uncalled_signatures()
     @_set_state("check")
     @

   # private
+
+  # TODO: re-order methods below

   _find_signature: (method_name, args...) ->
     for signature in @method_calls when signature.matches(method_name, args...)
@@ -72,6 +82,7 @@
     @_throw_returns_must_be_after_expects_or_args() unless @_state_in("expects", "args")
     @_throw_returns_usage() unless value?

+  # TODO: rename to _check_if_duplicate_signature
   _check_for_duplicate_signature: (method_name, args...) ->
     @_throw_duplicate_expectation("#{method_name}(#{args})") if @_find_signature(method_name, args...)

@@ -85,6 +96,7 @@
   _set_state: (state) ->
     @state = state

+  # TODO: rename to _is_state_in
   _state_in: (states...) ->
     @state in states

@@ -117,9 +129,11 @@

+# TODO: add description
 mock = (fn) ->
   mocks = ( new Mock() for i in [1..5] )
   fn.apply(undefined, mocks)
+  # TODO: wrap in try/catch: we want to capture all errors for all mocks.
   mock.check() for mock in mocks


Introduce is_ready command in run script

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

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -54,6 +54,10 @@
       echo "error: shell environment file does not exist"
       exit 1
     fi
+
+    function is_ready () {
+      exit 0
+    }

     mkdir $KONFIG_PROJECTROOT/.logs &>/dev/null

@@ -440,6 +444,9 @@
       checkrunfile
       run $1

+    elif [ "$1" == "is_ready" ]; then
+      is_ready
+
     elif [ "$1" == "docker-compose" ]; then
       shift
       docker_compose


Fixes #85. Add support for Windows for upwards recursive .jsbeautifyrc

See #37.

--- lib/beautify.coffee
+++ lib/beautify.coffee
@@ -249,7 +249,7 @@
     projectOptions = []
     p = path.dirname(editedFilePath)
     # Check if p is root (top directory)
-    while p isnt "/"
+    while p isnt path.resolve(p,"../")
       # Get config for p
       pf = path.join(p, "FAKEFILENAME")
       pc = getConfig(pf, false)


Change '= @' to '= this' for consistency.

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1114,7 +1114,7 @@
     @cached ||= findDeps([@context].concat(@args))

   apply: (obs) ->
-    obs.desc = @
+    obs.desc = this
     obs

   toString: ->


Remove space between a variable and a splat symbol for consistency.

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1210,7 +1210,7 @@
               functions = (sources[i.index].consume() for i in p.ixs)
               reply = sink trigger.e.apply ->
                 values = (fun() for fun in functions)
-                p.f(values ...)
+                p.f(values...)
               if triggers.length
                 triggers = _.filter nonFlattened, triggers
               if reply == Bacon.noMore


little mistake

little mistake

--- tests/interface.coffee
+++ tests/interface.coffee
@@ -9,7 +9,7 @@
   @timeout 15000 # browser launching
   @driver = new webdriver.Builder()
       # change 'phantomjs' to 'chrome' on your computer for visual tests
-      .forBrowser 'chrome'
+      .forBrowser 'phantomjs'
       .build()
   chai.use chaiWebdriver(@driver)
   @driver.get baseUrl


'Fixed' setAvatar stub warning

'Fixed' setAvatar stub warning

--- lib/atox-toxWorker.coffee
+++ lib/atox-toxWorker.coffee
@@ -68,7 +68,7 @@
     @sentRequests = []

     @setName   atom.config.get 'aTox.userName'
-    @setAvatar atom.config.get 'aTox.userAvatar'
+    #@setAvatar atom.config.get 'aTox.userAvatar'
     @setStatus "I am a Bot :)"

     for n in @nodes


Some parser improvements

Some parser improvements

--- src/parser.coffee
+++ src/parser.coffee
@@ -134,7 +134,7 @@
               # If the a block is embedded
               # directly in another block, we should
               # insert a socket around it automatically.
-              if stack?[stack.length - 1]?.type is 'block'
+              if stack[stack.length - 1]?.type is 'block'
                 # Push the socket to the autoinsertedStack,
                 # so that we know to close it later.
                 autoinsertStack.push mark.token.block
@@ -154,6 +154,25 @@
               head = head.append mark.token

             when 'socketStart'
+              # A socket is only allowed to be directly inside a block.
+              # Currently, our behaviour will be to auto-repair it in
+              # some cases, but this
+              # might need to be changed in the future.
+              #
+              # We will auto-repair if it is inside an indent,
+              # otherwise we will throw
+              unless stack[stack.length - 1]?.type is 'block'
+                if stack.length is 0
+                  throw new Error 'Improper parser: document root cannot be a socket.'
+                else if stack[stack.length - 1].type is 'indent'
+                  autoinsertStack.push mark.token.socket
+
+                  autoinsertedBlock = new model.Block()
+
+                  head = head.append autoinsertedBlock.start
+
+                  stack.push autoinsertedBlock
+
               # Update the stack and append the token.
               stack.push mark.token.socket

@@ -164,7 +183,7 @@
             # its corresponding Start token; if it is not,
             # we throw.
             when 'indentEnd'
-              unless mark.token.indent is stack?[stack.length - 1]
+              unless mark.token.indent is stack[stack.length - 1]
                 throw new Error 'Improper parser: indent ended too early.'

               # Update stack and indent depth
@@ -174,7 +193,7 @@
               head = head.append mark.token

             when 'blockEnd'
-              unless mark.token.block is stack?[stack.length - 1]
+              unless mark.token.block is stack[stack.length - 1]
                 throw new Error 'Improper parser: block ended too early.'

               # Update stack
@@ -184,18 +203,22 @@

               # If we need to close an autoinserted socket,
               # close it.
-              if mark.token.block is autoinsertStack?[autoinsertStack.length - 1]
+              if mark.token.block is autoinsertStack[autoinsertStack.length - 1]
                 autoinsertStack.pop()
                 head = head.append stack.pop().end

             when 'socketEnd'
               unless mark.token.socket is stack[stack.length - 1]
                 throw new Error 'Improper parser: socket ended too early.'
-
+
               # Update stack
               stack.pop()

               head = head.append mark.token
+
+              if mark.token.socket is autoinsertStack[autoinsertStack.length - 1]
+                autoinsertStack.pop()
+                head = head.append stack.pop().end

           lastIndex = mark.location.column


commands: Spawn process with shell:true

commands: Spawn process with shell:true

Signed-off-by: Marios Balamatsias <e5351d595bc582bda310ed831027b3dfc0f63346@gmail.com>

--- lib/commands.coffee
+++ lib/commands.coffee
@@ -121,6 +121,7 @@
 				# of using 'inherit' since the latter one is
 				# not supported in v0.10.
 				stdio: [ process.stdin, 'pipe', 'pipe' ]
+				shell: true

 	burn: (image, operation, options) ->
 		# Default image to the given path


Show developer tools on f12 press

Show developer tools on f12 press

--- src/player.coffee
+++ src/player.coffee
@@ -4,8 +4,15 @@
 window = global.window.nwDispatcher.requireNwGui().Window.get()
 Playlist = require './playlist'

+KEY_F12 = 123
+
 module.exports = (angular, document) ->
   global.document = document
+
+  $(document).on 'keyup', (event) ->
+    return unless event.keyCode is 123
+    event.stopPropagation()
+    window.showDevTools()

   player = angular.module 'player', []


remove some spew

remove some spew

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -145,7 +145,6 @@
                 super()

         exitAft: (fromBreak, label = null) ->
-                console.log "SwitchExitableScope.exitAft"
                 irbuilder.createBr @merge_bb

 class LoopExitableScope extends ExitableScope
@@ -157,7 +156,6 @@
                 irbuilder.createBr @fore_bb

         exitAft: (fromBreak, label = null) ->
-                console.log "LoopExitableScope.exitAft"
                 irbuilder.createBr @aft_bb

         @findLabeled: (l, stack = ExitableScope.scopeStack) ->
@@ -1179,7 +1177,6 @@
                 @createCall @ejs.throw, [arg], "", true

         visitTry: (n) ->
-                console.log "visitTry"
                 insertBlock = irbuilder.getInsertBlock()
                 insertFunc = insertBlock.parent

@@ -1222,9 +1219,7 @@
                                 catch_block = new llvm.BasicBlock "catch_bb", insertFunc

                                 irbuilder.setInsertPoint catch_block
-                                console.log 3
                                 @visit n.handlers[0]
-                                console.log 4
                                 irbuilder.createBr branch_target # XXX this should probably be to the cleanup block?

                         # if we have a handler then branch to it unconditionally, otherwise resumeunwind?
@@ -1273,7 +1268,6 @@
                                 switch_stmt.addCase (llvm.Constant.getIntegerValue int32Type, ExitableScope.REASON_RETURN), return_tramp

                 irbuilder.setInsertPoint merge_block
-                console.log "done with try"

 class AddFunctionsVisitor extends NodeVisitor
         constructor: (@module) ->


Add option to disable status bar icon

Add option to disable status bar icon

--- lib/git-plus.coffee
+++ lib/git-plus.coffee
@@ -86,6 +86,9 @@
       description: '(Experimental) Show diffs in commit pane?'
       type: 'boolean'
       default: false
+    enableStatusBarIcon:
+      type: 'boolean'
+      default: true

   subscriptions: null

@@ -149,7 +152,8 @@

   consumeStatusBar: (statusBar) ->
     @setupBranchesMenuToggle statusBar
-    @setupOutputViewToggle statusBar
+    if atom.config.get 'git-plus.enableStatusBarIcon'
+      @setupOutputViewToggle statusBar

   setupOutputViewToggle: (statusBar) ->
     div = document.createElement 'div'


Lua Run Selected Lines Fix

Lua Run Selected Lines Fix

Fixes Run Selected Line(s) for Lua by creating a temp file with the
selected lines and prepending blank lines to the temp file equal to the
number of lines above the selected source lines to match Lua error lines
numbers from the temp file with those of the source view.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -166,7 +166,10 @@
   Lua:
     "Selection Based":
       command: "lua"
-      args: (context)  -> ['-e', context.getCode()]
+      args: (context) ->
+        code = context.getCode(true)
+        tmpFile = GrammarUtils.createTempFileWithCode(code)
+        [tmpFile]
     "File Based":
       command: "lua"
       args: (context) -> [context.filepath]
@@ -199,11 +202,6 @@
         command: "bash"
         args: (context) -> ['-c', "xcrun clang++ -fcolor-diagnostics -Wc++11-extensions -Wall -include stdio.h -include iostream -framework Cocoa " + context.filepath + " -o /tmp/objc-cpp.out && /tmp/objc-cpp.out"]

-  ocaml:
-    "File Based":
-      command: "ocaml"
-      args: (context) -> [context.filepath]
-
   PHP:
     "Selection Based":
       command: "php"


Added a method to get the document changes since the start of the form session. Added a method to merge the properties of the latest document into the form, excluding the properties which were changed in the form session. Added a method to get all the input values in the form. setInputValue() returns whether it was successful.

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -104,6 +104,7 @@
       @settings = @data.settings ? {}
       @docs = Form.parseDocs()
       @data.doc = Form.getValues()
+      @origDoc = Setter.clone(@data.doc)
       @isSubmitting = false
       formArgs.onCreate?.apply(@, arguments)

@@ -216,10 +217,10 @@
         $input = Forms.getFieldElement(key, $form)
         Forms.setInputValue($input, value)

-    Form.getBulkValues = (template) ->
+    Form.getBulkValues = (template, docs) ->
       values = {}
       template = getTemplate(template)
-      docs = Form.getDocs(template)
+      docs ?= Form.getDocs(template)
       otherDocs = docs.slice(1)
       getValue = (doc, key) -> Objects.getModifierProperty(doc, key)
       # Populate all form fields with any common values across docs if possible.
@@ -327,12 +328,12 @@
         Collections.observe collection,
           changed: (doc) ->
             return unless docHasChanged(doc)
-            result = confirm('Warning: The ' + singularName + ' being edited by this form has been
+            result = confirm('The ' + singularName + ' being edited by this form has been
                 modified. Do you want to merge changes?')
-            console.log('result', result)
+            if result then Form.mergeLatestDoc(template)
           deleted: (doc) ->
             return unless docHasChanged(doc)
-            alert('Warning: The ' + singularName + ' being edited by this form has been removed.')
+            alert('The ' + singularName + ' being edited by this form has been removed.')
             # TODO(aramk) Change the form to insert.

     ################################################################################################
@@ -385,6 +386,57 @@
       else
         Form.getDocs(template)[0] ? null

+    # @returns {Object} The diff between the original document and the resulting document from
+    #     the current state of the form.
+    Form.getDocChanges = (template) ->
+      template = getTemplate(template)
+      formDoc = Form.getInputValues(template)
+      origDoc = template.origDoc ? {}
+      origDoc = Objects.flattenProperties(Setter.clone(origDoc))
+      delete origDoc._id
+      keys = _.union _.keys(formDoc), _.keys(origDoc)
+      changes = {}
+      _.each keys, (key) ->
+        formValue = formDoc[key]
+        if formValue != origDoc[key] then changes[key] = formValue
+      changes
+
+    # Merges the latest document into the form, giving precedence to the changed values in the form.
+    # @returns {Object} The flattened properties of the latest document which were merged into the
+    #     form.
+    Form.mergeLatestDoc = (template) ->
+      template = getTemplate(template)
+      return unless Form.hasDoc(template)
+      docs = Form.getDocs(template)
+      changedValues = Form.getDocChanges(template)
+      collection = Form.getCollection()
+      latestDocs = []
+      _.each docs, (doc) ->
+        doc = collection.findOne(doc._id)
+        if doc? then latestDocs.push(doc)
+      latestValues = if docs.length > 0 then Form.getBulkValues(template, latestDocs) else docs[0]
+      latestValues ?= {}
+      latestValues = Objects.flattenProperties(latestValues)
+      $form = Forms.getFormElement(template)
+      mergedValues = {}
+      _.each latestValues, (value, key) ->
+        unless changedValues[key]?
+          $input = Forms.getFieldElement(key, $form)
+          if Forms.setInputValue($input, value)
+            mergedValues[key] = value
+      mergedValues
+
+    Form.getInputValues = (template) ->
+      template = getTemplate(template)
+      $inputs = Forms.getFieldElements(Forms.getFormElement(template))
+      values = {}
+      $inputs.each ->
+        $input = $(@)
+        id = Forms.getInputId($input)
+        value = Forms.getInputValue($input)
+        values[id] = value
+      values
+
     Form.setSubmitButtonDisabled = (disabled, template) ->
       Form.getSubmitButton(template).toggleClass('disabled', !!disabled)

@@ -417,25 +469,40 @@
         $label = $parent.prev('label')
     $label

+  # @param {jQuery} $input
+  # @param {*} value
+  # @returns {Boolean} Whether the given value was successfully applied to the given input element.
+  #     This is false if the value is unchanged.
   setInputValue: ($input, value) ->
+    changed = @getInputValue($input)
+    return false if value == changed
     if @isSelectInput($input)
       @setSelectValue($input, value)
-    else if $input.is('[type="checkbox"]')
+    else if @isCheckbox($input)
       $input.prop('checked', value)
     else
       $input.val(value)
+    return true

   getInputValue: ($input) ->
     if @isSelectInput($input)
       @getSelectValue($input)
+    else if @isCheckbox($input)
+      $input.prop('checked')
     else
       $input.val()

-  getFieldElement: (name, formElement) -> $('[data-schema-key="' + name + '"]', formElement)
+  getInputId: ($input) -> $input.attr('data-schema-key')
+
+  getFieldElement: (name, formElement) -> $('[data-schema-key="' + name + '"]:first', formElement)
+
+  getFieldElements: (formElement) -> $('[data-schema-key]', formElement)

   getFormElement: (template) -> template.$('form:first')

   isSelectInput: ($input) -> $input.is('select') || @isDropdown($input)
+
+  isCheckbox: ($input) -> $input.is('[type="checkbox"]')

   getSelectOption: ($input, value) ->
     if @isDropdown($input)


Don't compile the bindingRegExp more times than needed

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -51,11 +51,12 @@
   # Builds the Rivets.Binding instances for the view.
   build: =>
     @bindings = []
+    bindingRegExp = @bindingRegExp()

     for node in @el.getElementsByTagName '*'
       for attribute in node.attributes
-        if @bindingRegExp().test attribute.name
-          type = attribute.name.replace @bindingRegExp(), ''
+        if bindingRegExp.test attribute.name
+          type = attribute.name.replace bindingRegExp, ''
           pipes = attribute.value.split('|').map (pipe) -> pipe.trim()
           path = pipes.shift().split '.'
           model = @models[path.shift()]


Removed a console.log statement that was committed by error

--- src/router.coffee
+++ src/router.coffee
@@ -163,7 +163,6 @@
 					ctx.response.header = routeRes.headers
 					routeRes.on "data", (chunk) ->
 						ctx.response.body = chunk
-						console.log chunk.toString()
 					routeRes.on "end", ->
 						next()
 		else


Generate negative numbers.

Generate negative numbers.

--- src/schema.coffee
+++ src/schema.coffee
@@ -4,6 +4,14 @@

 env = jsv.createEnvironment "json-schema-draft-03"
 metaSchema = env.getDefaultSchema()# "http://json-schema.org/draft-03/schema"
+
+MAX_NUMBER = 100
+MIN_NUMBER = -100
+MAX_ARRAY_LENGTH = 100
+
+# Generates a random number b/w max and min
+random = (max, min) ->
+  Math.random() * (max + min) + min

 # Generate a JSON object that matches the given schema filled with ipsum
 # text.
@@ -13,9 +21,9 @@
     when "boolean"
       ret Math.random() > 0.5
     when "number"
-      ret Math.random() * 100
+      ret random MAX_NUMBER, MIN_NUMBER
     when "integer"
-      ret Math.round(Math.random() * 100)
+      ret Math.round random MAX_NUMBER, MIN_NUMBER
     when "string"
       done null, "TODO"
     when "object"
@@ -23,7 +31,7 @@
         done err, _.object _.keys(schema.properties), ipsumVals
     when "array"
       async.map(
-        _.range(0, Math.random() * 100)
+        _.range(0, Math.random() * MAX_ARRAY_LENGTH)
         (i, done) -> toIpsum schema.items, done
         done)
     when "any"


use setting for RoutingSystem

use setting for RoutingSystem

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -230,8 +230,12 @@
   # @event application:urlchange - generate, when url change
   # @event application:404 - generate, if given url not matched with given routes
   # @event application:run - generate, after application running
-  create: (routes, fn = ->) ->
+  # setting : old, top, support
+  create: (routes, setting, fn = ->) ->
     current = prev = window.location.hash
+    hash_on_top        = setting["top"]
+    redirect_to_hash   = setting["old"]
+    push_state_support = setting["support"]

     # set routing by event
     for url, action of routes when @_event_route(url)
@@ -257,21 +261,23 @@
       [url, action]

     dispatcher = (e) ->
-      prev = current
-      result = false
+      prev        = current
       route_array = null
+      result      = false
+

       if event.type == "hashchange"
         # hashchange
         route_array = array_of_routes
         current = window.location.hash
-        tgt = window.location.origin
-        history.replaceState({href: current}, "#{current}", "#{tgt}/#{current}")
+        if hash_on_top and push_state_support
+          origin = window.location.origin
+          history.replaceState({href: current}, "#{current}", "#{origin}/#{current}")
       else
         # plain
         route_array = plain_routes
         href = e.target.href # TODO the same for hashchange
-        history.pushState({href: href}, "#{href}", href)
+        history.pushState({href: href}, "#{href}", href) if push_state_support
         pathname = window.location.pathname
         pathname = "/" if pathname == ""
         if (e.preventDefault)
@@ -308,7 +314,7 @@
       false

-    if plain_routes.length != 0 # TODO && use_modern_routing ???
+    if plain_routes.length != 0
       # bind all <a> element with dispatch function, but bind only when href not contain "#"
       selector  = "a:not([href^='#'])"

@@ -424,10 +430,13 @@
         route[url] = action
       @route = route

+    setting =
+      old: @use_hash_routing_for_old_browsers
+      top: @hash_always_on_top
+      support: push_state_support
+
     # start
-    #R.create(@route, () =>
-    #  @adapter.fire(document, "application:run", new Date());
-    #);
+    R.create @route, setting, () => @adapter.fire(document, "application:run", new Date())

     @logger()
     if @start


Reversed error semantics

Reversed error semantics

--- server/packages/nodemailer/index.coffee
+++ server/packages/nodemailer/index.coffee
@@ -142,7 +142,7 @@
 	new Promise (resolve, reject) ->

 		sandbox.on 'ready', (error) ->
-			return reject error? if error
+			return reject error if error?

 			# Convenience.
 			$ = sandbox._window.$


:art:

:art:

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -133,12 +133,11 @@
     descriptionMoreURL: if description then @getGlobalAttributeDocsURL(attribute) else null

   getAttributeValueCompletions: ({prefix, editor, bufferPosition}) ->
-    prefix = undefined if prefix.endsWith('"') or prefix.endsWith("'")
     completions = []
     tag = @getPreviousTag(editor, bufferPosition)
     attribute = @getPreviousAttribute(editor, bufferPosition)
     values = @getAttributeValues(tag, attribute)
-    for value in values when not prefix or firstCharsEqual(value, prefix)
+    for value in values when not prefix or firstCharsEqual(value, prefix) or prefix.endsWith('"') or prefix.endsWith("'")
       completions.push(@buildAttributeValueCompletion(tag, attribute, value))

     if completions.length is 0 and @completions.attributes[attribute].type is 'boolean'


:hack: Add space before replacement word.

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -46,7 +46,7 @@

         suggestions = for word in words
           {
-            word: @resultTemplate.replace('[key]', word.key)
+            word: ' ' + @resultTemplate.replace('[key]', word.key)
             prefix: prefix
             label: word.label
             renderLabelAsHtml: false


Extend API exception to include full response object

Change-type: patch
Signed-off-by: Miguel Casqueira <75004f149038473757da0be07ef76dd4a9bdbc8d@balena.io>

--- lib/register.coffee
+++ lib/register.coffee
@@ -16,6 +16,7 @@

 Promise = require('bluebird')
 randomstring = require('randomstring')
+{ TypedError } = require 'typed-error'

 ###*
 # @summary Creates a Balena Register Device instance
@@ -96,7 +97,11 @@
 				api_key: options.deviceApiKey
 		.tap (response) ->
 			if response.statusCode isnt 201
-				throw new Error(response.body)
+				throw new ApiError(response.body, response)
 		.get('body')
 		# Allow promise based and callback based styles
 		.asCallback(callback)
+
+class ApiError extends TypedError
+	constructor: (message = 'Error with API request', @response) ->
+		super(@message)


Add IPTABLES rule when "block" is true

Add IPTABLES rule when "block" is true

--- lib/zenproxy.coffee
+++ lib/zenproxy.coffee
@@ -1,9 +1,10 @@
 "use strict"

 # Libraries
-http    = require "http"
-colors  = require "colors"
-Table   = require "cli-table"
+http          = require "http"
+colors        = require "colors"
+Table         = require "cli-table"
+childProcess  = require "child_process"

 ZenProxy =

@@ -25,6 +26,8 @@

   run: ->
     @summary "Starting..."
+    do @blockPorts
+
     queries = {}
     http.createServer((request, response) ->
       url   = "#{request.headers.host}#{request.url}"
@@ -77,6 +80,11 @@

       request.pipe proxy, end: true

+  blockPorts: ->
+    for rule in global.config.rules
+      for host in rule.hosts when host.block is true
+        childProcess.exec "iptables -A INPUT -p tcp --dport #{host.port} -j DROP"
+
   summary: (message) ->
     table = new Table head: ["ZENproxy".green + " v0.08.27".grey + " - #{message}"], colWidths: [80]
     console.log table.toString()


Combine common block into beforeEach

Combine common block into beforeEach

--- spec/installed-package-view-spec.coffee
+++ spec/installed-package-view-spec.coffee
@@ -87,40 +87,34 @@
         expect(_.include(atom.config.get('core.packagesWithKeymapsDisabled') ? [], 'language-test')).toBe false

   describe "when a keybinding is copied", ->
+    [pack, card] = []
+
+    beforeEach ->
+      waitsForPromise ->
+        atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'language-test'))
+
+      runs ->
+        pack = atom.packages.getActivePackage('language-test')
+        card = new PackageKeymapView(pack)
+
     describe "when the keybinding file ends in .cson", ->
       it "writes a CSON snippet to the clipboard", ->
-
-        waitsForPromise ->
-          atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'language-test'))
-
-        runs ->
-          pack = atom.packages.getActivePackage('language-test')
-          card = new PackageKeymapView(pack)
-
-          spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.cson'
-          card.find('.copy-icon').click()
-          expect(atom.clipboard.read()).toBe """
-            'test':
-              'cmd-g': 'language-test:run'
-          """
+        spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.cson'
+        card.find('.copy-icon').click()
+        expect(atom.clipboard.read()).toBe """
+          'test':
+            'cmd-g': 'language-test:run'
+        """

     describe "when the keybinding file ends in .json", ->
       it "writes a JSON snippet to the clipboard", ->
-
-        waitsForPromise ->
-          atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'language-test'))
-
-        runs ->
-          pack = atom.packages.getActivePackage('language-test')
-          card = new PackageKeymapView(pack)
-
-          spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.json'
-          card.find('.copy-icon').click()
-          expect(atom.clipboard.read()).toBe """
-            "test": {
-              "cmd-g": "language-test:run"
-            }
-          """
+        spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.json'
+        card.find('.copy-icon').click()
+        expect(atom.clipboard.read()).toBe """
+          "test": {
+            "cmd-g": "language-test:run"
+          }
+        """

   describe "when the package is active", ->
     it "displays the correct enablement state", ->


They are multiplying!

They are multiplying!

--- spec/jasmine_examples/PlayerSpec.coffee
+++ spec/jasmine_examples/PlayerSpec.coffee
@@ -1,4 +1,4 @@
-describe "Player",  ->
+describe "Player", ->
   Player = require '../../lib/jasmine_examples/Player'
   Song = require '../../lib/jasmine_examples/Song'
   player = null
@@ -8,31 +8,31 @@
     player = new Player
     song = new Song

-  it "should be able to play a Song",  ->
+  it "should be able to play a Song", ->
     player.play song
     expect(player.currentlyPlayingSong).toEqual song

     # demonstrates use of custom matcher
     expect(player).toBePlaying song

-  describe "when song has been paused",  ->
+  describe "when song has been paused", ->
     beforeEach ->
       player.play song
       player.pause()

-    it "should indicate that the song is currently paused",  ->
+    it "should indicate that the song is currently paused", ->
       expect(player.isPlaying).toBeFalsy()

       # demonstrates use of 'not' with a custom matcher
       expect(player).not.toBePlaying song

-    it "should be possible to resume",  ->
+    it "should be possible to resume", ->
       player.resume()
       expect(player.isPlaying).toBeTruthy()
       expect(player.currentlyPlayingSong).toEqual song

   # demonstrates use of spies to intercept and test method calls
-  it "tells the current song if the user has made it a favorite",  ->
+  it "tells the current song if the user has made it a favorite", ->
     spyOn song, 'persistFavoriteStatus'

     player.play song
@@ -41,8 +41,8 @@
     expect(song.persistFavoriteStatus).toHaveBeenCalledWith true

   # demonstrates use of expected exceptions
-  describe "#resume",  ->
-    it "should throw an exception if song is already playing",  ->
+  describe "#resume", ->
+    it "should throw an exception if song is already playing", ->
       player.play song

       expect ->


Fix bug in clipboard.coffee

Fix bug in clipboard.coffee

--- catalog/src/js/clipboard.coffee
+++ catalog/src/js/clipboard.coffee
@@ -43,7 +43,7 @@

   __createZeroClipboardConnection: () ->
     @zeroClipboardClient = new ZeroClipboard.Client()
-    @zeroClipboardClient.glue(@buttonId, "#{@buttonId}_container")
+    @zeroClipboardClient.glue(@buttonId)
     @zeroClipboardClient.addEventListener('mouseDown', @__eventListenerHandler)

   __destroyZeroClipboardConnection: () ->


Update keymap configuration

Update keymap configuration

* Added: Permanent mapping for evaluating Emacs Lisp
* Removed: Unused debugging shortcut

--- user/keymap.cson
+++ user/keymap.cson
@@ -82,6 +82,7 @@
 	"cmd-alt-up":     "editor:duplicate-lines"
 	"ctrl-w":         "whitespace:convert-spaces-to-tabs"
 	"cmd-p":          "user:temp-1" # For temporary commands
+	"alt-f":          "user:temp-2"

 # Character transformation
@@ -104,5 +105,8 @@
 	"alt-o":          "file-icons:debug-outlines"
 	"alt-c":          "file-icons:recompile-config"
 	"ctrl-f":         "file-icons:open-settings"
-
-	"alt-f":          "user:toggle-faded-tokens"
+
+
+# Language-specific
+"atom-text-editor[data-grammar^='source emacs lisp']":
+	"cmd-p": "language-emacs-lisp:run-selection"


Handle UNKNOWN save errors

Handle UNKNOWN save errors

--- src/pane.coffee
+++ src/pane.coffee
@@ -679,9 +679,7 @@
       atom.notifications.addWarning("Unable to save file: #{error.message}")
     else if error.code is 'EACCES' and error.path?
       atom.notifications.addWarning("Unable to save file: Permission denied '#{error.path}'")
-    else if error.code is 'EPERM' and error.path?
-      atom.notifications.addWarning("Unable to save file '#{error.path}'", detail: error.message)
-    else if error.code is 'EBUSY' and error.path?
+    else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN'] and error.path?
       atom.notifications.addWarning("Unable to save file '#{error.path}'", detail: error.message)
     else if error.code is 'EROFS' and error.path?
       atom.notifications.addWarning("Unable to save file: Read-only file system '#{error.path}'")


ListView: add tabindex=“-1” to 3rd quadrant

to allow a parent scrollable element to get focus and thus be scrollable via pageUp and down

--- src/elements/ListView/ListView.coffee
+++ src/elements/ListView/ListView.coffee
@@ -225,7 +225,11 @@
 		html.push("")

 		add_quadrant = (qi) =>
-			html.push("
")
+			if qi is 3
+				# add tabindex="-1"
+				html.push("
")
+			else
+				html.push("
")

 			if qi in [2, 3]
 				html.push("

")


work with whitespace

work with whitespace

--- src/poll.coffee
+++ src/poll.coffee
@@ -53,7 +53,7 @@
             msg.send error
             @robot.logger.error error

-    robot.respond /vote\s+((\d+,?)*)/i, (msg) ->
+    robot.respond /vote\s+((\d+,?\s?)*)/i, (msg) ->
         try
             votes = msg.match[1]
             user = msg.envelope.user['name']
@@ -70,6 +70,7 @@
             @robot.logger.info "Finished removing user"
             votes = votes.split ","
             for vote in votes
+                vote = vote.replace /^\s+|\s+$/g, ""
                 @robot.logger.info "running for vote: #{vote}"
                 if vote >= Object.keys(options).length
                     @robot.logger.info "Invalid vote. #{vote} >= #{Object.keys(options).length}"


fixed some line endings

fixed some line endings

--- src/poll.coffee
+++ src/poll.coffee
@@ -12,79 +12,79 @@
 # Author:
 #   logikz
 class Option
-	constructor: () ->
+    constructor: () ->
 class Poll
-	constructor: () ->
+    constructor: () ->

 module.exports = (robot) ->
-	robot.respond /start poll (.*?)\s(option:\s?.*)+/i, (msg) ->
-		try
-			@robot.logger.info "Create poll called: #{msg}"
-			topic = msg.match[1]
-			#@robot.logger.info "Topic: #{topic}"
-			options = msg.match[2]
-			room = msg.message.room
-			#@robot.logger.info "options: #{options}"
-			@robot.logger.info "Creating poll with #{topic}"
-
-			pollOptions = (options.split "option: ")[1..]
-			@robot.brain.data.poll[room] = new Poll()
-			@robot.logger.info "Poll init complete"
-			@robot.brain.data.poll[room].topic = topic
-			@robot.logger.info "Poll added topic"
-			optionsString = ""
+    robot.respond /start poll (.*?)\s(option:\s?.*)+/i, (msg) ->
+        try
+            @robot.logger.info "Create poll called: #{msg}"
+            topic = msg.match[1]
+            #@robot.logger.info "Topic: #{topic}"
+            options = msg.match[2]
+            room = msg.message.room
+            #@robot.logger.info "options: #{options}"
+            @robot.logger.info "Creating poll with #{topic}"
+
+            pollOptions = (options.split "option: ")[1..]
+            @robot.brain.data.poll[room] = new Poll()
+            @robot.logger.info "Poll init complete"
+            @robot.brain.data.poll[room].topic = topic
+            @robot.logger.info "Poll added topic"
+            optionsString = ""

-			@robot.brain.data.poll[room].options = {}
-			for pollOption in pollOptions
-				index = pollOptions.indexOf(pollOption)
-				@robot.brain.data.poll[room].options[index] = new Option()
-				@robot.brain.data.poll[room].options[index].text = pollOption
-				optionsString += "\t#{index}: #{pollOption}\n"
-			@robot.brain.save()
-			@robot.logger.info "Brain saved"
+            @robot.brain.data.poll[room].options = {}
+            for pollOption in pollOptions
+                index = pollOptions.indexOf(pollOption)
+                @robot.brain.data.poll[room].options[index] = new Option()
+                @robot.brain.data.poll[room].options[index].text = pollOption
+                optionsString += "\t#{index}: #{pollOption}\n"
+            @robot.brain.save()
+            @robot.logger.info "Brain saved"

-			text =  """
-					Topic: #{topic}
-					Options:
-					#{optionsString}
-					"""
-			msg.send "#{text}"
-		catch error
-			msg.send error
-			@robot.logger.error error
+            text =  """
+                    Topic: #{topic}
+                    Options:
+                    #{optionsString}
+                    """
+            msg.send "#{text}"
+        catch error
+            msg.send error
+            @robot.logger.error error

-	robot.respond /vote\s+(\d+)/i, (msg) ->
-		try
-			vote = msg.match[1]
-			user = msg.envelope.user['name']
-			room = msg.message.room
-			options = @robot.brain.data.poll[room].options
-			@robot.logger.info "Adding new vote for #{@robot.brain.data.poll[room].topic} with #{Object.keys(options).length} options"
-			if vote >= Object.keys(options).length
-				@robot.logger.info "Invalid vote. #{vote} >= #{Object.keys(options).length}"
-				msg.reply "Please vote for a valid option"
-				return
-			#check if the user already voted
-			@robot.logger.info "Adding a vote for #{vote} for #{user}"
-			for index, option of options
-				@robot.logger.info "Checking #{index}: #{option.text}"
-				if option.users != undefined
-					@robot.logger.info "Some have voted for this one"
-					option.users = option.users.filter (currentUser) -> currentUser isnt user
-			@robot.logger.info "Finishes removing user"
-			selectedOption = @robot.brain.data.poll[room].options[vote]
-			if selectedOption.users != undefined
-				@robot.logger.info "add user to this option since it's defined"
-				selectedOption.users.push(user)
-			else
-				@robot.logger.info "First vote, create new array"
-				selectedOption.users = []
-				selectedOption.users.push(user)
-			@robot.brain.save()
-			@robot.logger.info "Brain saved"
-			msg.reply "Thanks for your vote"
-		catch error
-			@robot.logger.error error
+    robot.respond /vote\s+(\d+)/i, (msg) ->
+        try
+            vote = msg.match[1]
+            user = msg.envelope.user['name']
+            room = msg.message.room
+            options = @robot.brain.data.poll[room].options
+            @robot.logger.info "Adding new vote for #{@robot.brain.data.poll[room].topic} with #{Object.keys(options).length} options"
+            if vote >= Object.keys(options).length
+                @robot.logger.info "Invalid vote. #{vote} >= #{Object.keys(options).length}"
+                msg.reply "Please vote for a valid option"
+                return
+            #check if the user already voted
+            @robot.logger.info "Adding a vote for #{vote} for #{user}"
+            for index, option of options
+                @robot.logger.info "Checking #{index}: #{option.text}"
+                if option.users != undefined
+                    @robot.logger.info "Some have voted for this one"
+                    option.users = option.users.filter (currentUser) -> currentUser isnt user
+            @robot.logger.info "Finishes removing user"
+            selectedOption = @robot.brain.data.poll[room].options[vote]
+            if selectedOption.users != undefined
+                @robot.logger.info "add user to this option since it's defined"
+                selectedOption.users.push(user)
+            else
+                @robot.logger.info "First vote, create new array"
+                selectedOption.users = []
+                selectedOption.users.push(user)
+            @robot.brain.save()
+            @robot.logger.info "Brain saved"
+            msg.reply "Thanks for your vote"
+        catch error
+            @robot.logger.error error

     robot.respond /vote\s+(\d+,?)/i ->
         try
@@ -127,30 +127,30 @@
         catch error
             @robot.logger.error error

-	robot.respond /(view|show|poll) result(s)?/i, (msg) ->
-		try
-			@robot.logger.info "View results"
-			room = msg.message.room
-			topic = @robot.brain.data.poll[room].topic
-			options = @robot.brain.data.poll[room].options
-			optionString = ""
-			for index, option of options
-				text = option.text
-				userString = ""
-				numVotes = 0
-				if option.users != undefined
-					option.users.length
-					numVotes = option.users.length
-					users = option.users
-					userString = "Users:(#{users.join(", ")})"
-				@robot.logger.info "option: #{option} :: text: #{text} :: count: #{numVotes} :: users: userString"
-				optionString += "\t#{index}: #{text} Total: #{numVotes} #{userString}\n"
-			msg.send """
-			#{topic}
-			Current Poll Results:
-			#{optionString}
-			"""
-		catch error
-			@robot.logger.error error
+    robot.respond /(view|show|poll) result(s)?/i, (msg) ->
+        try
+            @robot.logger.info "View results"
+            room = msg.message.room
+            topic = @robot.brain.data.poll[room].topic
+            options = @robot.brain.data.poll[room].options
+            optionString = ""
+            for index, option of options
+                text = option.text
+                userString = ""
+                numVotes = 0
+                if option.users != undefined
+                    option.users.length
+                    numVotes = option.users.length
+                    users = option.users
+                    userString = "Users:(#{users.join(", ")})"
+                @robot.logger.info "option: #{option} :: text: #{text} :: count: #{numVotes} :: users: userString"
+                optionString += "\t#{index}: #{text} Total: #{numVotes} #{userString}\n"
+            msg.send """
+            #{topic}
+            Current Poll Results:
+            #{optionString}
+            """
+        catch error
+            @robot.logger.error error


FasterKoding: minor

FasterKoding: minor

--- client/Main/kodingrouter.coffee
+++ client/Main/kodingrouter.coffee
@@ -324,7 +324,7 @@
             @clear()

       # top level names
-      '/:name':do->
+      '/:name': do ->

         open = (routeInfo, model)->


account: wait for maincontroller to be ready to open modal

--- client/account/lib/index.coffee
+++ client/account/lib/index.coffee
@@ -20,7 +20,6 @@
 AccountNavigationItem = require './accountnavigationitem'
 require('./routehandler')()

-
 module.exports = class AccountAppController extends AppController

   @options =
@@ -109,7 +108,7 @@
       replaceState    : yes

-  loadView: (modal) ->
+  loadView: (modal) -> kd.singletons.mainController.ready =>

     @navController?.destroy()
     @navController = new KDListViewController


feat(logbook): scroll inputs into view

feat(logbook): scroll inputs into view

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

 $(document).on "focus", ".modal input, .modal textarea, .modal select", ->
-  $('.modal').animate
-    scrollTop: $(@).offset().top
-  , 1000
+  $(@)[0].scrollIntoView(true)

 document.addEventListener "turbolinks:load", ->
   $('.btn.btn-loading').click ->


Improvement: Support for new records in ModelFieldView#chooseByText

--- lib/ST/ModelFieldView.coffee
+++ lib/ST/ModelFieldView.coffee
@@ -223,6 +223,8 @@
       @trigger 'valueChosen', result[0]

   @method 'chooseByText', (text) ->
-    if @_results
+    if text == 'new'
+      @chooseResult 'new'
+    else if @_results
       for result in @_results
         return @chooseResult result if result[0].toFieldText().indexOf(text) >= 0


Collaboration: Put paneHash into file data to update editorpane hash.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1037,8 +1037,9 @@
         @createNewTerminal @mountedMachine, null, context.session, @collaborationHost or KD.nick()

       when 'editor'
-        {path}  = context.file
-        file    = FSHelper.createFileInstance path
+        {path}        = context.file
+        file          = FSHelper.createFileInstance path
+        file.paneHash = context.paneHash

         content = @rtm.getFromModel(path)?.getText() or ''


fix moment month names

fix moment month names

--- app/assets/javascripts/moment-init.coffee
+++ app/assets/javascripts/moment-init.coffee
@@ -1,7 +1,11 @@
 window.initMoment = ->
+  month_names = I18n.t('date.month_names')
+  abbr_month_names = I18n.t('date.abbr_month_names')
+  month_names.shift()
+  abbr_month_names.shift()
   moment.locale I18n.locale,
-    months: I18n.t('date.month_names')
-    monthsShort: I18n.t('date.abbr_month_names')
+    months: month_names
+    monthsShort: abbr_month_names
     weekdays: I18n.t('date.day_names')
     weekdaysShort: I18n.t('date.abbr_day_names')
     weekdaysMin: I18n.t('date.abbr_day_names')


Support auto paging.

Support auto paging.

--- app/assets/javascripts/undersky.js.coffee
+++ app/assets/javascripts/undersky.js.coffee
@@ -79,6 +79,8 @@
         column.addClass('actived')
         panels.filter('[data-id="' + id + '"]').show()
         self.resize()
+        if columns.size() - columns.index(column) < 10
+          $('.page-button.next-page a').click()

     @close: (e) ->
       e && e.preventDefault()


:bug: Fix Autocomplete prefix for return types Fixes steelbrain/AutoComplete-Hack#8

--- lib/autocomplete-hack.coffee
+++ lib/autocomplete-hack.coffee
@@ -12,11 +12,11 @@
       selector: '.source.cpp, .source.hack, .source.cpp'
       disableForSelector: '.comment'
       getPrefix:(editor, bufferPosition)->
-        regex = /::([\$\w0-9_-]+)$|(:[\$\w0-9_-]+)$|([\$\w0-9_-]+)$/
+        regex = /::([\$\w0-9_-]+)$|\)\s*:(\w+)$|(:[\$\w0-9_-]+)$|([\$\w0-9_-]+)$/
         line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition])
         match = line.match regex
         return '' unless match
-        return match[3] || match[2] || match[1] || match[0]
+        return match[4] || match[3] || match[2] || match[1] || match[0]
       getSuggestions: ({editor, bufferPosition, scopeDescriptor}) ->
         return [] unless Hack.config.status
         prefix = Provider.getPrefix editor, bufferPosition


Cancel after a word has been completed

Cancel after a word has been completed

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -119,11 +119,12 @@

   ###
    * Focuses the editor again
-   * @private
-  ###
-  cancel: =>
+   * @param {Boolean} focus
+   * @private
+  ###
+  cancel: (focus=true) =>
     super
-    @editorView.focus()
+    @editorView.focus() if focus

   ###
    * Finds suggestions for the current prefix, sets the list items,
@@ -190,6 +191,9 @@
   onChanged: (e) =>
     if e.newText.length is 1 and atom.config.get "autocomplete-plus.enableAutoActivation"
       @contentsModified()
+    else
+      # Don't refocus since we probably still have focus
+      @cancel focus = false

   ###
    * Repositions the list view. Checks for boundaries and moves the view


use jupyter kernelspec if available

use jupyter kernelspec if available

--- lib/kernel-manager.coffee
+++ lib/kernel-manager.coffee
@@ -7,7 +7,10 @@
     runningKernels: {}

     getAvailableKernels: _.memoize ->
-        out = child_process.spawnSync('ipython',['kernelspec','list', '--json']).stdout.toString()
+        try
+            out = child_process.spawnSync('jupyter',['kernelspec','list', '--json']).stdout.toString()
+        catch
+            out = child_process.spawnSync('ipython',['kernelspec','list', '--json']).stdout.toString()
         _.pluck(JSON.parse(out).kernelspecs, 'spec')

     getRunningKernels: ->


Move key handling to keydown and event.key.

- For PDF tabs, we see keydown events but we don't see the corresponding
  keypress events, so this allows us to navigate through PDF tabs with
  J/K.

- This relies on using `event.key` instead of `event.keyIdentifier`
  (which is no longer supported).

- Also move mapkey handling into the keyboard utils.

The effect of all of this is to simplify and unify much of our key
handling.

Fixes #1243 (for tab commands).

There are known places where we can prune a considerable amount of key
handling code.  However, that code is left in place for now in case this
change throws up some unexpected issues.

--- lib/keyboard_utils.coffee
+++ lib/keyboard_utils.coffee
@@ -91,8 +91,7 @@
     # TODO(smblott) Change this to use event.key.
     (event) ->
       event.keyCode == @keyCodes.ESC || do =>
-        keyChar = @getKeyCharString event, true
-        keyChar = mapKeyRegistry[keyChar] ? keyChar
+        keyChar = @getKeyCharString event
         # <c-[> is mapped to Escape in Vim by default.
         keyChar == "<c-[>"

@@ -109,7 +108,7 @@

   # Return the Vimium key representation for this keyboard event. Return a falsy value (the empty string or
   # undefined) when no Vimium representation is appropriate.
-  getKeyCharString: (event, allKeydownEvents = false) ->
+  getKeyCharString: (event) ->
     switch event.type
       when "keypress"
         # Ignore modifier keys by themselves.
@@ -117,19 +116,19 @@
           String.fromCharCode event.charCode

       when "keydown"
-        # Handle special keys and normal input keys with modifiers being pressed.
-        keyChar = @getKeyChar event
-        if 1 < keyChar.length or (keyChar.length == 1 and (event.metaKey or event.ctrlKey or event.altKey)) or allKeydownEvents
+        if keyChar = @getKeyChar event
           modifiers = []

-          keyChar = keyChar.toUpperCase() if event.shiftKey
+          keyChar = keyChar.toUpperCase() if event.shiftKey and keyChar.length == 1
           # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey).
           modifiers.push "a" if event.altKey
           modifiers.push "c" if event.ctrlKey
           modifiers.push "m" if event.metaKey

           keyChar = [modifiers..., keyChar].join "-"
-          if 1 < keyChar.length then "<#{keyChar}>" else keyChar
+          keyChar = "<#{keyChar}>" if 1 < keyChar.length
+          keyChar = mapKeyRegistry[keyChar] ? keyChar
+          keyChar

 KeyboardUtils.init()


simpler logic to detect backsapce

simpler logic to detect backsapce

--- lib/keyboard_utils.coffee
+++ lib/keyboard_utils.coffee
@@ -49,8 +49,8 @@
       event.key.toLowerCase() # F1 to F9.
     else if event.key.length == 3 and "F10" <= event.key <= "F12"
       event.key.toLowerCase() # F10 to F12.
-    else if event.key.length > 3 and event.key in ["Backspace"]
-      event.key.toLowerCase() # F10 to F12.
+    else if event.key == "Backspace"
+      event.key.toLowerCase() # Backspace.
     else
       ""


Realized I was comparing against a bad index.

--- tests/loud_test.coffee
+++ tests/loud_test.coffee
@@ -55,4 +55,4 @@
       room.user.say 'bravo',   '@hubot loud ban FISH'
       room.user.say 'charlie', 'CHICKEN'
       room.user.say 'delta',   'COW'
-      expect(room.messages[2]).to.eql ['hubot', 'CHICKEN']
+      expect(room.messages[4]).to.eql ['hubot', 'CHICKEN']


needs refactoring

needs refactoring

--- app/scripts/alchemy/core/drawnodes.coffee
+++ app/scripts/alchemy/core/drawnodes.coffee
@@ -58,8 +58,8 @@
         .attr('r', (d) -> alchemy.utils.nodeSize(d))
         .attr('shape-rendering', 'optimizeSpeed')
         .attr('target-id', (d) -> d.id)
-        .attr('style', (d) ->
-           "#{nodeColours(d)}; stroke-width: #{if d.node_type == 'root' then alchemy.conf.rootNodeRadius/3 else alchemy.conf.nodeRadius/3}")
+        # .attr('style', (d) ->
+        #    "#{nodeColours(d)}; stroke-width: #{if d.node_type == 'root' then alchemy.conf.rootNodeRadius/3 else alchemy.conf.nodeRadius/3}")

     #append caption to the node
     nodeEnter


Clear domain listeners

Clear domain listeners

--- lib/domain_repository.coffee
+++ lib/domain_repository.coffee
@@ -91,6 +91,9 @@
       aggregate.appliedEvents = []
     callback()

+  @clearEventHandlers: =>
+    @eventHandlers = {}
+
   @onEvent: (eventName, eventHandler) =>
     @eventHandlers[eventName] ?= []
     @eventHandlers[eventName].push eventHandler


Add add method to Size

Add add method to Size

--- src/size.coffee
+++ src/size.coffee
@@ -1,3 +1,4 @@
+Point = require './point'

 #
 module.exports =
@@ -15,6 +16,14 @@
   set: (@width, @height) ->
     [@width, @height] = @width if Array.isArray(@width)

+  add: (width, height) ->
+    if width instanceof Point or (width.x? and width.y?)
+      point = width
+      new Size(@width + point.x, @height + point.y)
+    else
+      other = Size.create(width, height)
+      new Size(@width + other.width, @height + other.height)
+
   toArray: ->
     [@width, @height]


Add price actions to blacklist.

Add price actions to blacklist.

--- lib/product-import.coffee
+++ lib/product-import.coffee
@@ -9,6 +9,7 @@

   constructor: (@logger, options = {}) ->
     @sync = new ProductSync
+    @sync.config [{type: 'prices', group: 'black'}].concat(['base', 'references', 'attributes', 'images', 'variants', 'metaAttributes'].map (type) -> {type, group: 'white'})
     @client = new SphereClient options
     @_resetCache()
     @_resetSummary()


Fix errors when reloading editor window

Fix errors when reloading editor window

--- lib/project-config.coffee
+++ lib/project-config.coffee
@@ -106,6 +106,8 @@
                 @globalConfig = JSON.parse(contents)
             catch e
                 atom.notifications.addInfo('Error parsing .existdb.json.', detail: e)
+        for name, connection of @globalConfig.servers
+            connection.name = name

     getProjectConfig: (context) ->
         context ?= @paths[0]


Use longopts for clarity

Use longopts for clarity

--- src/test.coffee
+++ src/test.coffee
@@ -29,7 +29,7 @@
     env = process.env

     atomCommand = args.argv.path ? 'atom'
-    @spawn atomCommand, ['-d', '-t', "--spec-directory=#{path.join(process.cwd(), 'spec')}"], {env, streaming: true}, (code) ->
+    @spawn atomCommand, ['--dev', '--test', "--spec-directory=#{path.join(process.cwd(), 'spec')}"], {env, streaming: true}, (code) ->
       if code is 0
         process.stdout.write 'Tests passed\n'.green
         callback()


add new require option, remove bootstrap hard coded dependency and prefix module dependency with require ! indicator

--- src/tree.coffee
+++ src/tree.coffee
@@ -60,8 +60,6 @@
   setImmediate =>
     modules = [modules] unless Array.isArray modules
     modules = flatten modules
-    # Declare bootstrap as a required dependecies
-    modules.unshift 'phyla/bootstrap'
     # Buil a full tree
     try tree = @load_tree modules
     catch err
@@ -72,12 +70,13 @@
     if options.modules.length
       modules = tree.map (leaf) -> leaf.module
       modules = options.modules.filter (module) -> modules.indexOf(module) isnt -1
-      modules.unshift 'phyla/bootstrap' if modules.length
       tree = @load_tree modules
       # Filter with the fast options
       tree = tree.filter( (leaf) =>
-        leaf.module.indexOf('phyla/bootstrap') is 0 or
-        options.modules.indexOf(leaf.module) isnt -1
+        return true if options.modules.indexOf(leaf.module) isnt -1
+        leaf.actions = leaf.actions.filter (action) =>
+          action.required
+        leaf.actions.length
       ) if options.fast
     # Emit event and return actions
     actions = []
@@ -123,30 +122,73 @@
   for module in modules then build_tree module
   tree

+# ###
+# Load a module and return its actions
+# A module may be prefixed with:
+# *   "?": Load this module only if it is defined by the user in the run list.
+# *   "!": Force this module to be loaded and executed, apply to "fast" mode.
+# ###
+# Tree::load_module = (module) ->
+#   @cache ?= {}
+#   return @cache[module] if @cache[module]
+#   @cache[module] = actions = []
+#   # Load the module
+#   # required = false
+#   # [_, meta, module] = /([\!\?]?)(.*)/.exec module
+#   # console.log meta, module
+#   # switch meta
+#   #   when '?' then # nothing yet
+#   #   when '!' then required = true
+#   actions = load module
+#   actions = [actions] unless Array.isArray actions
+#   for action, i in actions
+#     # Module dependencies
+#     if typeof action is 'string'
+#       actions.push action
+#       continue
+#     action = callback: action if typeof action is 'function'
+#     action.hidden ?= true unless action.name
+#     action.name ?= "#{module}/#{i}"
+#     action.module ?= module
+#     action.index ?= i
+#     # action.required ?= required
+#     action.skip = false
+#     action.required = true if module.indexOf('phyla/bootstrap') is 0
+#     actions.push action
+#   actions
+
 ###
-Load a module and return its actions
+Load a module and return its actions.
+
+Module actions when defining a string dependency may be prefixed with:
+
+*   "?": Load this module only if it is defined by the user in the run list.
+*   "!": Force this module to be loaded and executed, apply to "fast" mode.
+
 ###
 Tree::load_module = (module) ->
   @cache ?= {}
   return @cache[module] if @cache[module]
-  @cache[module] = actions = []
   # Load the module
-  callbacks = load module
-  callbacks = [callbacks] unless Array.isArray callbacks
-  for callback, i in callbacks
+  required = false
+  [_, meta, module] = /([\!\?]?)(.*)/.exec module
+  switch meta
+    when '?' then # nothing yet
+    when '!' then required = true
+  # Load the module
+  actions = load module
+  actions = [actions] unless Array.isArray actions
+  for callback, i in actions
     # Module dependencies
-    if typeof callback is 'string'
-      actions.push callback
-      continue
-    callback = callback: callback if typeof callback is 'function'
+    continue if typeof callback is 'string'
+    callback = actions[i] = callback: callback if typeof callback is 'function'
     callback.hidden ?= true unless callback.name
     callback.name ?= "#{module}/#{i}"
     callback.module ?= module
     callback.index ?= i
-    callback.skip = false
-    callback.required = true if module.indexOf('phyla/bootstrap') is 0
-    actions.push callback
-  actions
+    callback.skip ?= false
+    callback.required = true if required
+  @cache[module] = actions

 module.exports = (modules, options, callback)->
   (new Tree).actions modules, options, callback


Removed useless className

Removed useless className

--- core/app/backbone/factlink/text_area_view.coffee
+++ core/app/backbone/factlink/text_area_view.coffee
@@ -16,8 +16,6 @@

   ui:
     inputField: 'textarea'
-
-  className: 'TextAreaView'

   templateHelpers: =>
     placeholder: @options.placeholder


Only insert in the benchmark. Keeps it pure (but the line gets long)

--- benchmark/benchmark-suite.coffee
+++ benchmark/benchmark-suite.coffee
@@ -95,9 +95,8 @@
           editor.moveCursorToBeginningOfWord()
           editor.setCursorScreenPosition(endPosition)

-        benchmark "insert-delete", 10, ->
+        benchmark "insert", ->
           editor.insertText('x')
-          editor.backspace()

 describe "TokenizedBuffer.", ->
   describe "coffee-script-grammar.", ->


changed likes display

changed likes display

--- index.coffee
+++ index.coffee
@@ -37,7 +37,7 @@
         title: o.title
         description: "
           <img align='left' hspace='5' src='#{o.artwork_url}'/>
-          |▶#{o.playback_count}|⬇#{o.download_count}|💬#{o.comment_count}|❤#{o.likes_count}|🔁#{o.reposts_count}|\n
+          [▶#{o.playback_count} ⬇#{o.download_count} 💬#{o.comment_count} ❤#{o.likes_count} 🔁#{o.reposts_count}]\n
           #{o.description}
         "
         guid: o.stream_url


fix finding

fix finding

--- index.coffee
+++ index.coffee
@@ -187,7 +187,7 @@
     words = query.split(" ")
     words.shift()
     query = words.join(" ")
-  looker = foundLooker || lookers[0]
+  context.looker = foundLooker || lookers[0]

   runner = new LookFinder(context, type, query)
   runner.start()


put cursor at the bottom after every update

--- index.coffee
+++ index.coffee
@@ -17,6 +17,7 @@
   socket.on 'add', (s) ->
     l = cm.lineCount() - 1
     cm.replaceRange s, {line: l, ch: 0}, {line: l, ch: cm.getLine(l).length}, 'Dyalog'
+    cm.setCursor cm.lineCount() - 1, 0

   socket.on 'prompt', ->
     l = cm.lineCount() - 1


Default to being a login shell. Fixes env issues for zsh users.

--- index.coffee
+++ index.coffee
@@ -108,7 +108,7 @@
     default: do ({SHELL, HOME}=process.env) ->
       switch path.basename SHELL.toLowerCase()
         when 'bash' then "--init-file #{path.join HOME, '.bash_profile'}"
-        when 'zsh'  then ""
+        when 'zsh'  then "-l"
         else ''
   openPanesInSameSplit:
     type: 'boolean'


fix(decode): return the code itself if it's unknown

--- index.coffee
+++ index.coffee
@@ -1,7 +1,7 @@
 module.exports =

   decode = ( str ) ->
-    str.replace /%[\d\w]{2}/gi, ( code ) -> charsByCode[ code ]
+    str.replace /%[\d\w]{2}/gi, ( code ) -> charsByCode[ code ] ? code

 # https://encoding.spec.whatwg.org/index-windows-1251.txt
 charsByCode =


updating optimist

updating optimist

--- index.coffee
+++ index.coffee
@@ -19,7 +19,7 @@
   .demand(['url', 'key'])
   .default('url', 'redis://127.0.0.1:6379')
   .alias('url', 'u')
-  .describe('url', 'A formatted redis url pointing to your redis instance')
+  .describe('url', 'A formatted redis url')
   .describe('key', 'Your Reddish connection key')
   .alias('key', 'k')
   .argv


disable touch scroll

disable touch scroll

--- index.coffee
+++ index.coffee
@@ -211,3 +211,7 @@
   if not draghint.classList.contains 'hidden'
     draghint.classList.add 'hover'
     chooser.click()
+
+window.addEventListener 'touchstart', (e) ->
+  e.preventDefault()
+