Extend SubIndicatorMap from original MapView to remove repeated code

--- client/src/views/sub_indicator_map_view.coffee
+++ client/src/views/sub_indicator_map_view.coffee
@@ -20,60 +20,7 @@
     popupSize:   [0, 0]
     shadowSize:   [0, 0]

-class Backbone.Views.SubIndicatorMapView extends Backbone.View
-  className: 'section-visualisation map-visualisation'
-
-  initialize: (options) ->
-    @visualisation = options.visualisation
-    @listenTo(@visualisation, 'change:data', @render)
-    @render()
-
-  render: =>
-    if @visualisation.get('data')?
-      setTimeout(=>
-        @renderMap()
-      , 500)
-    else
-      @visualisation.getIndicatorData()
-
-    return @
-
-  renderMap: =>
-    @map = L.map(
-      @el,
-      scrollWheelZoom: false
-      attributionControl: false
-    )
-
-    L.tileLayer(
-      'http://{s}.tiles.mapbox.com/v3/onlyjsmith.map-9zy5lnfp/{z}/{x}/{y}.png'
-    ).addTo(@map)
-
-    @fitToBounds()
-    @renderDataToMap()
-
-  saveMapBounds: =>
-    bounds = @map.getBounds()
-    minll = bounds.getSouthWest()
-    maxll = bounds.getNorthEast()
-    bbox = [
-      [minll.lat,minll.lng],
-      [maxll.lat, maxll.lng]
-    ]
-
-    @visualisation.set('map_bounds', bbox)
-
-  fitToBounds: =>
-    if @visualisation.get('map_bounds') and @visualisation.get('map_bounds').length > 1 ?
-      bounds = @visualisation.get('map_bounds')
-    else
-      bounds = [
-        [26.204734267107604, 57.44750976562499],
-        [22.19757745335104, 50.877685546875]
-      ]
-
-    @map.fitBounds(bounds)
-
+class Backbone.Views.SubIndicatorMapView extends Backbone.Views.MapView
   renderDataToMap: ->
     mostRecentData = @visualisation.getHighestXRow()
     subIndicatorData = mostRecentData[@visualisation.getSubIndicatorField()]
@@ -95,7 +42,3 @@
       )
       marker.bindPopup(Libs.LeafletHelpers.generatePopupText(subIndicator, indicatorDefinition))
     )
-
-  onClose: ->
-    @map.off('moveend') if @map?
-    @stopListening()


Build abbrev tree

Build abbrev tree

--- src/cmd/abbrev.coffee
+++ src/cmd/abbrev.coffee
@@ -2,4 +2,12 @@

 exports.alias = 'abbreviations'

-exports.cmd = (args)->
+exports.cmd = ->
+  tree = {}
+  for x in cmd.all
+    for name in [x.name].concat x.alias
+      z = tree
+      for c in name.split ''
+        z = z[c] ||= {}
+      z['.'] = name
+  echo yaml.dump tree


Remove c&p usage. Add description for all cmd options.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -5,10 +5,13 @@

 argv = require('optimist')
   .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret --logDir dir --logLevel level --timeout timeout')
-  .describe('projectKey', 'Sphere.io project key (required if you use sphere-specific value transformers).')
-  .describe('clientId', 'Sphere.io HTTP API client id (required if you use sphere-specific value transformers).')
-  .describe('clientSecret', 'Sphere.io HTTP API client secret (required if you use sphere-specific value transformers).')
-  .describe('sphereHost', 'Sphere.io host.')
+  .describe('projectKey', 'your SPHERE.IO project-key')
+  .describe('clientId', 'your SPHERE.IO OAuth client id')
+  .describe('clientSecret', 'your SPHERE.IO OAuth client secret')
+  .describe('timeout', 'timeout for requests')
+  .describe('sphereHost', 'SPHERE.IO API host to connecto to')
+  .describe('logLevel', 'log level for file logging')
+  .describe('logDir', 'directory to store logs')
   .default('logLevel', 'info')
   .default('logDir', '.')
   .default('timeout', 60000)


close db connections with monmon closeAll

--- filepaths_migration.coffee
+++ filepaths_migration.coffee
@@ -1,10 +1,10 @@
 require.paths.unshift './node_modules'
 require.paths.push 'server'

-monmon = require('monmon').monmon
+monmon = require('monmon')
 _      = require('underscore')
-
-db  = monmon.use('crash-reports')
+async  = require('async')
+db     = monmon.monmon.use('crash-reports')
 crashreports_db = db.collection('crashreports')

 read_crashreports = (cb) ->
@@ -14,30 +14,31 @@
         cb null, arr

 write_crashreports = (arr, cb) ->
-    console.log "#{arr.length} crashreports to write"
-    #crashreports_db.find({'id':crashreport.id}).upsert().update(crashreport).run
-        # if err?
-        #     cb err
-        # else
-        #     cb null
-    cb null, null
+    async.forEachSeries arr,
+        (crashreport, cb_next) ->
+            console.log "store crashreport id:#{crashreport.id} to db" #debug
+            crashreports_db.find({'id':crashreport.id}).upsert().update(crashreport).run (err) ->
+                cb_next err if err?
+                cb_next null
+        (err) ->
+            cb err if err?
+            cb null

 modify_crashreports = (arr, cb) ->
     arr_modified = _(arr).map (crashreport) ->
         _(crashreport.files).each (properties,fileid) ->
-            console.log "before:#{properties.path}"
+            #console.log "before:#{properties.path}"
             properties.path = properties.path.replace /^.*\/public\//,''
             crashreport.files["fileid"] = properties
-            console.log "after:#{properties.path}"
+            #console.log "after:#{properties.path}"
         crashreport
     cb null, arr_modified

 read_crashreports (err,arr) ->
     throw err if err?
-#    console.log arr[0] #debug
     modify_crashreports arr, (err,arr) ->
         throw err if err?
-        console.log arr[3] #debug
-        console.log "done!"
-
-
+        write_crashreports arr, (err) ->
+            throw err if err?
+            monmon.closeAll()
+            console.log "done!"


Do not attempt to connect after instance lookup in final state.

Fixes #185

--- src/connection.coffee
+++ src/connection.coffee
@@ -447,6 +447,8 @@
         @config.server
         @config.options.instanceName
         (message, port) =>
+          if @state == @STATE.FINAL
+            return
           if message
             @emit('connect', ConnectionError(message, 'EINSTLOOKUP'))
           else


Fix cmd+x having `pressedVKey = true`

Fix cmd+x having `pressedVKey = true`

Cut and paste could get the editor into a bad state because `pressedVKey` wasn't always being cleared. Pressing `cmd+v` then `cmd+x` would hit the paste code path and throw.

--- src/controller.coffee
+++ src/controller.coffee
@@ -4325,16 +4325,11 @@
   pressedXKey = false

   @copyPasteInput.addEventListener 'keydown', (event) ->
+    pressedVKey = pressedXKey = false
     if event.keyCode is 86
       pressedVKey = true
     else if event.keyCode is 88
       pressedXKey = true
-
-  @copyPasteInput.addEventListener 'keyup', (event) ->
-    if event.keyCode is 86
-      pressedVKey = false
-    else if event.keyCode is 88
-      pressedXKey = false

   @copyPasteInput.addEventListener 'input', =>
     if @readOnly


Add event handlers for file drop support on button area.

--- lib/client/autoform-file.coffee
+++ lib/client/autoform-file.coffee
@@ -62,6 +62,27 @@
       if err then return console.log err
       t.value.set fileObj._id

+  "dragover .js-select-file": (e) ->
+    e.stopPropagation()
+    e.preventDefault()
+
+  "dragenter .js-select-file": (e) ->
+    e.stopPropagation()
+    e.preventDefault()
+
+  "drop .js-select-file": (e, t) ->
+    e.stopPropagation();
+    e.preventDefault();
+    collection = getCollection t.data
+
+    file = new FS.File e.originalEvent.dataTransfer.files[0]
+    if Meteor.userId
+      file.owner = Meteor.userId()
+
+    collection.insert file, (err, fileObj) ->
+      if err then return console.log err
+      t.value.set fileObj._id
+
   'click .js-remove': (e, t) ->
     e.preventDefault()
     t.value.set null


Remove unused (at this time) step parameter in handleSubmitted method

--- client/app/lib/onboarding.coffee
+++ client/app/lib/onboarding.coffee
@@ -48,8 +48,8 @@
         @user = user
         @steps = steps.map (step) =>
             stepModel = new Step step
-            stepModel.onValidated (step) =>
-                @handleStepSubmitted step
+            stepModel.onValidated () =>
+                @handleStepSubmitted()
             return stepModel

@@ -66,8 +66,8 @@
     # when it has been successfully submitted
     # Maybe validation should be called here
     # Maybe we will return a Promise or call some callbacks in the future.
-    handleStepSubmitted: (step) ->
-        @goNext()
+    handleStepSubmitted: () ->
+        @goToNext()

     # Go to the next step in the list.


Home: About footer link added

Home: About footer link added

--- client/landing/Intro/home.coffee
+++ client/landing/Intro/home.coffee
@@ -200,7 +200,7 @@
           </address>

-          About
+          About
           Contact
           University
           Jobs


Add reset method for clearing clauses

Add reset method for clearing clauses

--- client/lanes/models/Query.coffee
+++ client/lanes/models/Query.coffee
@@ -263,6 +263,11 @@
         this.addNewClause()
         this

+    reset: ->
+        @clauses.reset([
+            {query: this, available_fields: @fields, field: @initialField}
+        ])
+
     setSortField: (field, silent = false) ->
         @set({
             sortAscending: (if @sortField is field then !@sortAscending else true)


add client object encapsulation base.

add client object encapsulation base.

--- client/scripts/coffee/app.coffee
+++ client/scripts/coffee/app.coffee
@@ -5,9 +5,9 @@

 class Player
     constructor: (@game, @id, @score) ->
-        ground = platforms.create (@game.phaser.world.width / @game.players.length)*id, @game.phaser.world.height - 64, 'ground'
-        ground.scale.setTo 0.3, 2
-        ground.body.immovable = true
+        @bucket = @game.plateforms.create 0, 0, 'ground'
+        @bucket.scale.setTo 0.3, 2
+        @bucket.body.immovable = true

 class Game
     GameStatus =
@@ -17,42 +17,58 @@

     constructor: (@status, @players) ->
         # basic config
-        @window_x = 1200
-        @window_y = 680
-        @stars = null
+        @balls = null
+        @plateforms = null
         @scoreText = null
         @score = 0
         @players = []
         # Generate the word
-        @phaser = new Phaser.Game @window_x, @window_y, Phaser.AUTO, '', preload: @preload, create: @create, update: @update
-        @generate_fake_player()
-        @platforms = @phaser.add.group()
+        self = this
+        @phaser = new Phaser.Game(
+            1200,
+            680,
+            Phaser.AUTO,
+            '',
+            preload:( -> self.preload()),
+            create: ( -> self.create()),
+            update: (-> self.update())
+            )

     generate_fake_player: () ->
         x = y = 0
         for i in [0..3]
-            @players.push new Player @game, i, 0
+            @players.push(new Player(this, i, 0))
+
+    set_players_position: () ->
+        for player  in @players
+            player.bucket.x =  250 * player.id
+            player.bucket.y =  @phaser.world.height - 64
+

     preload: () ->
       console.log ':preload'
-      console.log this
-      this.load.image 'sky', '/assets/images/sky.png'
-      this.load.image 'ground', '/assets/images/platform.png'
-      this.load.image 'star', '/assets/images/star.png'
-      #game.load.spritesheet 'dude', '/assets/images/dude.png', 32, 48
-
+      @phaser.load.image 'sky', '/assets/images/sky.png'
+      @phaser.load.image 'ground', '/assets/images/platform.png'
+      @phaser.load.image 'star', '/assets/images/star.png'

     create: () ->
       console.log ':create'
       @phaser.add.sprite 0, 0, 'sky'
       @phaser.add.sprite 0, 0, 'star'

+      @plateforms = @phaser.add.group()
+
+      @generate_fake_player()
+
+      @set_players_position()
+      console.log @plateforms
+
       #cursors = @phaser.input.keyboard.createCursorKeys()
-      stars = @phaser.add.group()
+      @balls = @phaser.add.group()

       for i in [0..12]
-        star = stars.create i * 70, 0, 'star'
+        star = @balls.create i * 70, 0, 'star'
         star.body.gravity.y = 6
         star.body.bounce.y = 0.7 + Math.random() * 0.2

@@ -65,7 +81,7 @@
       scoreText.content = "Score: #{score}"

     update: () ->
-      @phaser.physics.collide stars, platforms
-      @phaser.physics.overlap stars, collectStar, null, this
+      @phaser.physics.collide @balls, @platforms
+      #@phaser.physics.overlap @balls, @collectStar, null, this

 game = new Game()


Guess the first vm name correctly

Guess the first vm name correctly

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -64,8 +64,8 @@
   getDefaultVmName:->
     {entryPoint} = KD.config
     currentGroup = if entryPoint?.type is 'group' then entryPoint.slug
-    if not currentGroup or currentGroup is 'koding' then "koding~#{KD.nick()}"
-    else currentGroup
+    if not currentGroup or currentGroup is 'koding' then "koding~#{KD.nick()}~0"
+    else "#{currentGroup}~0"

   createGroupVM:(type='user', planCode, callback)->
     defaultVMOptions = {planCode}
@@ -101,6 +101,7 @@

   createNewVM:->
     return  if @dialogIsOpen
+
     vmController        = @getSingleton('vmController')
     paymentController   = @getSingleton('paymentController')
     canCreateSharedVM   = "owner" in KD.config.roles or "admin" in KD.config.roles


Fix type error upon completed commit

Fix type error upon completed commit

--- lib/models/diffs/diff.coffee
+++ lib/models/diffs/diff.coffee
@@ -25,7 +25,7 @@

   constructor: (diff) ->
     @giftDiff = diff
-    @raw = diff.diff
+    @raw = diff?.diff
     diff = @removeHeader @raw
     chunks = @splitChunks diff
     chunks = _.map chunks, (chunk) => chunk: chunk, header: @header


rtp: Modify error message

rtp: Modify error message

--- rtp.coffee
+++ rtp.coffee
@@ -435,7 +435,7 @@
         when api.H264_NAL_UNIT_TYPE_FU_A  # FU-A (28)
           info.fu_a = api.readH264FragmentationUnitA bits
         else
-          throw new Error "Not implemented: nal_unit_type=#{info.nal_unit_type}"
+          throw new Error "Not implemented: nal_unit_type=#{info.nal_unit_type} (please report this bug)"
     else
       throw new Error "Invalid nal_unit_type=#{info.nal_unit_type}"
     return info


APNs push message调整

APNs push message调整

--- lib/pushservices/apns.coffee
+++ lib/pushservices/apns.coffee
@@ -66,11 +66,9 @@
                     if alertNew.replace(/[^\x00-\xff]/gi, "--").length + alert.charAt(i).replace(/[^\x00-\xff]/gi, "--").length <= alert_bytesize
                         alertNew = alertNew + alert.charAt(i)
                     else
-                        alertNew = alertNew + '...'
-
-                    i++
+                        note.alert = alertNew + '...'
+                        i = alert.length + 1

-                note.alert = alertNew

             logger.verbose "APNS push msg: " + JSON.stringify(note)
             @driver.pushNotification note, device


Add special case for element consisting of only whitespace

Otherwise this whitespace would be taken as both leading and trailing whitespace.

--- lib/text-manipulation.coffee
+++ lib/text-manipulation.coffee
@@ -14,7 +14,10 @@
     [@start, @end] = range
     element = bufferText.slice @start, @end
     @leadingWhitespace  = TextManipulation.getLeadingWhitespace(element)
-    @trailingWhitespace = TextManipulation.getTrailingWhitespace(element)
+    @trailingWhitespace = if @leadingWhitespace.length is element.length
+                            '' # element is all whitespace, which we take to be the leading whitespace
+                          else
+                            TextManipulation.getTrailingWhitespace(element)
     @eltStart = @start + @leadingWhitespace.length
     @eltEnd = @end - @trailingWhitespace.length
     @strippedElement = bufferText.slice @eltStart, @eltEnd


cleaning code for coderwall

cleaning code for coderwall

--- src/githubRepo.coffee
+++ src/githubRepo.coffee
@@ -23,60 +23,67 @@
     @callSettingFunction = ( name, args = [] ) ->
       @settings[name].apply( this, args )

+    # Format date
+    @formatDate = ( isoDate ) ->
+      date = new Date(isoDate)
+      return ("0"+date.getDate()).slice(-2) +
+        '-' + ("0"+(date.getMonth()+1)).slice(-2) +
+        '-' + date.getFullYear()
+
+    # Coderwall badges
     @coderwall = ->
-      # Coderwall badges
+      # DOM element to use
       if @$element.find('div.badges').length>0
+        # use existing div.badges if any
         $divBadges = @$element.find('div.badges')
       else
+        # create a div.badges
         $divBadges = $('
').addClass('badges')
         @$element.append(
           $divBadges
         )

-      urlCoderwall = 'http://www.coderwall.com/'+@settings.user+'.json'
+      # api url definition
+      urlCoderwall = "http://www.coderwall.com/#{ @settings.user }.json"
       if @settings.coderwallForceJson
         urlCoderwall = @settings.coderwallForceJson
+
+      # api call
+      that = @
       $.ajax urlCoderwall,
         success: (data, textStatus, jqXHR) ->
-          # Filter
+          # Filter badges
           badges = data.badges.slice(0)
           for dataBadge in data.badges
             levels = new Array()
             for badge in data.badges
               if badge.name.indexOf(dataBadge.name)!=-1
                 levels.push(badge)
-
+            # keep only top levels badges
             if levels.length>1
               for level in levels when levels.indexOf(level) != levels.length-1
                 badges.splice(badges.indexOf(level),1)

-          # Display
+          # Display badges
           for dataBadge in badges
-            date = new Date(dataBadge.created)
-            created = ("0"+date.getDate()).slice(-2) +
-              '-' + ("0"+(date.getMonth()+1)).slice(-2) +
-              '-' + date.getFullYear()
-            template =
-            $divBadges.append(
-              """
-

-

-
-

-

-

-                    #{ dataBadge.name }
-                    #{ created }
-

-
#{ dataBadge.description }

-

-

-              """
-            )
+            $divBadges.append("""
+

+

+
+

+

+

+      #{ dataBadge.name }
+      #{ that.formatDate(dataBadge.created) }
+

+
#{ dataBadge.description }

+

+

+""")

     @github = ->
       # GitHub Repositories


Keep existing array if it has contents

Keep existing array if it has contents

--- components/StorageReducer.coffee
+++ components/StorageReducer.coffee
@@ -68,15 +68,15 @@
         state = {}
         project = data.payload
         project.graphs = [] unless project.graphs
-        if data.state.storedGraphs
+        if data.state.storedGraphs and not project.graphs.length
           project.graphs = data.state.storedGraphs.filter (item) ->
             item.properties.project is project.id
         project.components = [] unless project.components
-        if data.state.components
+        if data.state.components and not project.components.length
           project.components = data.state.components.filter (item) ->
             item.project is project.id
         project.specs = [] unless project.specs
-        if data.state.specs
+        if data.state.specs and not project.specs.length
           project.specs = data.state.specs.filter (item) ->
             item.project is project.id
         state.projects = data.state.projects or []


fix processPushEvent

fix processPushEvent

--- src/github-tfs.coffee
+++ src/github-tfs.coffee
@@ -304,7 +304,7 @@
           robot.logger.debug body
           return
         else
-          buildResData = JSON.parse apiCallRes.body
+          buildResData = JSON.parse body
           robot.logger.debug buildResData

           robot.messageRoom room, "@#{pusher} just pushed code on #{repo}/#{branch}. Requested TFS build ##{buildResData.id} with #{tfsCollection}/#{tfsProject}/#{tfsDefinition}"


Head of html file recreated in teacup-view

--- src/html/index.coffee
+++ src/html/index.coffee
@@ -2,6 +2,37 @@

 module.exports = new View (options) ->
   @doctype 5
-  @html =>
+  @html lang: 'en', =>
+    @head =>
+      @title "Responsa.eu"
+
+      @meta charset :"utf-8">
+      @meta 'http-equiv': "X-UA-Compatible", content: "IE=edge"
+      @meta
+        name    : "viewport"
+        content : "width=device-width, initial-scale=1"
+      @meta
+        name    : "description"
+        content : "Responsa.eu project"
+      @meta
+        name    : "author"
+        content : "Responsa.eu team"
+
+      @link href: url, rel: 'stylesheet', type: "text/css" for url in [
+        "css/bootstrap.min.css"
+        "css/landing-page.css"
+        "font-awesome/css/font-awesome.min.css"
+        # TODO: use protocol relative url
+        "http://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic"
+      ]
+
+
+      # HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries
+      # WARNING: Respond.js doesn't work if you view the page via file://
+      @ie 'lt IE9', => @script src: url for url in [
+        "https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"
+        "https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"
+      ]
+
     @body =>
       @h1 'Hello, Teacup!'


add moduleNotFound error

add moduleNotFound error

--- src/lib/facade.coffee
+++ src/lib/facade.coffee
@@ -326,7 +326,9 @@
         fullName   = @fullName(modFullName)

         if not @nonExistingClassNames[modFullName] # avoid searching non-existing files many times
-            klass = @getModule(moduleName).requireOwn(fullName)
+            mod = @getModule(moduleName)
+            throw @error('moduleNotFound', "module '#{moduleName}' is not found") if not mod?
+            klass = mod.requireOwn(fullName)

         if not klass?
             @nonExistingClassNames[modFullName] = true


User-updater: keep users in memory

User-updater: keep users in memory

--- scripts/user-updater/main.coffee
+++ scripts/user-updater/main.coffee
@@ -25,6 +25,8 @@
   JReferral = rekuire 'referral/index.coffee'
   JUser     = rekuire 'user/index.coffee'

+  userCache = {}
+
   # Config
   # ------

@@ -48,6 +50,9 @@

   fetchAccount = (_id, cb)->

+    if cached = userCache[_id]
+      return cb null, cached
+
     JAccount.one {_id}, (err, account)->
       return cb err  if err
       return cb {message: "no account found"}  unless account
@@ -56,7 +61,7 @@
         return cb err  if err
         return cb {message: "no user found"}  unless user

-        cb null, {user, account}
+        cb null, userCache[_id] = {user, account}

   fetchMembers = (referral, as, cb)->


Multi-platform icons.

Multi-platform icons.

--- src/UI/main.coffee
+++ src/UI/main.coffee
@@ -8,6 +8,8 @@
 # to use arrays and objects combined in the same style property.

 is_linux = process.platform == 'linux'
+is_win = process.platform == 'win32'
+is_mac = process.platform == 'darwin'

 electron = require 'electron'
 ewin = electron.remote.getCurrentWindow()
@@ -135,8 +137,15 @@

 trayMenu = Menu.buildFromTemplate trayMenuTemplate

-#TODO: make it multiplatform
-tray = new Tray __dirname + '/../../assets/icons/win/icon.ico'
+if is_win
+    icon = '/../../assets/icons/win/icon.ico'
+if is_mac
+    icon = '/../../assets/icons/mac/icon.icns'
+else
+    icon = '/../../assets/icons/png/24x24.png'
+
+
+tray = new Tray __dirname + icon

 tray.setContextMenu trayMenu
 tray.on 'click', ->


SocketCluster support

SocketCluster support

--- src/avs-rpc.coffee
+++ src/avs-rpc.coffee
@@ -154,3 +154,23 @@
     else
         @log args = "error: method #{msg.method} is unknown"
         ack_cb null, args
+
+#
+# class scRpc extends ioRpc
+#
+exports.scRpc = class scRpc extends scRpc
+  process: (message, ack_cb) ->
+    msg = json.parse message
+    @log "rpc #{msg.id}: in  #{@tag} #{message}"
+
+    if local = @locals[msg.method]
+      try
+        args = msg.args or []
+        args.push => ack_cb.apply @, arguments
+        if local.asynchronous then local[msg.method] msg.id, args else ack_cb null, local[msg.method] msg.id, args
+      catch e
+        @log args = "error in #{msg.method}: #{e}"
+        ack_cb args
+    else
+        @log args = "error: method #{msg.method} is unknown"
+        ack_cb args


all working!

all working!

--- scripts/util/cursor_cache.coffee
+++ scripts/util/cursor_cache.coffee
@@ -73,16 +73,11 @@
   # return the last node along that path

   clearPath = (node, key, parent, path) ->
-    console.log 'clear path at path ' + path + ' opening'
     del node, cursorSymbol
     nextKey = path[0]
     nextNode = get node, nextKey
-    final = clearPath nextNode, nextKey, node, path.slice 1 if nextNode?
+    clearPath nextNode, nextKey, node, path.slice 1 if nextNode?
     del parent, key if parent? and empty node
-
-    console.log 'clear path at path ' + path + ' returning ' + if next? then final else node
-
-    if next? then final else node

   clearPath: (path) ->
     clearPath @root, null, null, path
@@ -100,24 +95,25 @@
     node

   clearObject: (path, obj) ->
-    target = @clearPath path
+    @clearPath path
+
+    # at some point make the clear path call above just return the target
+    target = @root
+    for key in path
+      return unless (target = get target, key)?
+
     clearObject target, null, null, obj

   # clear certain elements in an array by index, shifting following elements

   spliceArray: (path, start, deleteCount, addCount) ->
-    console.log 'splice array called'
-    console.log arguments
-    console.log path
+    @clearPath path

-    target = @clearPath path
-
-    console.log target
-    console.log target.size
-    console.log @data()
-    i = target.keys()
-    console.log(value) while ({done, value} = i.next()) and not done
+    # at some point make the clear path call above just return the target
+    target = @root
+    for key in path
+      return unless (target = get target, key)?

     unless target instanceof Array
       throw new Error 'CursorCache attempted spliceArray on non array'


Fix deprecated selectors in the keymap

Fix deprecated selectors in the keymap

--- keymaps/rails.cson
+++ keymaps/rails.cson
@@ -7,10 +7,10 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'.workspace':
+'.atom-workspace':
   'ctrl-alt-o': 'rails:toggle'

-'.editor.vim-mode.command-mode:not(.insert-mode)':
+'.atom-text-editor.vim-mode.command-mode:not(.insert-mode)':
   'g m': 'rails:go-to-model'
   'g v': 'rails:go-to-view'
   'g c': 'rails:go-to-controller'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -43,7 +43,7 @@
   ###*
   Get the object closest to this one.

-  @param {Object|Array|String} groupB An object or set of objects to check collisions with
+  @param {Object|Array|String} selector An object or set of objects to check collisions with
   ###
   closest: (selector) ->
     if Object.isString(selector)


oauth: fix bug from previous commit

oauth: fix bug from previous commit

--- server/lib/server/helpers.coffee
+++ server/lib/server/helpers.coffee
@@ -95,11 +95,12 @@
         then callback err, no, account
         else callback null, yes, account

-saveOauthToSession = (resp, clientId, callback)->
+saveOauthToSession = (oauthInfo, clientId, callback)->
   {JSession} = koding.models
-  query = {"foreignAuth.#{resp.provider}" : resp}
+  query      = {}
+  query["foreignAuth.#{oauthInfo.provider}"] = oauthInfo

-  JSession.update { clientId }, $set:query, callback
+  JSession.update {clientId}, $set:query, callback

 renderOauthPopup = (res, locals)->
   {projectRoot}    = KONFIG


Log process fork address

Log process fork address

--- server/controllers/deploy.coffee
+++ server/controllers/deploy.coffee
@@ -12,7 +12,7 @@
     console.log "Ignoring, only deploys on pushes from deploy"
     return res.send 500, "Only commits from deploy branch are accepted"

-  console.log "Forking deploy.coffee"
+  console.log "Forking #{process.cwd()}/bin/deploy.coffee"
   cp.fork("#{process.cwd()}/bin/deploy.coffee")
   console.log "Forked!"


各種項目を追加

各種項目を追加

--- lamjet_init.coffee
+++ lamjet_init.coffee
@@ -12,7 +12,7 @@
 stdin.setEncoding("utf8")

 question = (stdout, stdin, message, defaultValue)->
-  stdout.write "#{message} [#{defaultValue}] "
+  stdout.write "#{message} [#{defaultValue}] ? "
   buffer  = ""
   pattern = /^(.*?)\n/

@@ -37,11 +37,25 @@
     stdin.on "readable", onReadable
     stdin.on "end", onEnd

+config = {
+  functionName: "defFunName",
+  region: "us-east-1",
+  role: "arn:aws:iam::ACCOUNTID:role/ROLENAME",
+  memorySize: 128,
+  timeout: 3,
+}
+
 Promise.resolve()
-  .then (result)-> question(stdout, stdin, "hoge?", "yes")
+  .then (result)-> question(stdout, stdin, "Function name", config.functionName)
+  .then (result)-> config.functionName = result
+  .then (result)-> question(stdout, stdin, "Region", config.region)
+  .then (result)-> config.region = result
+  .then (result)-> question(stdout, stdin, "Role", config.role)
+  .then (result)-> config.role = result
+  .then (result)-> question(stdout, stdin, "Memory size in MB", String(config.memorySize))
+  .then (result)-> config.memorySize = Number(result)
+  .then (result)-> question(stdout, stdin, "Timeout in sec", String(config.timeout))
+  .then (result)-> config.timeout = Number(result)
+  .then (result)-> stdin.pause()
   .then (result)->
-    console.log(JSON.stringify({then: result}, null, 2))
-  .then (result)->
-    question(stdout, stdin, "foo?", "z")
-  .then (result)->
-    console.log(JSON.stringify({then: result}, null, 2))
+    console.log(JSON.stringify({config: config}, null, 2))


Trakttv.fetchToWatchList: sometimes the inner async.series calls the finall callback with a function passed as err. To work around this, always return the resulting shows, ignoring the error.

--- src/js/trakttv.coffee
+++ src/js/trakttv.coffee
@@ -161,7 +161,7 @@
                 doneItem()
           (err) =>
             events.emit 'update', 'shows', shows: shows
-            callback err, shows
+            callback null, shows
         )
     )


Lower default ping interval to 30s

Lower default ping interval to 30s

--- src/matteruser.coffee
+++ src/matteruser.coffee
@@ -24,7 +24,7 @@
             @robot.logger.emergency "MATTERMOST_GROUP is required"
             process.exit 1

-        @client = new MatterMostClient mmHost, mmGroup, mmUser, mmPassword, {wssPort: mmWSSPort}
+        @client = new MatterMostClient mmHost, mmGroup, mmUser, mmPassword, {wssPort: mmWSSPort, pingInterval: 30000}

         @client.on 'open', @.open
         @client.on 'loggedIn', @.loggedIn


Improved simulation leaderboard performance

--- server/users/user_handler.coffee
+++ server/users/user_handler.coffee
@@ -165,13 +165,11 @@
       simulatedByQuery[if req.query.order is 1 then "$gt" else "$lte"] = req.query.scoreOffset
       query.simulatedBy = simulatedByQuery
       sortOrder = 1 if req.query.order is 1
-    aggregation = User.aggregate [
-      {$match: query}
-      {$project:{"name":1, "simulatedBy": 1, "simulatedFor":1}}
-      {$sort: {"simulatedBy":sortOrder}}
-      {$limit: limit}
-    ]
-    aggregation.exec (err, otherUsers) ->
+    else
+      query.simulatedBy = {"$exists": true}
+
+    leaderboardQuery = User.find(query).select("name simulatedBy simulatedFor").sort({"simulatedBy":sortOrder}).limit(limit)
+    leaderboardQuery.exec (err, otherUsers) ->
         otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1
         otherUsers ?= []
         res.send(otherUsers)


Comment-out v0.10 features.

Comment-out v0.10 features.

--- src/command.coffee
+++ src/command.coffee
@@ -39,26 +39,26 @@
           help: "build path"
           metavar: "DIRECTORY"
           full: "output"
-        mvc:
-          help: "Set application framework"
-          metavar: "FRAMEWORK"
-          default: "backbone"
-          choices: ["backbone", "batman"]
-        templates:
-          help: "Set templates engine"
-          metavar: "ENGINE"
-          default: "eco"
-          choices: ["eco", "jade", "haml"]
-        styles:
-          help: "Set style engine"
-          metavar: "ENGINE"
-          default: "css"
-          choices: ["css", "sass", "compass", "stylus"]  # "sass" == "compass"
-        tests:
-          help: "Set testing framework"
-          metavar: "FRAMEWORK"
-          default: "jasmine"
-          choices: ["jasmine", "nodeunit"]
+        #mvc:
+        #  help: "Set application framework"
+        #  metavar: "FRAMEWORK"
+        #  default: "backbone"
+        #  choices: ["backbone", "batman"]
+        #templates:
+        #  help: "Set templates engine"
+        #  metavar: "ENGINE"
+        #  default: "eco"
+        #  choices: ["eco", "jade", "haml"]
+        #styles:
+        #  help: "Set style engine"
+        #  metavar: "ENGINE"
+        #  default: "css"
+        #  choices: ["css", "sass", "compass", "stylus"]  # "sass" == "compass"
+        #tests:
+        #  help: "Set testing framework"
+        #  metavar: "FRAMEWORK"
+        #  default: "jasmine"
+        #  choices: ["jasmine", "nodeunit"]
       callback: (options) ->
         brunch.new options, ->
           brunch.build parseOpts options


Strip extension for required modules.

Strip extension for required modules.

--- src/middleware.coffee
+++ src/middleware.coffee
@@ -9,7 +9,9 @@
         middleware req, res, next
       return

-    url = req.url
+    url = req.url.replace /\.\w+$/, ''
+
+    console.log url: url

     unless (mod = bundle.find url)?
       return next()


implementing table-relative borders

implementing table-relative borders

--- src/mkts-table.coffee
+++ src/mkts-table.coffee
@@ -294,18 +294,27 @@
     throw new Error "(MKTS/TABLE 6043) expected a text for cellborder, got a #{rpr type}"
   unless ( groups = cellborder.match /^(.+):(.+):(.*)$/ )?
     throw new Error "(MKTS/TABLE 5822) expected a cellborder like 'a1:left:sDashed,sThick', got #{rpr cellborder}"
-  [ _, quads, sides, style, ] = groups
+  [ _, cellhints, sides, style, ] = groups
+  #.........................................................................................................
+  sides = ( _.trim() for _ in sides.split ',' )
+  sides = [ 'top', 'left', 'bottom', 'right', ] if '*' in sides
   #.........................................................................................................
   ### TAINT this will have to be changed to allow for named cells ###
-  cells = if '*' in quads
-    Object.keys me.cellquads
+  cellhints = new Set ( _.trim() for _ in cellhints.split ',' )
+  if cellhints.has '*'
+    cells = Object.keys me.cellquads
   else
-    @_cellnames_from_quadkeys me, ( _.trim() for _ in quads.split ',' )
-  #.........................................................................................................
-  sides = if '*' in sides
-    [ 'top', 'left', 'bottom', 'right', ]
-  else
-    ( _.trim() for _ in sides.split ',' )
+    ### TAINT as it stands, `cellborder'table:bottom,right:red'` will style all bottom and right borders
+    of all cells that have real estate along the bottom and right borders of the table. An improved version
+    should probably only affect the bottom borders of table-bottom cells and the right borders of
+    table-right cells. Use two statements `cellborder'table:bottom:red'`, `cellborder'table:right:red'` to
+    express that meaning FTTB. ###
+    if cellhints.has 'table'
+      cellhints.delete 'table'
+      for side in sides
+        cellhints.add d.quadkey for d from @_walk_table_edge_quads me, side
+    quadkeys  = ( cellhint for cellhint from cellhints )
+    cells     = @_cellnames_from_quadkeys me, quadkeys
   #.........................................................................................................
   style = style.trim()
   style = null if style in [ 'none', '', ]
@@ -607,7 +616,8 @@
 @_cellnames_from_quadkeys = ( me, quadkeys ) ->
   R = new Set()
   for quadkey in quadkeys
-    R.add cellname for cellname in me.quadcells
+    continue unless ( quadcells = me.quadcells[ quadkey ] )?
+    R.add cellname for cellname in quadcells
   return ( cellname for cellname from R )

@@ -638,6 +648,56 @@
       col_letter  = ( EXCJSCC.n2l col_nr ).toLowerCase()
       designation = "#{col_letter}#{row_nr}"
       yield { col_nr, row_nr, col_letter, designation, }
+  yield return
+
+#-----------------------------------------------------------------------------------------------------------
+@_walk_table_edge_cellnames = ( me, edge ) ->
+  seen_cellnames = new Set()
+  for d from @_walk_table_edge_quads me, edge
+    continue unless ( cellnames = me.quadcells[ d.quadkey ] )?
+    for cellname in cellnames
+      continue if seen_cellnames.has cellname
+      seen_cellnames.add cellname
+      yield cellname
+  yield return
+
+#-----------------------------------------------------------------------------------------------------------
+@_walk_table_edge_quads = ( me, edge ) ->
+  switch edge
+    when 'left'
+      col_nr_1    = 1
+      col_nr_2    = 1
+      row_nr_1    = 1
+      row_nr_2    = me.gridheight
+    when 'right'
+      col_nr_1    = me.gridwidth
+      col_nr_2    = me.gridwidth
+      row_nr_1    = 1
+      row_nr_2    = me.gridheight
+    when 'top'
+      col_nr_1    = 1
+      col_nr_2    = me.gridwidth
+      row_nr_1    = 1
+      row_nr_2    = 1
+    when 'bottom'
+      col_nr_1    = 1
+      col_nr_2    = me.gridwidth
+      row_nr_1    = me.gridheight
+      row_nr_2    = me.gridheight
+    when '*'
+      yield from @_walk_table_edge_quads me, 'left'
+      yield from @_walk_table_edge_quads me, 'right'
+      yield from @_walk_table_edge_quads me, 'top'
+      yield from @_walk_table_edge_quads me, 'bottom'
+      yield return
+    else
+      throw new Error "(MKTS/TABLE 4550) illegal argument for edge #{rpr edge}"
+  for row_nr in [ row_nr_1 .. row_nr_2 ]
+    for col_nr in [ col_nr_1 .. col_nr_2 ]
+      ### TAINT don't use EXCJSCC directly ###
+      col_letter  = ( EXCJSCC.n2l col_nr ).toLowerCase()
+      quadkey     = "#{col_letter}#{row_nr}"
+      yield { col_nr, row_nr, col_letter, quadkey, edge, }
   yield return


Removed setLogger. Renamed last logger to console logger. Added docpad-debug.log output if we are running in debug log level.

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -157,20 +157,16 @@
 		@serverExpress = servers.serverExpress
 		@serverHttp = servers.serverHttp

-	# The caterpillar instance bound to docpad
+	# The caterpillar instances bound to docpad
 	loggerInstances: null
-	getLogger: (all=false) ->
-		if all
-			@loggerInstances
+	getLogger: -> @loggerInstances?.logger
+	getLoggers: -> @loggerInstances
+	setLoggers: (loggers) ->
+		if @loggerInstances
+			@warn('Loggers have already been set')
 		else
-			@loggerInstances?.logger
-	setLogger: (value,all=false) ->
-		if all or value.logger?
-			@loggerInstances = value
-		else
-			@loggerInstances ?= {}
-			@loggerInstances.logger = value
-		@
+			@loggerInstances = loggers
+		return loggers

 	# The action runner instance bound to docpad
 	actionRunnerInstance: null
@@ -969,12 +965,27 @@
 				locale = docpad.getLocale()
 				docpad.log('warn', locale.trackError+'\n'+locale.errorFollows, err)

-		# Initialize the logger
-		logger = new (require('caterpillar').Logger)()
-		filter = new (require('caterpillar-filter').Filter)
-		last = human = new (require('caterpillar-human').Human)
-		logger.pipe(filter).pipe(human).pipe(process.stdout)
-		@setLogger({logger,filter,human,last})
+		# Initialize the loggers
+		if (loggers = instanceConfig.loggers)
+			delete instanceConfig.loggers
+		else
+			# Create
+			logger = new (require('caterpillar').Logger)()
+
+			# console
+			(console = logger
+				.pipe(
+					new (require('caterpillar-filter').Filter)
+				).pipe(
+					new (require('caterpillar-human').Human)
+				)
+			).pipe(process.stdout)
+
+			# Apply
+			loggers = {logger,console}
+
+		# Configure
+		@setLoggers(loggers)
 		@setLogLevel(6)

 		# Log to bubbled events
@@ -1279,7 +1290,6 @@
 		@setServer(@config.server)  if @config.server

 		# Extract and apply the logger
-		@setLogger(@config.logger)  if @config.logger
 		@setLogLevel(@config.logLevel)

 		# Resolve any paths
@@ -1795,6 +1805,15 @@
 	# Set Log Level
 	setLogLevel: (level) ->
 		@getLogger().setConfig({level})
+		if level is 7
+			loggers = @getLoggers()
+			loggers.debug ?= loggers.logger
+				.pipe(
+					new (require('caterpillar-human').Human)
+				)
+				.pipe(
+					require('fs').createWriteStream(process.cwd()+'/docpad-debug.log')
+				)
 		@

 	# Are we debugging?
@@ -2862,11 +2881,11 @@
 		# Progress
 		if @getLogLevel() is 6 and 'development' in @getEnvironments()
 			opts.progress ?= require('progressbar').create()
-			docpad.getLogger(true).last.unpipe(process.stdout)
+			docpad.getLoggers().console.unpipe(process.stdout)
 		finish = (err) ->
 			opts.progress?.finish()
 			opts.progress = null
-			docpad.getLogger(true).last.pipe(process.stdout)
+			docpad.getLoggers().console.pipe(process.stdout)
 			return next(err)

 		# Re-load and re-render only what is necessary


add comment for Facade#init

add comment for Facade#init

--- src/lib/facade.coffee
+++ src/lib/facade.coffee
@@ -39,6 +39,7 @@
         @init()

+    # for base-domainify. keep it empty
     init: ->


fixed regexp

fixed regexp

--- src/numbersapi.coffee
+++ src/numbersapi.coffee
@@ -14,7 +14,7 @@
 #   Víctor San Martín

 module.exports = (robot) ->
-  robot.respond /numbersapi ([0-9])/i, (msg) ->
+  robot.respond /numbersapi ([0-9]+)/i, (msg) ->
     msg
       .http("http://numbersapi.com/" + msg.match[1])
       .get() (err, res, body) ->


Change some phrasing in the module

Change some phrasing in the module

--- library/nucleus/watch.coffee
+++ library/nucleus/watch.coffee
@@ -77,7 +77,7 @@
         return if resolved is require.main.filename
         fails = "Exception in module at #{path}:\r\n%s"
         relative = paths.relative process.cwd(), path
-        addition = "Adding module found at %s".cyan
+        addition = "Add module at %s".cyan.toString()
         logger.info addition, relative.underline
         return go() if resolved of require.cache
         try require resolved; go() catch error
@@ -101,7 +101,7 @@
         return if resolved is require.main.filename
         fails = "Exception in module at #{path}:\r\n%s"
         relative = paths.relative process.cwd(), path
-        changing = "Changing module found at %s".cyan
+        changing = "Change module at %s".cyan.toString()
         logger.info changing, relative.underline
         try require resolved; go() catch error
             logger.warn fails.red, error.stack
@@ -116,7 +116,7 @@
         modules = @constructor.EXTENSIONS
         extension = paths.extname absolute
         return unless extension in modules
-        unlink = "Unlinking module found at %s".cyan
+        unlink = "Unlink module at %s".cyan.toString()
         relative = paths.relative process.cwd(), path
         logger.info unlink, relative.toString().underline
         registry = (router = @kernel.router)?.registry or []


better phrasing

better phrasing

--- library/nucleus/watch.coffee
+++ library/nucleus/watch.coffee
@@ -257,7 +257,7 @@
         formats = [notExists.grey, relative.underline]
         return unless @directoryTracking directory
         return logger.warn formats... unless exists
-        watching = "Watching %s directory for modules"
+        watching = "Watching %s directory for changes"
         logger.info watching.blue, relative.underline
         watcher = chokidar.watch directory.toString()
         watcher.on "unlink", @hotSwappingUnlink.bind @


Send list command output in one send command in order to ensure the right order

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -103,11 +103,16 @@
   robot.respond /(?:grafana|graph|graf) list/i, (msg) ->
     callGrafana "search", (dashboards) ->
       robot.logger.debug dashboards
-      msg.send "Available dashboards:"
+      response = "Available dashboards:\n"

       for dashboard in dashboards
         slug = dashboard.uri.replace /^db\//, ""
-        msg.send "- #{slug}: #{dashboard.title}"
+        response = response + "- #{slug}: #{dashboard.title}\n"
+
+      # Remove trailing newline
+      response.trim()
+
+      msg.send response

   sendError = (message, msg) ->
     robot.logger.error message


Revert "[Trivial] Additional logging"

Revert "[Trivial] Additional logging"

This reverts commit d9a6165363586594b733cc75171dedb52c87b073.

Turns out the SDK isn't providing any useful information there.

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -232,6 +232,6 @@
         sendRobotResponse msg, title, client.https(filename), link
       else
         robot.logger.debug res
-        robot.logger.error "Upload Error: #{res.statusMessage} (#{res.statusCode})"
-        msg.send "#{title} - [Error] - #{link}"
+        robot.logger.error "Upload Error Code: #{res.statusCode}"
+        msg.send "#{title} - [Upload Error] - #{link}"
     req.end(content);


Don't use US equivalent for events w/ alt as the only modifier

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -44,7 +44,7 @@

   # Use US equivalent character for non-latin characters in keystrokes with modifiers
   keyIsNonLatin = key.length is 1 and key.charCodeAt(0) > EndOFLatinCharCodeRange
-  keystrokeHasModifiers = (event.ctrlKey or event.altKey or event.metaKey)
+  keystrokeHasModifiers = (event.ctrlKey or event.metaKey)
   if keyIsNonLatin and keystrokeHasModifiers
     if characters = usCharactersForKeyCode(event.code)
       if event.shiftKey


Refactor pluginHelpers block

Refactor pluginHelpers block

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -149,15 +149,11 @@
     joinConfig[type].pluginHelpers = configFiles[type].pluginHelpers or
       do ->
         destFiles = Object.keys joinConfig[type]
-        vendorMatch = destFiles.filter (file) ->
-          joinConfig[type][file] 'vendor/.'
-        if vendorMatch.length > 0
-          return vendorMatch[0]
-        vendorFiles = destFiles.filter (file) -> /vendor/i.test file
-        if vendorFiles.length > 0
-          vendorFiles[0]
-        else
-          destFiles.shift()
+        joinMatch = destFiles.filter (file) -> joinConfig[type][file] 'vendor/.'
+        return joinMatch[0] if joinMatch.length > 0
+        nameMatch = destFiles.filter (file) -> /vendor/i.test file
+        return nameMatch[0] if nameMatch.length > 0
+        destFiles.shift()

   Object.freeze(joinConfig)


Fixed helpers indentation.

Fixed helpers indentation.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -1,8 +1,8 @@
 exports.getHostname = (url) ->
-		parts = url
-						.replace('http://','')
-						.replace('https://','')
-						.replace('www.','')
-						.split(/[/?\u0023]/)
+    parts = url
+            .replace('http://','')
+            .replace('https://','')
+            .replace('www.','')
+            .split(/[/?\u0023]/)

-		parts[0]+    parts[0]


Allow native context menu to be display

Allow native context menu to be display

--- tree.jquery.coffee
+++ tree.jquery.coffee
@@ -473,6 +473,7 @@
         saveState: false  # true / false / string (cookie name)
         dragAndDrop: false
         selectable: false
+        useContextMenu: true
         onCanSelectNode: null
         onSetStateFromStorage: null
         onGetStateFromStorage: null
@@ -748,7 +749,8 @@
         @_initData()

         @element.click($.proxy(@_click, this))
-        @element.bind('contextmenu', $.proxy(@_contextmenu, this))
+        if @options.useContextMenu
+            @element.bind('contextmenu', $.proxy(@_contextmenu, this))

     _deinit: ->
         @element.empty()


Fix a bug caused by missing cloning statement


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -19,6 +19,7 @@
       } = meterData

       if not I[name]
+        I[n]

       ratio = I[name] / I["max#{name.capitalize()}"]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -112,7 +112,7 @@
     text: 'Boss Health'
     width: 150

-  # => Sets up a health meter, u
+  # => Sets up a health meter, using all the con
   </pre></code>

   @name meter


namespaced Movable

namespaced Movable

--- src/movable.coffee
+++ src/movable.coffee
@@ -42,7 +42,7 @@
   I.acceleration = Point(I.acceleration.x, I.acceleration.y)
   I.velocity = Point(I.velocity.x, I.velocity.y)

-  self.unbind ".M"
+  self.unbind ".Movable" #

   self.bind 'update.Movable', (dt) ->
     I.velocity = I.velocity.add(I.acceleration.scale(dt))


lock_refs maps actual objects, not their reference numbers

--- src/natives.coffee
+++ src/natives.coffee
@@ -313,7 +313,7 @@
       Thread: [
         o 'currentThread()L!/!/!;', (rs) -> rs.curr_thread
         o 'setPriority0(I)V', (rs) -> # NOP
-        o 'holdsLock(L!/!/Object;)Z', (rs, obj) -> rs.curr_thread is rs.lock_refs[obj.ref]
+        o 'holdsLock(L!/!/Object;)Z', (rs, obj) -> rs.curr_thread is rs.lock_refs[obj]
         o 'isAlive()Z', (rs, _this) -> _this.fields.$isAlive ? false
         o 'isInterrupted(Z)Z', (rs, _this, clear_flag) ->
             tmp = _this.fields.$isInterrupted ? false


work with properties

work with properties

--- src/obserer.coffee
+++ src/obserer.coffee
@@ -31,11 +31,33 @@

     tag  = adapter.get_attr(from, 'tagName')
     type = adapter.get_attr(from, 'type')
-
+    # FIXME maybe save all needed attributes in hash ????
     handler = (e) ->
-      result = {text: null, attrribute: null}
+      result = {text: null, attribute: null}
       if e.type == "input" || e.type == "childList" || e.type == "change"
         result['text'] = adapter.text(from)
+      if e.type == "attributes"
+        attr_name = e.attributeName
+        old_attr = e.oldValue || [] # FIXME remove this, because not used
+        new_attr  = adapter.get_attr(from, attr_name)
+
+        result['text'] = new_attr
+
+#        # when old < new, then we add value, send diff
+#        # when old = new, then swap value, send new
+#        # when old > new, then we remove value, send new
+#        new_value = null
+#        old_attr_length = old_attr.length
+#        new_attr_length = new_attr.length
+#
+#        if old_attr_length < new_attr_length
+#
+#        else if old_attr_length == new_attr_length
+#
+#        else # old > new
+
+
+        result['attribute'] = attr_name
       clb(result)

     if ONCHANGE_TAGS.indexOf(tag) != -1
@@ -52,7 +74,13 @@
           mutations.forEach handler
         )

-        cnf = { childList: true, attributes: true, characterData: true, subtree: false } # FIXME subtree: true
+        cnf =
+          childList: true
+          attributes: true
+          characterData: true
+          attributeOldValue: true
+          characterDataOldValue: true
+          subtree: false # FIXME subtree: true

         elems = adapter.get(@from_element) # fixme : all


Keep old context menu format in api preview mode

--- src/package.coffee
+++ src/package.coffee
@@ -203,16 +203,15 @@
       try
         itemsBySelector = map['context-menu']

-        if includeDeprecatedAPIs
-          # Detect deprecated format for items object
-          for key, value of itemsBySelector
-            unless _.isArray(value)
-              deprecate("""
-                The context menu CSON format has changed. Please see
-                https://atom.io/docs/api/latest/ContextMenuManager#context-menu-cson-format
-                for more info.
-              """, {packageName: @name})
-              itemsBySelector = atom.contextMenu.convertLegacyItemsBySelector(itemsBySelector)
+        # Detect deprecated format for items object
+        for key, value of itemsBySelector
+          unless _.isArray(value)
+            deprecate("""
+              The context menu CSON format has changed. Please see
+              https://atom.io/docs/api/latest/ContextMenuManager#context-menu-cson-format
+              for more info.
+            """, {packageName: @name})
+            itemsBySelector = atom.contextMenu.convertLegacyItemsBySelector(itemsBySelector)

         @activationDisposables.add(atom.contextMenu.add(itemsBySelector))
       catch error


Implement an argument parser that supports primitives (primary and special data types) and keypaths. [#197]

--- src/parsers.coffee
+++ src/parsers.coffee
@@ -17,6 +17,43 @@
         current.path += char

     tokens.push current
+    tokens
+
+# Rivets.ArgumentParser
+# ---------------------
+
+# Parser and tokenizer for arguments within binding declarations.
+class Rivets.ArgumentParser
+  @types:
+    primitive: 0
+    keypath: 1
+
+  @parse: (args) ->
+    tokens = []
+
+    for arg in args
+      tokens.push if /^'.*'$/.test arg
+        type: @types.primitive
+        value: arg.slice 1, -1
+      else if arg is 'true'
+        type: @types.primitive
+        value: true
+      else if arg is 'false'
+        type: @types.primitive
+        value: false
+      else if arg is 'null'
+        type: @types.primitive
+        value: null
+      else if arg is 'undefined'
+        type: @types.primitive
+        value: undefined
+      else if isNaN(Number(arg)) is false
+        type: @types.primitive
+        value: Number arg
+      else
+        type: @types.keypath
+        value: arg
+
     tokens

 # Rivets.TextTemplateParser


Support array-of-longs socket API

Support array-of-longs socket API

--- irc.coffee
+++ irc.coffee
@@ -46,6 +46,45 @@
 	chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 	(chars[Math.floor(Math.random() * chars.length)] for x in [0...length]).join('')

+# Many thanks to Dennis for his StackOverflow answer: http://goo.gl/UDanx
+string2ArrayBuffer = (string, callback) ->
+  bb = new WebKitBlobBuilder()
+  bb.append(string)
+  f = new FileReader()
+  f.onload = (e) ->
+    callback(e.target.result)
+  f.readAsArrayBuffer(bb.getBlob())
+
+arrayBuffer2String = (buf, callback) ->
+  bb = new WebKitBlobBuilder()
+  bb.append(buf)
+  f = new FileReader()
+  f.onload = (e) ->
+    callback(e.target.result)
+  f.readAsText(bb.getBlob())
+
+arrayBufferToArrayOfLongs = (arrayBuffer) ->
+  longs = []
+  arrayBufferView = new Uint8Array(arrayBuffer)
+  for i in [0...arrayBufferView.length]
+    longs[i] = arrayBufferView[i]
+  longs
+
+arrayOfLongsToArrayBuffer = (longs) ->
+	ab = new ArrayBuffer longs.length
+	abView = new Uint8Array ab
+	for i in [0...longs.length]
+		abView[i] = longs[i]
+	ab
+
+toSocketData = (str, cb) ->
+	string2ArrayBuffer str, (ab) ->
+		cb arrayBufferToArrayOfLongs ab
+
+fromSocketData = (arr, cb) ->
+	ab = arrayOfLongsToArrayBuffer arr
+	arrayBuffer2String ab, cb
+
 class EventEmitter
 	on: (ev, cb) ->
 		@_listeners ?= {}
@@ -61,7 +100,7 @@
 		@socket = new net.Socket
 		@socket.on 'connect', => @onConnect()
 		@socket.on 'data', (data) => @onData data
-		@data = ''
+		@data = []

 	connect: ->
 		@socket.connect(@port, @server)
@@ -78,12 +117,11 @@
 		@send 'USER', @opts.nick, '0', '*', 'An irc5 user'

 	onData: (pdata) ->
-		@data += pdata
+		@data = @data.concat pdata
 		while @data.length > 0
 			cr = false
 			crlf = undefined
 			for d,i in @data
-				d = d.charCodeAt 0
 				if d == 0x0d
 					cr = true
 				else if cr and d == 0x0a
@@ -94,15 +132,16 @@
 			if crlf?
 				line = @data.slice(0, crlf-1)
 				@data = @data.slice(crlf+1)
-				console.log '<=', line.toString('utf8')
-				@onCommand(parseCommand line)
+				fromSocketData line, (lineStr) =>
+					console.log '<=', lineStr
+					@onCommand(parseCommand lineStr)
 			else
 				break

 	send: (args...) ->
 		msg = makeCommand args...
 		console.log('=>', msg[0...msg.length-2])
-		@socket.write(msg)
+		toSocketData msg, (arr) => @socket.write arr

 	onCommand: (cmd) ->
 		switch cmd.command


Allow hubot to emote (#208)

Allow hubot to emote (#208)

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -196,6 +196,9 @@

 	send: (envelope, strings...) =>
 		@chatdriver.sendMessage(str, envelope.room) for str in strings
+
+	emote: (envelope, strings...) =>
+		@chatdriver.sendMessage("_#{str}_", envelope.room) for str in strings

 	customMessage: (data) =>
 		@chatdriver.customMessage(data)


Replaced class method fat arrows with thin ones (#240)

That fat arrows break the binding attempts when code is transpiled by babel into es6, which means there's no way to use the adapter in an es6 hubot. Thanks to @timkinanne

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -41,7 +41,7 @@

 class RocketChatBotAdapter extends Adapter

-	run: =>
+	run: ->
 		@robot.logger.info "Starting Rocketchat adapter version #{pkg.version}..."

 		@robot.logger.info "Once connected to rooms I will respond to the name: #{@robot.name}"
@@ -217,16 +217,16 @@
 				@robot.logger.error "Unable to complete setup. See https://github.com/RocketChat/hubot-rocketchat for more info."
 			)

-	send: (envelope, strings...) =>
+	send: (envelope, strings...) ->
 		@chatdriver.sendMessage(str, envelope.room) for str in strings

-	emote: (envelope, strings...) =>
+	emote: (envelope, strings...) ->
 		@chatdriver.sendMessage("_#{str}_", envelope.room) for str in strings

-	customMessage: (data) =>
+	customMessage: (data) ->
 		@chatdriver.customMessage(data)

-	sendDirect: (envelope, strings...) =>
+	sendDirect: (envelope, strings...) ->
 		channel = @chatdriver.getDirectMessageRoomId(envelope.user.name)
 		Q(channel)
 		.then((chan) =>
@@ -237,17 +237,17 @@
 			@robot.logger.error "Unable to get DirectMessage Room ID: #{JSON.stringify(err)} Reason: #{err.reason}"
 		)

-	reply: (envelope, strings...) =>
+	reply: (envelope, strings...) ->
 		@robot.logger.info "reply"
 		isDM = envelope.room.indexOf(envelope.user.id) > -1
 		unless isDM
 			strings = strings.map (s) -> "@#{envelope.user.name} #{s}"
 		@send envelope, strings...

-	getRoomId: (room) =>
+	getRoomId: (room) ->
 		@chatdriver.getRoomId(room)

-	callMethod: (method, args...) =>
+	callMethod: (method, args...) ->
 		@chatdriver.callMethod(method, args)

 exports.use = (robot) ->


rename manifest to man to fix a problem

rename manifest to man to fix a problem

--- src/preview.coffee
+++ src/preview.coffee
@@ -46,17 +46,13 @@
     if err then return callback(err)
     return callback() unless json

-    manifest = mapManifest json
+    man = mapManifest json
     linkPath = path.resolve dir

-    if manifest.launcher == 'element'
-      elementPath = path.join('elements', manifest.name, manifest.version)
-      app.use '/' + elementPath, express.static(linkPath)
-    else
-      gadgetPath = "api/gadgets/#{manifest.username}/#{manifest.name}/#{manifest.version}/"
-      app.use '/' + gadgetPath, express.static(linkPath)
+    gadgetPath = "api/gadgets/#{man.username}/#{man.name}/#{man.version}/"
+    app.use '/' + gadgetPath, express.static(linkPath)

-    callback null, manifest
+    callback null, man

 mapManifest = (manifest) ->
   manifest.id = shortid.generate()


Further reorg.

Further reorg.

--- src/runWith.coffee
+++ src/runWith.coffee
@@ -1,18 +1,18 @@
 optimist = require 'optimist'
 commander = require "commander"
-faithful = require "faithful"
 Memoblock = require "Memoblock"
+{detectSeries,adapt} =  require "faithful"
 fsExists = require "fs-exists"
-fsExists = faithful.adapt fsExists
+fsExists = adapt fsExists
+

 createProject = require "./createProject"

-logg = undefined
 module.exports = runWith = ({cwd, env, containingDir, inputs, resolveValues, log}) ->
   templateName = inputs[1]
-  logg = log
+  templateDirs = ("#{path}/jumpstart-#{templateName}/template" for path in [cwd,containingDir])
   Memoblock.do([
-    -> @configFilePath = findConfigFilePath cwd, env.HOME
+    -> @configFilePath = detectSeries ("#{dir}/.jumpstart.json" for dir in [cwd,env.HOME]), fsExists
     -> log "Did not find config file in #{searchPaths.join ", "}." unless @configFilePath
     -> log "Using config file #{@configFilePath}." if @configFilePath
     -> throw new Error "Usage: jumpstart [project-name] [template-name]" if inputs.length isnt 2
@@ -22,25 +22,12 @@
     -> throw new Error "Supplied cwd #{cwd} does not exist." unless @cwdExists
     -> throw new Error "Supplied containing dir #{containingDir} does not exist." unless @cdExists
     -> @config = if @configFilePath then require @configFilePath else {}
-    -> @templateDir = findTemplateDir cwd, containingDir, templateName
+    -> @templateDir = detectSeries templateDirs, fsExists
+    -> throw new Error "Cannot find template in #{templateDirs.join ", or "}." unless @templateDir
+    -> log "Using template #{@templateDir}."
     -> @creatingProject = createProject @targetDir, @templateDir, @config, resolveValues, log
     -> log "Created #{@targetDir}."
   ])
-
-findTemplateDir = (cwd, containingDir, templateName) ->
-  logg "Searching for template #{templateName}."
-  searchPaths = [cwd,containingDir]
-  dirs = ("#{searchPath}/jumpstart-#{templateName}/template" for searchPath in searchPaths)
-  tryDir = (path) =>
-    logg "Trying #{path}"
-    fsExists path
-  faithful.detectSeries(dirs, tryDir).then (path) =>
-    throw new Error "Cannot find template #{templateName}." unless path
-    logg "Using template #{path}."
-    path
-
-findConfigFilePath = (cwd, home) ->
-  faithful.detectSeries(("#{dir}/.jumpstart.json" for dir in [cwd,home]), fsExists)

 # setDefault = (name, value) ->
 #   getConfig(config).then (config) ->


:memo: Use TomDoc

:memo: Use TomDoc

--- src/scanner.coffee
+++ src/scanner.coffee
@@ -41,10 +41,10 @@

   # Public: Find the next match on the line start at the given position
   #
-  # * line: the string being scanned.
-  # * firstLine: true if the first line is being scanned.
-  # * position: numeric position to start scanning at.
-  # * anchorPosition: numeric position of the last anchored match.
+  # line - the string being scanned.
+  # firstLine - true if the first line is being scanned.
+  # position - numeric position to start scanning at.
+  # anchorPosition - numeric position of the last anchored match.
   #
   # Returns an Object with details about the match or null if no match found.
   findNextMatch: (line, firstLine, position, anchorPosition) ->
@@ -56,11 +56,11 @@
   # Public: Handle the given match by calling `handleMatch` on the
   # matched {Pattern}.
   #
-  # * match: An object returned from a previous call to `findNextMatch`.
-  # * stack: An array of {Rule} objects.
-  # * line: The string being scanned.
-  # * rule: The rule that matched.
-  # * endPatternMatch: true if the rule's end pattern matched.
+  # match - An object returned from a previous call to `findNextMatch`.
+  # stack - An array of {Rule} objects.
+  # line - The string being scanned.
+  # rule - The rule that matched.
+  # endPatternMatch - true if the rule's end pattern matched.
   #
   # Returns an array of tokens representing the match.
   handleMatch: (match, stack, line, rule, endPatternMatch) ->


Return false from spy

Return false from spy

This prevents the event from bubbling.

For some time now, application:minimize has been the 6th element in the
array and so it was being invoked and causing the spec window to minimize.

--- spec/command-palette-spec.coffee
+++ spec/command-palette-spec.coffee
@@ -77,7 +77,7 @@

   describe "when an command selection is confirmed", ->
     it "detaches the palette, then focuses the previously focused element and emits the selected command on it", ->
-      eventHandler = jasmine.createSpy 'eventHandler'
+      eventHandler = jasmine.createSpy('eventHandler').andReturn(false)
       activeEditor = atom.workspaceView.getActiveView()
       {eventName} = palette.items[5]
       activeEditor.preempt eventName, eventHandler


debug

debug

--- spec/fast-activation-spec.coffee
+++ spec/fast-activation-spec.coffee
@@ -37,6 +37,7 @@
           oldPaths.forEach (p) ->
             savedCache[p] = require.cache[p]
             delete require.cache[p]
+          console.log getRequiredLibOrNodeModulePaths()

         waitsForPromise ->
           atom.packages.activatePackage('vim-mode-plus').then (_pack) ->
@@ -141,7 +142,7 @@
           newRegistriesLength = Object.keys(Base.getClassRegistry()).length
           expect(newRegistriesLength).toBeGreaterThan(oldRegistriesLength)

-    describe "make sure cmd-table is NOT out-of-date", ->
+    xdescribe "make sure cmd-table is NOT out-of-date", ->
       it "generateCommandTableByEagerLoad return table which is equals to initially loaded command table", ->
         withCleanActivation (pack) ->
           Base = pack.mainModule.provideVimModePlus().Base


:green_heart: Fix broken test using the wrong selector

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -856,7 +856,7 @@
           expect(quickSettingsElement.querySelectorAll('li').length).toEqual(4)

         it 'selects the first item of the list', ->
-          expect(quickSettingsElement.querySelector('li.active:first-child')).toExist()
+          expect(quickSettingsElement.querySelector('li.selected:first-child')).toExist()

         describe 'core:confirm', ->
           beforeEach ->
@@ -877,41 +877,39 @@
             atom.commands.dispatch quickSettingsElement, 'core:move-down'

           it 'selects the second item', ->
-            expect(quickSettingsElement.querySelector('li.active:nth-child(2)')).toExist()
+            expect(quickSettingsElement.querySelector('li.selected:nth-child(2)')).toExist()

           describe 'reaching a separator', ->
             beforeEach ->
               atom.commands.dispatch quickSettingsElement, 'core:move-down'
-              console.log Array::map.call quickSettingsElement.querySelectorAll('li'), (el) -> el.outerHTML

             it 'moves past the separator', ->
-              expect(quickSettingsElement.querySelector('li.active:last-child')).toExist()
+              expect(quickSettingsElement.querySelector('li.selected:last-child')).toExist()

           describe 'then core:move-up', ->
             beforeEach ->
               atom.commands.dispatch quickSettingsElement, 'core:move-up'

             it 'selects again the first item of the list', ->
-              expect(quickSettingsElement.querySelector('li.active:first-child')).toExist()
+              expect(quickSettingsElement.querySelector('li.selected:first-child')).toExist()

         describe 'core:move-up', ->
           beforeEach ->
             atom.commands.dispatch quickSettingsElement, 'core:move-up'
-            console.log Array::map.call quickSettingsElement.querySelectorAll('li'), (el) -> el.outerHTML

           it 'selects the last item', ->
-            expect(quickSettingsElement.querySelector('li.active:last-child')).toExist()
+            expect(quickSettingsElement.querySelector('li.selected:last-child')).toExist()

           describe 'reaching a separator', ->
             beforeEach ->
               atom.commands.dispatch quickSettingsElement, 'core:move-up'

             it 'moves past the separator', ->
-              expect(quickSettingsElement.querySelector('li.active:nth-child(2)')).toExist()
+              expect(quickSettingsElement.querySelector('li.selected:nth-child(2)')).toExist()

           describe 'then core:move-down', ->
             beforeEach ->
               atom.commands.dispatch quickSettingsElement, 'core:move-down'

             it 'selects again the first item of the list', ->
-              expect(quickSettingsElement.querySelector('li.active:first-child')).toExist()
+              expect(quickSettingsElement.querySelector('li.selected:first-child')).toExist()


fix(spec): fix package manager spec

fix(spec): fix package manager spec

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -130,6 +130,7 @@
             expect(pack.mainModule).toBe indexModule

       it "assigns config schema, including defaults when package contains a schema", ->
+        spyOn(NylasEnv.config, "_logError")
         expect(NylasEnv.config.get('package-with-config-schema.numbers.one')).toBeUndefined()

         waitsForPromise ->
@@ -140,6 +141,8 @@
           expect(NylasEnv.config.get('package-with-config-schema.numbers.two')).toBe 2

           expect(NylasEnv.config.set('package-with-config-schema.numbers.one', 'nope')).toBe false
+          expect(NylasEnv.config._logError).toHaveBeenCalled()
+          expect(NylasEnv.config._logError.callCount).toBe(1)
           expect(NylasEnv.config.set('package-with-config-schema.numbers.one', '10')).toBe true
           expect(NylasEnv.config.get('package-with-config-schema.numbers.one')).toBe 10


Reset selected itinerary on route change

Fixes issue #206

--- src/transit.coffee
+++ src/transit.coffee
@@ -180,6 +180,7 @@
                         return

                     data = otpCleanup data
+                    @set 'selected_itinerary', 0
                     @set 'plan', data.plan
                 error: =>
                     @clear()


adjust communication; console.log delete for troubleshooting

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -54,7 +54,7 @@
   robot.router.get "/#{robot.name}/#{modulename}/blacklist/url", (req, res) ->
     clientip = req.connection.remoteAddress || req.socket.remoteAddress ||
       req.connection.socket.remoteAddress
-    logmsg = "#{modulename}: web request from #{clientip}: url blacklist"
+    logmsg = "#{modulename}: web request from #{clientip}: get url blacklist"
     robot.logger.info logmsg

     arr = []
@@ -74,7 +74,7 @@
   robot.router.get "/#{robot.name}/#{modulename}/blacklist/cidr", (req, res) ->
     clientip = req.connection.remoteAddress || req.socket.remoteAddress ||
       req.connection.socket.remoteAddress
-    logmsg = "#{modulename}: web request from #{clientip}: cidr blacklist"
+    logmsg = "#{modulename}: web request from #{clientip}: get cidr blacklist"
     robot.logger.info logmsg

     arr = []
@@ -92,19 +92,20 @@

     return

-  robot.respond /fw(?: help|)$/, (msg) ->
+  robot.respond /fw(?: help| h|)$/, (msg) ->
     cmds = ['```']
     arr = [
-      modulename + " blacklist - show blacklist"
-      modulename + " blacklist add (url|cidr) <url|cidr> - add to blacklist"
-      modulename + " blacklist del (url|cidr) <url|cidr> - del from blacklist"
+      modulename + " blacklist [(cidr|url) [searchterm]] - show blacklist"
+      modulename + " blacklist add (url|cidr) <weburl.tld/etc|x.x.x.x> - add to blacklist"
+      modulename + " blacklist del (url|cidr) <weburl.tld/etc|x.x.x.x> - del from blacklist"
       modulename + " blacklist notify [username] - notify when changes happen"
       modulename + " blacklist subscribers - list notify subscribers"
     ]

     for str in arr
-      cmd = str.split " - "
-      cmds.push "#{cmd[0]} - #{cmd[1]}"
+      #cmd = str.split " - "
+      #cmds.push "#{cmd[0]} - #{cmd[1]}"
+      cmds.push str
     cmds.push '```'

     msg.reply cmds.join "\n"
@@ -116,7 +117,7 @@
     return

   robot.respond /fw (?:blacklist|b)(?: (cidr|url)(?: ([^ ]+)|)|)$/i, (msg) ->
-    logmsg = "#{modulename}: #{msg.envelope.user.name} requested: blacklist"
+    logmsg = "#{modulename}: #{msg.envelope.user.name} requested: show blacklist"
     robot.logger.info logmsg

     bl_type = false
@@ -215,28 +216,32 @@
       "blacklist delete #{bl_type} #{bl_search}"
     robot.logger.info logmsg

-    arr = []
-    newdata = []
-    arr.push sprintf displayfmt, 'Type', 'Value', 'Expiration', 'Creator'
+    deleted = []
+    new_bl = []
+    deleted.push sprintf displayfmt, 'Type', 'Value', 'Expiration', 'Creator'
     for obj in fwdata.blacklist
       expires = moment(obj.expires)
       if bl_type and bl_type != obj.type
-        newdata.push obj
+        console.log 'type: ['+ bl_type +'] != [' + obj.type +']'
+        new_bl.push obj
         continue
       if bl_search and obj.val.indexOf(bl_search) == -1
-        newdata.push obj
+        console.log 'search: indexOf['+ bl_search +'] not found in [' + obj.val +']'
+        new_bl.push obj
         continue
       if expires.isBefore() # now
-        newdata.push obj
-        continue
-      arr.push sprintf displayfmt, obj.type, obj.val, expires.fromNow(), obj.creator
-
-    deltaN = fwdata.blacklist.length - newdata.length
+        console.log 'expires: ['+ expires +'] is before now'
+        new_bl.push obj
+        continue
+      deleted.push sprintf displayfmt, obj.type, obj.val,
+        expires.fromNow(), obj.creator
+
+    deltaN = fwdata.blacklist.length - new_bl.length
     if deltaN > 0
       usermsg = "Removed `#{deltaN}` entries from firewall blacklist.  " +
         "Change will be applied in < 5 minutes. Removed: " +
-        "```"+ arr.join("\n") + "```"
-      fwdata.blacklist = newdata
+        "```"+ deleted.join("\n") + "```"
+      fwdata.blacklist = new_bl
       fs.writeFileSync blacklistfile, JSON.stringify(fwdata), 'utf-8'
     else
       usermsg = "Blacklist delete request did not match any records."


Hotfixing inject API

Hotfixing inject API

--- src/udefine.coffee
+++ src/udefine.coffee
@@ -73,8 +73,8 @@

   udefine.inject.modules = {}

-  udefine.inject.add = (name) ->
-    udefine.inject.modules[name] = undefined
+  udefine.inject.add = (name, value) ->
+    udefine.inject.modules[name] = value
     @

   udefine.inject.remove = (name) ->


Saving some bytes on module check + autoInjection + strict module name checking

--- src/udefine.coffee
+++ src/udefine.coffee
@@ -4,7 +4,7 @@
 do -> Array.isArray ?= (a) -> a.push is Array.prototype.push and a.length?

 # Module switch
-hasModule = module? and module.exports?
+hasModule = !!(module? and module.exports)
 exportObject = {}

 # Safe way to test against an object without array being a false positive
@@ -40,6 +40,9 @@
   udefine = (name, deps, factory) ->
     throw new Error 'A udefine module needs to have a name' unless name?

+    unless name is name.toLowerCase()
+      console.warn 'A module should be all lowercase'
+
     if typeof deps is 'function' or isObject(deps)
       [name, deps, factory] = [name, [], deps]

@@ -61,16 +64,18 @@
     # Inject result into defined namespace
     if Object.hasOwnProperty.call udefine.inject.modules, name
       injectObject = udefine.inject.modules[name]
+    else
+      if udefine.autoInject and udefine.env.globals
+        injectObject = {root, name}

-      {root: injectRoot, name: injectName, exportable} = injectObject
+      {root: injectRoot, name: injectName} = injectObject

       udefine.inject(injectRoot, injectName)(result)
-
-      if hasModule
-        if exportable or exportable is 'all' then module.exports = result
-        if exportable is 'partial' then exports[injectName] = result

     result
+
+  # Allow auto injection of modules in a browser environment
+  udefine.autoInject = true

   # Helper function to inject function/object into any object
   udefine.inject = (obj, name) -> (res) ->
@@ -156,7 +161,7 @@

   # Configuration helper function
   udefine.configure = (configFunc) ->
-    configFunc.apply udefine, [root]
+    configFunc.apply udefine, [if hasModule then module.exports else root]

   # Export udefine function on CommonJS environments
   if hasModule then module.exports = udefine else root.udefine = udefine


customizable loading scene

customizable loading scene

--- src/3d/extra/LoadingScene.coffee
+++ src/3d/extra/LoadingScene.coffee
@@ -1,5 +1,16 @@
 # A helper scene which loads [models, images, save objects], and calls a
 # function once loading is done
+#
+# Basic configuration of the loading scene can be done though LOADING_OPTIONS
+#
+# @todo - look into
+#
+# THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
+#   console.log( 'Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
+# };
+#
+# @example
+#   LoadingScene.LOADING_OPTIONS.fillStyle = 'red'
 #
 # @example
 #   gameScene = new GameScene()
@@ -7,6 +18,13 @@
 #     engine.sceneManager.setScene(gameScene)
 #   )
 class LoadingScene extends BaseScene
+
+  @LOADING_OPTIONS =
+    fillStyle: 'white'
+    font: '64px Arial'
+    align: 'center'
+    text: 'loading'
+    model: Helper.cube(color: 'white', size: 0.5)

   # You can either override the method hasFinishedLoading or you can
   # pass it as a param. It will be called once JsonModelManager has
@@ -51,18 +69,52 @@
         console.log "WARNING: #{url} is not a valid format"

     interval = setInterval =>
+      console.log(@getLoadedPercent())
       if @isLoadingDone()
         clearInterval(interval)
         console.ce 'Finished loading'
         @hasFinishedLoading()
-    , 100
+    , 50

   # Override this to add more code which should be executed before loading starts
   preStart: ->
+    engine = Hodler.item('engine')
+    engine.setClearColor(0x000000)
+
+    cam = Helper.camera()
+    cam.position.set 0, 0, 10
+    engine.setCamera(cam)
+
+    @scene.add Helper.ambientLight()
+    @scene.add Helper.ambientLight()
+    @scene.add Helper.light()
+
+    @loadingAnimation = LoadingScene.LOADING_OPTIONS.model
+    @loadingAnimation.position.set(0, -0.5, 0)
+    @scene.add @loadingAnimation
+
+    @text = new BaseText(LoadingScene.LOADING_OPTIONS)
+    @text.mesh.position.set 0, -3, 0
+    @scene.add @text.mesh

   # checks if the managers have finished loading
   isLoadingDone: ->
     @jmm.hasFinishedLoading() and @tm.hasFinishedLoading() and @som.hasFinishedLoading() and @sm.hasFinishedLoading()
+
+  # Returns the total amount of loaded assets
+  getLoadedCount: ->
+    @jmm.loadCount + @tm.loadCount + @som.loadCount + @sm.loadCount
+
+  # Returns the total amount of assets
+  getAllAssetsCount: ->
+    count = 0
+    for loader in [@jmm, @tm, @som, @sm]
+      count += Object.keys(loader.items).size()
+    count
+
+  # Returns the percentage of assets loaded
+  getLoadedPercent: (decimals = 2)->
+    Math.round(@getLoadedCount() / @getAllAssetsCount() * 100, decimals)

   # assumes the url has been validated as a json model
   _loadJsonModel: (url) ->
@@ -94,6 +146,9 @@

   # @see BaseScene.tick
   tick: (tpf) ->
+    if @loadingAnimation?
+      @loadingAnimation.rotation.x += tpf
+      @loadingAnimation.rotation.y += tpf

   # @nodoc
   doMouseEvent: (event, raycaster) ->


WIP: added some notes, this is the logic

This should in theory work :(

--- src/welcome.coffee
+++ src/welcome.coffee
@@ -19,13 +19,13 @@
     robot.brain.data.users ||= [ ]

   robot.enter (msg) ->
-    welcome = robot.brain.get('data.welcome')
-    stored_users = robot.brain.get('data.users')
-    users = robot.brain.usersForFuzzyName("#{msg.message.user.name}")
-    if ~stored_users.indexOf users
+    welcome = robot.brain.get('data.welcome') # pull out the welcome message
+    stored_users = robot.brain.get('data.users') # get a list of the known stored users
+    users = robot.brain.usersForFuzzyName("#{msg.message.user.name}") # get the user name of someone saying something
+    if users in stored_users # if the user above is in the stored_users do nothing
     else
-      msg.send "Welcome, #{msg.message.user.name}, #{welcome}"
-      robot.brain.set 'data.users', msg.message.user.name
+      msg.send "Welcome, #{msg.message.user.name}, #{welcome}" # if it's the first time you're seeing them give them the welcome message
+      robot.brain.set 'data.users', msg.message.user.name # add the user name to the stored_users

   robot.respond /welcome (.*)$/i, (msg) ->


project overview: remove some dumb debugging code and improve memory efficiency

--- salvus/bup_server.coffee
+++ salvus/bup_server.coffee
@@ -3213,7 +3213,6 @@
                     cb        : (err, r) =>
                         if err
                             cb(err); return
-                        console.log(r)
                         dbg("got #{r.length} projects; now processing")
                         if bad_servers?
                             for x in r
@@ -3230,7 +3229,10 @@
                                 return -1
                             else
                                 return 0
-                        projects = ({last_edited:misc.date_to_snapshot_format(x.last_edited), project_id:x.project_id, server_id:x.bup_location} for x in r)
+                        projects = r  # to save memory (also why we process in place below)
+                        for i in [0...projects.length]
+                            x = projects[i]
+                            projects[i] = {last_edited:misc.date_to_snapshot_format(x.last_edited), project_id:x.project_id, server_id:x.bup_location}
                         dbg("done processing projects (phase 1)")
                         cb()
             (cb) =>


wrote a first very rough and probably completely broken draft of code to move projects off server

--- salvus/bup_server.coffee
+++ salvus/bup_server.coffee
@@ -2073,31 +2073,154 @@
             P = @_project_cache[project_id] = new GlobalProject(project_id, @)
         return P

-    move_projects_off_server: (opts) =>
+    move_running_projects_off_server: (opts) =>
         opts = defaults opts,
             server_id : required     # uuid of a host
-            cb        : undefined
-
-        dbg = (m) -> winston.debug("move_projects_off_host: #{m}")
+            limit     : 1            # number at once...
+            cb        : undefined    # cb(err, number of projects moved)
+
+        dbg = (m) -> winston.debug("move_running_projects_off_server(#{opts.server_id}): #{m}")
         dbg()
         projects = undefined
         async.series([
             (cb) =>
+                dbg("determine projects currently running on server")
                 @projects_running_on_server
                     server_id : opts.server_id
                     cb        : (err, _projects) =>
                         projects = _projects
+                        dbg("there are #{projects.length} projects running on the server")
                         cb(err)
             (cb) =>
+                dbg("for each project, try to move it somewhere else")
+                i = 0
                 f = (project_id, cb) =>
+                    i += 1
+                    dbg("#{i}/#{projects.length}: moving project #{project_id}")
                     @get_project(project_id).move
                         cb : (err) =>
+                            dbg("done with #{project_id}; err=#{err}")
                             if err
-                                dbg("error moving #{project_id} -- ignored -- #{err}")
+                                dbg("ERROR moving #{project_id} -- #{err}")
+                            cb(err)
+                async.mapLimit(projects, opts.limit, f, cb)
+        ], (err) => opts.cb?(err, projects?.length))
+
+    decommission_server: (opts) =>
+        opts = defaults opts,
+            server_id : required
+            limit     : 1            # number of running projects to simultaneously move off server
+            cb        : undefined
+        dbg = (m) -> winston.debug("decommission_server(#{opts.server_id}): #{m}")
+        dbg()
+        projects = undefined
+        delete_bup_location = []
+        delete_bup_last_save = []
+
+        async.series([
+            (cb) =>
+                dbg("reduce chances of project startup on this server via the database")
+                @database.update
+                    table : 'storage_servers'
+                    set   :
+                        experimental : true
+                        health       : 0
+                    where :
+                        server_id : opts.server_id
+                    cb : cb
+            (cb) =>
+                dbg("move running projects off the server")
+                @move_running_projects_off_server
+                    server_id : opts.server_id
+                    limit     : opts.limit
+                    cb        : cb
+            (cb) =>
+                dbg("move running projects off the server AGAIN")
+                @move_running_projects_off_server
+                    server_id : opts.server_id
+                    limit     : opts.limit
+                    cb        : (err, number) =>
+                        if err
+                            cb(err)
+                        else
+                            if number > 0
+                                cb("after two attempts still couldn't move everything running off -- suspicious")
+                            else
                                 cb()
-                async.map(projects, f, cb)
-        ], (err) => opts.cb?(err))
-
+            (cb) =>
+                dbg("do a full repair, just in case -- this will ensure data is consistent")
+                @repair
+                    limit  : 10
+                    dryrun : false
+                    cb     : cb
+            (cb) =>
+                dbg("move running projects off the server a final time, just to be sure")
+                @move_running_projects_off_server
+                    server_id : opts.server_id
+                    limit     : opts.limit
+                    cb        : (err, number) =>
+                        if err
+                            cb(err)
+                        else
+                            if number > 0
+                                cb("after two attempts still couldn't move everything running off -- suspicious")
+                            else
+                                cb()
+            (cb) =>
+                dbg("load data from entire database about all projects")
+                # TODO: this must change to have some sort of index or something, since this
+                # doesn't scale.
+                @database.select
+                    table     : 'projects'
+                    columns   : ['project_id', 'bup_location', 'bup_last_save']
+                    objectify : true
+                    stream    : true
+                    cb        : (err, r) =>
+                        if err
+                            cb(err)
+                        else
+                            dbg("got #{r.length} projects from database")
+                            projects = r
+                            cb()
+            (cb) =>
+                dbg("for every project on this server, remove the references to it in the database")
+                # figure out what we plan to do
+                for project in projects
+                    if project.bup_location == opts.server_id
+                        delete_bup_location.push(project.project_id)
+                    if project.bup_last_save[opts.server_id]
+                        delete_bup_last_save.push(project.project_id)
+                dbg("delete #{delete_bup_locations.length} bup_locations, so projects last run on this server -- doing this in one very big query")
+                @database.delete
+                    table   : 'projects'
+                    columns : ['bup_location']
+                    where   : {project_id:{'in':delete_bup_locations}}
+                    cb      : cb
+            (cb) =>
+                dbg("delete #{delete_bup_last_save.length} bup_last_saves (10 at once), so projects with one replica on this server")
+                i = 0
+                f = (project_id, cb) =>
+                    i += 1
+                    dbg("#{i}/#{delete_bup_last_save.length}: deleting bup_last_save for project #{project_id}")
+                    @database.cql
+                        query : 'UPDATE projects SET bup_last_save[?]=null WHERE project_id=?'
+                        vals  : [opts.server_id, project_id]
+                        cb    : cb
+                async.mapLimit(delete_bup_last_save, 10, f, cb)
+            (cb) =>
+                dbg("remove entry in the storage_servers table corresponding to this server")
+                @database.delete
+                    table : 'storage_servers'
+                    where :
+                        server_id : opts.server_id
+                    cb : cb
+        ], (err) =>
+            if err
+                dbg("something bad happened -- #{err}")
+            else
+                dbg("YES!  We have successfully decommissioned the server with server_id #{server_id}.  You can turn it off and forget about it.")
+            opts.cb?(err)
+        )

     projects_running_on_server: (opts) =>
         opts = defaults opts,


Add explanation for qotd responder

Add explanation for qotd responder

--- scripts/nostalgia.coffee
+++ scripts/nostalgia.coffee
@@ -388,6 +388,19 @@
         qotd += "\"#{randomQuote}\" - #{randomName}"
         robot.send room: process.env.GENERAL_ROOM_ID, qotd

+qotdScheduleRespond = (res) ->
+    res.send """
+    #{process.env.QOTD_SCHEDULE.replace(/\ /g, '\t')}
+    ┬\t┬\t┬\t┬\t┬\t┬
+    |\t│\t│\t│\t│\t|
+    │\t│\t│\t│\t│\t└ day of week (0 - 7) (0 or 7 is Sun)
+    │\t│\t│\t│\t└─── month (1 - 12)
+    │\t│\t│\t└────── day of month (1 - 31)
+    │\t│\t└───────── hour (0 - 23)
+    │\t└──────────── minute (0 - 59)
+    └─────────────── second (0 - 59, OPTIONAL)
+    """
+
 module.exports = (robot) ->
     robot.respond /Remember +(?:that )?(.+) +said +"([^"]+)"/i, rememberPerson

@@ -425,4 +438,4 @@

     # Schedule a quote of the day
     schedule.scheduleJob process.env.QOTD_SCHEDULE, quoteOfTheDaySend(robot)
-    robot.respond /qotd/i, (res) -> res.send process.env.QOTD_SCHEDULE
+    robot.respond /qotd/i, qotdScheduleRespond


mod 2015.02.11

mod 2015.02.11

--- scripts/randompop.coffee
+++ scripts/randompop.coffee
@@ -5,7 +5,6 @@
 happyo = []

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

 module.exports = (robot) ->
   removeListener = (listener) ->
@@ -26,14 +25,12 @@
     robot.listeners.push listener

   robot.respond /発表者\s+一覧/i, (msg) ->
-    msg.send "発表者一覧"
+    msg.send '発表者一覧'
     for name, idx in happyo
-      msg.send "  (#{idx})#{name}"
+      msg.send '  (#{idx})#{name}'

   robot.respond /発表者\s+追加/i, (msg) ->
-    msg.reply "発表者を追加します。終了する場合は <end> と入力してください。"
+    msg.reply '発表者を追加します。終了する場合は <end> と入力してください。'
     waitForResponse msg, (msg) ->
-      # ここら辺に追加用のコードを書く予定
+      msg.reply '? '

-
-


hooking up react component to talk to flux store and action

--- src/components/plan/reading.cjsx
+++ src/components/plan/reading.cjsx
@@ -1,10 +1,9 @@
 React = require 'react'
 BS = require 'react-bootstrap'
 Router = require 'react-router'
-Widgets = require 'react-widgets'
-Datepicker = Widgets.DateTimePicker
+Datepicker = (require 'react-widgets').DateTimePicker

-{TaskPlanActions, TaskPlanStore} = require '../../flux/task-plan'
+{TaskPlanStore, TaskPlanActions} = require '../../flux/task-plan'

 SelectReadings = React.createClass
   mixins: [BS.OverlayMixin],
@@ -42,21 +41,27 @@

     </BS.Modal>

-Reading = React.createClass
+ReadingPlan = React.createClass
   mixins: [Router.State]
-  getInitialState: -> {title: '', state: 'draft', topics: [], due_at: null}
-  getDefaultProps: -> {title: '', state: 'draft', topics: [], due_at: null}
+
+  getInitialState: ->
+    {id: @getParams().id}
+
   componentWillMount: ->
     # Fetch the task if it has not been loaded yet
-    id = @getParams().id
-    # leave flux stuff to phil
-    # if ReadingStore.isUnknown(id)
-    #  ReadingActions.load(id)
+    TaskPlanStore.addChangeListener(@update)
+
+  componentWillUnmount: ->
+    TaskPlanStore.removeChangeListener(@update)
+
+  update: ->
+    @setState({})

   setDueAt: (value) ->
-    @setState({
-      due_at: value
-    })
+    TaskPlanActions.updateDueAt(@state.id, value)
+
+  setTitle: (value) ->
+    TaskPlanActions.updateTitle(@state.id, value)

   render: ->
     id = @getParams().id
@@ -68,7 +73,7 @@

{headerText}

         Name
-        <input id="title" type="text" value={@props.title}/>
+        <input id="title" type="text" onChange={@setTitle} value={@props.title}/>

         Due Date
@@ -87,4 +92,4 @@

     </BS.Panel>

-module.exports = Reading
+module.exports = ReadingPlan


simpler construction of vocabulary from its parts

--- src/vocabulary.coffee
+++ src/vocabulary.coffee
@@ -1,58 +1,16 @@
-# lazyRequires maps module names to the list of symbols we want to import from them
-lazyRequires =
-  arithmetic:    '+-×÷*⍟∣|'
-  floorceil:     '⌊⌈'
-  question:      '?'
-  exclamation:   '!'
-  circle:        '○'
-  comparisons:   '=≠<>≤≥≡≢'
-  logic:         '~∨∧⍱⍲'
-  rho:           '⍴'
-  iota:          '⍳'
-  rotate:        '⌽⊖'
-  transpose:     '⍉'
-  epsilon:       '∊'
-  zilde:         ['get_⍬', 'set_⍬']
-  comma:         ',⍪'
-  grade:         '⍋⍒'
-  take:          '↑'
-  drop:          '↓'
-  squish:        ['⌷', '_index']
-  quad:          ['get_⎕', 'set_⎕', 'get_⍞', 'set_⍞']
-  format:        '⍕'
-  forkhook:      ['_fork', '_hook']
-  each:          '¨'
-  commute:       '⍨'
-  cupcap:        '∪∩'
-  find:          '⍷'
-  enclose:       '⊂'
-  disclose:      '⊃'
-  execute:       '⍎'
-  poweroperator: '⍣'
-  innerproduct:  '.'
-  outerproduct:  ['∘.']
-  slash:         '/⌿'
-  backslash:     '\\⍀'
-  tack:          '⊣⊢'
-  encode:        '⊤'
-  decode:        '⊥'
-  special:       ['_aplify', '_complex', '_bool', 'get_⎕IO', 'set_⎕IO']
+moduleNames = '''
+  arithmetic backslash circle comma commute comparisons cupcap decode disclose
+  drop each enclose encode epsilon exclamation execute find floorceil forkhook
+  format grade innerproduct iota logic outerproduct poweroperator quad question
+  rho rotate slash special squish tack take transpose vhelpers zilde
+'''.split /\s+/

-# With a bit of metaprogramming, the call to require() can be delayed as much as possible
-createLazyRequire = (obj, name, fromModule) ->
-  obj[name] = (args...) ->
-    obj[name] = f = require(fromModule)[name]
-    f.aplName = name
-    f.aplMetaInfo = arguments.callee.aplMetaInfo
-    f args...
-
-for fromModule, names of lazyRequires
-  for name in names
-    createLazyRequire @, name, './vocabulary/' + fromModule
+for moduleName in moduleNames
+  for k, v of require "./vocabulary/#{moduleName}"
+    @[k] = v
+    if typeof v is 'function' then v.aplName = k

 # Some symbols can act as adverbs or conjunctions.  They need to be marked as such.
-for name in ['∘.']    then (@[name].aplMetaInfo ?= {}).isPrefixAdverb = true
+for name in ['∘.']    then (@[name].aplMetaInfo ?= {}).isPrefixAdverb  = true
 for name in '⍨¨/⌿\\⍀' then (@[name].aplMetaInfo ?= {}).isPostfixAdverb = true
-for name in '.⍣'      then (@[name].aplMetaInfo ?= {}).isConjunction = true
-
-for k, v of @ when typeof v is 'function' then v.aplName = k
+for name in '.⍣'      then (@[name].aplMetaInfo ?= {}).isConjunction   = true


Added clear method to clean the storage

Added clear method to clean the storage

--- src/device.module.storage.coffee
+++ src/device.module.storage.coffee
@@ -36,7 +36,9 @@

   _handler = (storage, key, value) ->
     storage = window[storage]
-    if value
+    if not key
+      _clear(storage)
+    else if value
       _saveKey storage, key, value
     else if value is null
       _removeKey storage, key
@@ -47,9 +49,9 @@

   _removeKey = (storage, key) -> storage.removeItem key

+  _clear = (storage) -> storage.clear()
+
   _getKey = (storage, key) -> JSON.parse storage.getItem(key)

   local: local
   session: session
-
-


refactoring glyph details processing

refactoring glyph details processing


simplify `$document()` as we know require explicit tag


remove some dead code and some debugging statements

--- src/types/json.coffee
+++ src/types/json.coffee
@@ -14,7 +14,6 @@
 # {p:[1,2,3], m:[1,2,4]}. This method expands relative paths
 # Makes sure a path is a list.
 normalizeMovePath = (path, newPath) ->
-

 invertComponent = (c) ->
 	c_ = { p: c['p'] }
@@ -182,21 +181,8 @@
 			return i-1
 	return

-transformPosByMove = (pos, from, to, bumpIfEqual) ->
-	if pos != from
-		pos-- if pos > from
-		pos++ if pos > to or (pos == to and bumpIfEqual)
-	pos
-
+# transform c so it applies to a document with otherC applied.
 transformComponent = (dest, c, otherC, type) ->
-	j = JSON.stringify
-	console.log 'transformComponenting',j(c),'against',j(otherC),'type:',type
-	res = transformComponent_ dest, c, otherC, type
-	console.log 'got',j dest
-	res
-
-# transform c so it applies to a document with otherC applied.
-transformComponent_ = (dest, c, otherC, type) ->
 	c = clone c
 	c['p'].push(0) if c['na'] != undefined
 	otherC['p'].push(0) if otherC['na'] != undefined


Updated Elyssa.Rect to use the new serialization functions

--- src/types/rect.coffee
+++ src/types/rect.coffee
@@ -1,11 +1,17 @@
 do (window = @, Elyssa = @Elyssa or= {}) ->

   class Elyssa.Rect
-    constructor: ({@x, @y, @w, @h} = {x: 0, y: 0, w: 0, h: 0}) ->
-      @x = 0 unless @x?
-      @y = 0 unless @y?
-      @w = 0 unless @w?
-      @h = 0 unless @h?
+    defaultValue =
+      x: 0
+      y: 0
+      w: 0
+      h: 0
+
+    constructor: ({@x, @y, @w, @h} = {x: defaultValue.x, y: defaultValue.y, w: defaultValue.w, h: defaultValue.h}) ->
+      @x = defaultValue.x unless @x?
+      @y = defaultValue.y unless @y?
+      @w = defaultValue.w unless @w?
+      @h = defaultValue.h unless @h?

     contains: ({x, y, w, h}) ->
       if w? and h?
@@ -27,5 +33,6 @@

     toVector: -> new Elyssa.Vector {@x, @y, z: 0}

-    toString: -> JSON.stringify {@x, @y, @w, @h}
-      +    toString: -> Elyssa.serialize {@x, @y, @w, @h}, defaultValue
+
+    @fromString: (rectString) -> Elyssa.deserialize rectString, 'Rect'


Include uoms with load

Include uoms with load

--- client/skr/screens/sku-maint/SkuMaint.cjsx
+++ client/skr/screens/sku-maint/SkuMaint.cjsx
@@ -6,7 +6,7 @@
         query: ->
             new Lanes.Models.Query({
                 syncOptions:
-                    include: ['default_vendor']
+                    include: ['default_vendor', 'uoms']
                 src: Skr.Models.Sku, fields: [
                     {id:'id', visible: false}
                     'code'


Prevent errors

Prevent errors

--- client/views/app/sideNav/userStatus.coffee
+++ client/views/app/sideNav/userStatus.coffee
@@ -1,7 +1,7 @@
 Template.userStatus.helpers
 	myUserInfo: ->
 		visualStatus = "online"
-		username = Meteor.user().username
+		username = Meteor.user()?.username
 		switch Session.get('user_' + username + '_status')
 			when "away"
 				visualStatus = t("userStatus.away")


Change 'mock = require("TinyMockJS").mock' to 'mock = require("TinyMockJS")'

--- tutorial/test/Logger.test.coffee
+++ tutorial/test/Logger.test.coffee
@@ -1,6 +1,6 @@
 chai   = require("chai")
 should = chai.should()
-mock   = require("TinyMockJS").mock
+mock   = require("TinyMockJS")
 Logger = require("../src/Logger").Logger
 fs     = require("fs")


unofficial

unofficial

--- parse.coffee
+++ parse.coffee
@@ -165,7 +165,7 @@

 parseD3Functions = (code) ->
-  # we match d3.foo.bar( which will find plugins and unnoficial api functions
+  # we match d3.foo.bar( which will find plugins and unofficial api functions
   re = new RegExp(/d3\.[a-zA-Z0-9\.]*?\(/g)
   matches = code.match(re) or []
   return matches