implement EPIPE protection

implement EPIPE protection

--- src/ReplrServer.coffee
+++ src/ReplrServer.coffee
@@ -100,6 +100,11 @@
     # Track client and remove when disconnect occurs
     client = new ReplrClient(@, @options, socket, r)
     @clients.push client
+    socket.on 'error', ()=>
+      if err && err.code == 'EPIPE'
+        return
+
+      throw err
     socket.on 'end', ()=>
       @clients.splice @clients.indexOf(client), 1


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/point.coffee
+++ test/point.coffee
@@ -140,7 +140,7 @@
     ok eval(p.toString()).equal(p)

   test "#clamp", ->
-    p
+    p = Point()

   test ".centroid", ->
     centroid = Point.centroid(


Assert Image#getMeta()

Assert Image#getMeta()

--- test/suite.coffee
+++ test/suite.coffee
@@ -3,8 +3,6 @@

 hash = require('crypto').createHash
 rand = require('crypto').pseudoRandomBytes
-
-S3 = require('aws-sdk').S3

 client = listObjects = putObject = null
 cleanup = []
@@ -62,7 +60,7 @@
     it 'should set default values if not provided', ->
       client = new Upload 'myBucket'

-      assert client.s3 instanceof S3
+      assert client.s3 instanceof require('aws-sdk').S3
       assert client.versions instanceof Array
       assert.equal client.awsBucketPath, ''
       assert.equal client.awsBucketUrl, undefined
@@ -114,7 +112,7 @@
       image = null

       beforeEach ->
-        src = __dirname + 'test/assets/photo.jpg'
+        src = __dirname + '/assets/photo.jpg'
         dest = 'images_test/Wm/PH/f3/I0'
         opts = {}

@@ -124,17 +122,54 @@
         it 'should set default values', ->
           assert image instanceof Upload.Image
           assert image.config instanceof Upload
-          assert.equal image.src, __dirname + 'test/assets/photo.jpg'
+          assert.equal image.src, __dirname + '/assets/photo.jpg'
           assert.equal image.dest, 'images_test/Wm/PH/f3/I0'
           assert /[a-z0-9]{24}/.test image.tmpName
           assert.deepEqual image.meta, {}
           assert.equal image.gm, null

       describe '#getMeta()', ->
-        it 'should store image matadata'
-        it 'should store gm image instance'
-        it 'should return image metadata'
-        it 'should return exif data if returnExif is set to true'
+        it 'should return image metadata', (done) ->
+          image.getMeta (err, meta) ->
+            assert.ifError err
+            assert.equal meta.format, 'jpeg'
+            assert.equal meta.fileSize, '617KBB'
+            assert.equal meta.imageSize.width, 1536
+            assert.equal meta.imageSize.height, 2048
+            assert.equal meta.orientation, 'Undefined'
+            assert.equal meta.colorSpace, 'RGB'
+            assert.equal meta.compression, 'JPEG'
+            assert.equal meta.quallity, '96'
+            assert.equal meta.exif, undefined
+            done()
+
+        it 'should store image matadata', (done) ->
+          image.getMeta (err) ->
+            assert.ifError err
+            assert.equal image.meta.format, 'jpeg'
+            assert.equal image.meta.fileSize, '617KBB'
+            assert.equal image.meta.imageSize.width, 1536
+            assert.equal image.meta.imageSize.height, 2048
+            assert.equal image.meta.orientation, 'Undefined'
+            assert.equal image.meta.colorSpace, 'RGB'
+            assert.equal image.meta.compression, 'JPEG'
+            assert.equal image.meta.quallity, '96'
+            assert.equal image.meta.exif, undefined
+            done()
+
+
+        it 'should return exif data if returnExif is set to true', (done) ->
+          image.config.returnExif = true
+          image.getMeta (err) ->
+            assert.ifError err
+            assert.equal typeof image.meta.exif, 'object'
+            done()
+
+        it 'should store gm image instance', (done) ->
+          image.getMeta (err) ->
+            assert.ifError err
+            assert image.gm instanceof require('gm')
+            done()

       describe '#resize()', ->
         it 'should return updated properties for original image'


Allow disabling server-logs standalone server

--- src/listeners/wsServer.coffee
+++ src/listeners/wsServer.coffee
@@ -28,13 +28,14 @@
   {port, mainStory} = config

   # Launch stand-alone log server
-  expressApp = express()
-  expressApp.use express.static path.join(__dirname, '../../serverLogsApp')
-  httpServer = http.createServer expressApp
-  _ioStandalone = socketio(httpServer).of k.WS_NAMESPACE
-  _ioStandalone.on 'connection', (socket) -> _socketOnConnection socket, config
-  httpServer.listen port
-  mainStory.info LOG_SRC, "Server logs available on port #{chalk.cyan port}"
+  if port?
+    expressApp = express()
+    expressApp.use express.static path.join(__dirname, '../../serverLogsApp')
+    httpServer = http.createServer expressApp
+    _ioStandalone = socketio(httpServer).of k.WS_NAMESPACE
+    _ioStandalone.on 'connection', (socket) -> _socketOnConnection socket, config
+    httpServer.listen port
+    mainStory.info LOG_SRC, "Server logs available on port #{chalk.cyan port}"

   # If a main application server is also provided,
   # launch another log server on the same application port
@@ -46,9 +47,9 @@
     _ioServerAdaptor.on 'connection', (socket) -> _socketOnConnection socket, config
     try
       port2 = _ioServerAdaptor.server.httpServer.address().port
-      mainStory.info LOG_SRC, "Server logs also available through main HTTP server on port #{chalk.cyan port2}"
+      mainStory.info LOG_SRC, "Server logs available through main HTTP server on port #{chalk.cyan port2}"
     catch
-      mainStory.info LOG_SRC, "Server logs also available through main HTTP server (#{chalk.red 'port could not be determined'})"
+      mainStory.info LOG_SRC, "Server logs available through main HTTP server (#{chalk.red 'port could not be determined'})"
   return

 _socketOnConnection = (socket, config) ->


NR.API should be sending parameters in the body of the request on non-GET requests.

--- src/main/coffee/NR.API.coffee
+++ src/main/coffee/NR.API.coffee
@@ -68,10 +68,12 @@
                     queryString = queryString + encodeURIComponent(k.toString()) + "=" + encodeURIComponent(i.toString()) + "&"
             else
                 queryString = queryString + encodeURIComponent(k.toString()) + "=" + encodeURIComponent(v.toString()) + "&"
-        if url.indexOf("?") >= 0
-            url = url + "&" + queryString
-        else
-            url = url + "?" + queryString
+
+        if method == "GET"
+            if url.indexOf("?") >= 0
+                url = url + "&" + queryString
+            else
+                url = url + "?" + queryString

         this._ajax.addEventListener 'readystatechange', =>
             if this._ajax.readyState is 4 # complete
@@ -112,7 +114,11 @@
             document.dispatchEvent event

         this._ajax.open method, NR.API._rootURL + url, true
-        this._ajax.send()
+        if method != "GET"
+            this._ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
+            this._ajax.send(queryString)
+        else
+            this._ajax.send()

     #############################################################################
     # Aborts the request.


update bokehjs client test

update bokehjs client test

--- bokehjs/test/test_common/test_client.coffee
+++ bokehjs/test/test_common/test_client.coffee
@@ -54,7 +54,7 @@
     pypath = basedir
   port = next_port()
   env = _.extend({}, process.env, { PYTHONPATH: pypath })
-  handle = child_process.spawn("python", ["-c", "import bokeh.command as command; command.main(['bokeh', 'serve', '--port=#{port}'])"], {
+  handle = child_process.spawn("python", ["-m", "bokeh.command", "serve", "--port=#{port}"], {
     env: env,
     cwd: basedir
   })


Use absolute keymap/menu paths

Use absolute keymap/menu paths

--- src/package.coffee
+++ src/package.coffee
@@ -192,13 +192,13 @@

   loadKeymaps: ->
     if @bundledPackage and packagesCache[@name]?
-      @keymaps = ([keymapPath, keymapObject] for keymapPath, keymapObject of packagesCache[@name].keymaps)
+      @keymaps = (["#{@path}#{path.sep}#{keymapPath}", keymapObject] for keymapPath, keymapObject of packagesCache[@name].keymaps)
     else
       @keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath)]

   loadMenus: ->
     if @bundledPackage and packagesCache[@name]?
-      @menus = ([menuPath, menuObject] for menuPath, menuObject of packagesCache[@name].menus)
+      @menus = (["#{@path}#{path.sep}#{menuPath}", menuObject] for menuPath, menuObject of packagesCache[@name].menus)
     else
       @menus = @getMenuPaths().map (menuPath) -> [menuPath, CSON.readFileSync(menuPath)]


match an optional trailing slash

match an optional trailing slash

--- lib/assets/javascripts/stark/route.js.coffee
+++ lib/assets/javascripts/stark/route.js.coffee
@@ -49,7 +49,7 @@
     pattern = pattern.replace /:(\w+)/g, @addParamName

     # Create the actual regular expression
-    @regExp = new RegExp '^' + pattern + '(?=\\?|$)' # End or begin of query string
+    @regExp = new RegExp '^' + pattern + '\/?(?=\\?|$)' # End or begin of query string

   addParamName: (match, paramName) =>
     # Test if parameter name is reserved


Added globals property to udefine.env

Added globals property to udefine.env

--- src/udefine.coffee
+++ src/udefine.coffee
@@ -128,6 +128,7 @@
     amd: do -> define? and (define.amd or define.umd)
     commonjs: hasModule
     browser: not hasModule
+    globals: not hasModule and not udefine.amd

   # Paths
   udefine.paths =


fixed calibration

fixed calibration

--- src/main/app/index/FSPreviewController.coffee
+++ src/main/app/index/FSPreviewController.coffee
@@ -49,6 +49,7 @@
         if data['message'] == 'SCANNING_TEXTURE'
           $scope.streamUrl = Configuration.installation.httpurl+'stream/texture.mjpeg'
           $scope.showStream = true
+          $scope.$apply()

         if data['message'] == 'START_CALIBRATION'
           $scope.streamUrl = Configuration.installation.httpurl+'stream/texture.mjpeg'
@@ -61,6 +62,7 @@
         if data['message'] == 'SCANNING_OBJECT'
           $scope.showStream = false
           $scope.streamUrl = ""
+          $scope.$apply()

         if data['message'] == 'SCAN_COMPLETE'
           FSScanService.setScanId(data['scan_id'])
@@ -108,7 +110,7 @@

               $scope.sampledRemainingTime = parseFloat(Math.floor(median(_time_values)))

-              if $scope.sampledRemainingTime >= 60
+              if $scope.sampledRemainingTime > 60
                   $scope.remainingTimeString =  parseInt($scope.sampledRemainingTime/60)+" minutes"
               else
                   $scope.remainingTimeString = ($scope.sampledRemainingTime)+" seconds"


Fixed example.

Fixed example.

--- example/client/base.coffee
+++ example/client/base.coffee
@@ -68,14 +68,20 @@
       duration: 500
     )

+    [parent, node, before, true]
+
   moveDOMElement: (parent, node, before) ->
     @insertDOMElement parent, node, before

-  removeDOMElement: (node) ->
+    [parent, node, before, true]
+
+  removeDOMElement: (parent, node) ->
     $(node).velocity('transition.slideRightOut',
       duration: 500,
       complete: =>
         $(node).remove()
     )

+    [parent, node, true]
+
 BlazeComponent.register 'AnimatedListComponent', AnimatedListComponent


activity: define `ActivityAppController::edit` method

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -24,6 +24,14 @@
     {socialapi} = KD.singletons

     socialapi.message.post {body}, callback
+
+
+  edit: (options = {}, callback = noop) ->
+
+    {id, body} = options
+    {socialapi} = KD.singletons
+
+    socialapi.message.edit {id, body}, callback

   reply: ({activity, body}, callback = noop) ->


Cleaner

Cleaner

--- client/landing/app/MainApp/fs/fsitem.coffee
+++ client/landing/app/MainApp/fs/fsitem.coffee
@@ -13,18 +13,14 @@
         callback? err, response
         warn err
       else
-        options = switch type
-          when 'file'
-            method           : "fs.writeFile"
-            withArgs         :
-              path           : response
-              content        : ""
-              donotoverwrite : yes
-          when 'folder'
-            method           : "fs.createDirectory"
-            withArgs         :
-              path           : response
-        KD.getSingleton('kiteController').run options, (err, res)->
+        KD.getSingleton('kiteController').run
+          method           : if type is 'folder' then "fs.createDirectory" \
+                             else "fs.writeFile"
+          withArgs         :
+            path           : response
+            content        : ""
+            donotoverwrite : yes
+        , (err, res)->
           if err then warn err
           else
             file = FSHelper.createFileFromPath response, type


Delete job.coffee

Delete job.coffee

--- jobs/vm-get/job.coffee
+++ jobs/vm-get/job.coffee
@@ -1,58 +0,0 @@
-http = require 'http'
-debug = require('debug')('meshblu-connector-xenserver:VmStart')
-
-class VmStart
-  constructor: ({@connector}) ->
-    throw new Error 'VmStart requires connector' unless @connector?
-
-  do: ({data}, callback) =>
-    return callback @_userError(422, 'data.name is required') unless data?.name?
-
-    {name} = data
-
-    metadata =
-      code: 200
-      status: http.STATUS_CODES[200]
-
-    @connector.getXapi().then((xapi) =>
-      xapi.call('VM.get_by_name_label', name).then((vmref) =>
-
-        if !vmref || vmref.length == 0
-          message = "Cannot find VM " + name
-          status = "error"
-          data = {message, status}
-          callback null, {metadata, data}
-          return
-
-        xapi.call('VM.start', vmref[0], false, false).then((response) =>
-
-          message = "Started VM " + name
-          status = "ok"
-          data = {message, status}
-
-          debug "Success " + data
-          callback null, {metadata, data}
-
-        ).catch((error) =>
-          message = error.toString()
-          status = "error"
-          data = {message, status}
-
-          debug "Error " + data
-          callback null, {metadata, data}
-        )
-      )
-    ).catch((error) =>
-      message = "XenServer connection not available for VM " + name
-      status = "error"
-      data = {message, status}
-      callback null, {metadata, data}
-      return
-    )
-
-  _userError: (code, message) =>
-    error = new Error message
-    error.code = code
-    return error
-
-module.exports = VmStart


Add wrong flatMapLatest usage

Add wrong flatMapLatest usage

--- example_observables.coffee
+++ example_observables.coffee
@@ -64,3 +64,19 @@
           ]
       }
     ]
+  "wrong flatMapLatest":
+    description: "Using flatMapLatest is not correct when you want all the mapped results, in this case flatMap is preferred."
+    root:
+      type: 'of', args: "'question1', 'question2', 'question3'"
+    operators: [
+      {
+        type: 'flatMapLatest'
+        args: 'function(question) { return '
+        observable:
+          root:
+            type: 'timer', args: "1000"
+          operators: [
+            { type: 'map', args: 'function(x) { return "answer" + question[question.length - 1] }' }
+          ]
+      }
+    ]


Fix blacklist

Fix blacklist

--- modules/admin/blacklist.coffee
+++ modules/admin/blacklist.coffee
@@ -20,7 +20,7 @@
   remove: (user)=>
     u = user
     u = user.id if user.id
-    if u in @doc.users
+    if u in Core.settings.blacklist
       Core.settings.blacklist.splice(Core.settings.blacklist.indexOf(u), 1)
       # Save
       await Core.data.set('Blacklist', Core.settings.blacklist)


Work on RequireJS Jasmine test.

Work on RequireJS Jasmine test.

--- lms/static/coffee/spec/requirejs_spec.coffee
+++ lms/static/coffee/spec/requirejs_spec.coffee
@@ -1,3 +1,12 @@
 describe "RequireJS", ->
-  it "check that the RequireJS object is present in the global namespace", ->
-    expect(true).toBe true
+    it "check that the RequireJS object is present in the global namespace", ->
+        expect RequireJS.toEqual(jasmine.any(Object))
+        expect window.RequireJS.toEqual(jasmine.any(Object))
+
+    it "check that requirejs(), require(), and define() are not in the global namespace", ->
+        expect(requirejs).not.toBeDefined()
+        expect(require).not.toBeDefined()
+        expect(define).not.toBeDefined()
+        expect(window.requirejs).not.toBeDefined()
+        expect(window.require).not.toBeDefined()
+        expect(window.define).not.toBeDefined()


implemented udp transport

implemented udp transport

--- docker-log-central.coffee
+++ docker-log-central.coffee
@@ -22,24 +22,31 @@
 domain = require('domain')
 fs     = require('fs')
 net    = require('net')
-
+dgram  = require('dgram')
+
+syslog_host  = process.env.SYSLOG_HOST  || '127.0.0.1'
 syslog_port  = process.env.SYSLOG_PORT  || '514'
-syslog_host  = process.env.SYSLOG_HOST  || '127.0.0.1'
 syslog_proto = process.env.SYSLOG_PROTO || 'tcp'

+# Init syslog connection depending on the protocol
+initiateSyslog = (proto, host, port) ->
+  if (proto == 'tcp')
+    connection = net.createConnection syslog_port, syslog_host
+    connection.on 'connect', () ->
+    console.log((".. Opened connection to #{syslog_host}:#{syslog_port}/#{syslog_proto}").green)
+    connection.on 'error', (err) ->
+      console.log(("xx Error connecting to #{syslog_host}:#{syslog_port}/#{syslog_proto}").yellow)
+      delay 1000, -> connection.connect syslog_port, syslog_host
+    connection.on 'end', (data) ->
+      console.log(('Server lost ... Retrying...').yellow)
+      delay 1000, -> connection.connect syslog_port, syslog_host
+    return connection
+  else if (proto == 'udp')
+    connection = dgram.createSocket('udp4')
+    console.log((".. Socket Ready to #{syslog_host}:#{syslog_port}/#{syslog_proto}").green)
+    return connection
 #Initialize Outgoing log connection
-connection = net.createConnection syslog_port, syslog_host
-connection.on 'connect', () ->
-	console.log "Opened connection to #{syslog_host}:#{syslog_port}/#{syslog_proto}"
-
-connection.on 'error', (err) ->
-	console.log "Error connecting to #{syslog_host}:#{syslog_port}/#{syslog_proto}"
-	delay 1000, -> connection.connect syslog_port, syslog_host
-
-connection.on 'end', (data) ->
-	console.log('Server lost ... Retrying...')
-	delay 1000, -> connection.connect syslog_port, syslog_host
-
+connection = initiateSyslog(syslog_proto, syslog_host, syslog_port)

 #Docker logging Mgmt
 console.log('.. initializing docker-log-central (hit ^C to quit)')
@@ -92,6 +99,9 @@
 ######################
 #Functions Definition
 ######################
+
+
+
 #Format syslog TimeStamp
 formatSyslogDate = (unixtime) ->
   months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
@@ -106,11 +116,17 @@
 	socket.write msg

 #used to send syslog message
-send_syslog = (socket, date, container, msg) ->
+send_syslog = (socket, host, port, proto, date, container, msg) ->
   criticity = 5
   facility = 1
-  socket.write('<' + criticity + facility + '>' + date + ' ' + container.id.substr(0,12) + ' dockerlogger: ' + msg)
-
+  message = '<' + criticity + facility + '>' + date + ' ' + container.id.substr(0,12) + ' dockerlogger: ' + msg
+  if (proto == 'tcp')
+    socket.write(message)
+  else if (proto == 'udp')
+    console.log('Sending UDP log')
+    msg = new Buffer(message)
+    socket.send(msg, 0, message.length, port, host)
+
 #Attach to the container to get its logs
 attach = (container) ->
     domain.create().on('error', (err) ->
@@ -169,7 +185,7 @@
                             }
 			    # Do something with the log ;)
 #                            send_log(connection, JSON.stringify(message))
-                            send_syslog(connection, formatSyslogDate(Date.now()), container, frame.content)
+                            send_syslog(connection, syslog_host, syslog_port, syslog_proto, formatSyslogDate(Date.now()), container, frame.content)
                         )
                     catch e
                         console.log 'could not parse packet: '.red + e.message


fixing tokenisation of "<" and ">"

fixing tokenisation of "<" and ">"

--- fizzylogo_tokenizer.coffee
+++ fizzylogo_tokenizer.coffee
@@ -54,7 +54,7 @@
   # see this playground with an example:
   # https://regex101.com/r/LRqxeN/3
   #
-  command = command.replace /([0-9]*\.[0-9]+([eE][- ]?[0-9]*)?)|([^+\-^*/()\[\]=←.!%])([+\-^*/()\[\]=←.!%]+)/g, "$1$3 $4 "
+  command = command.replace /([0-9]*\.[0-9]+([eE][- ]?[0-9]*)?)|([^+\-^*/<>()\[\]=←.!%])([+\-^*/<>()\[\]=←.!%]+)/g, "$1$3 $4 "

   # separates the *end* of a sequence of punctuations from what
@@ -62,12 +62,12 @@
   # not that we can't/don't need to do that with the dot because
   # a) it was already done by the regex above and
   # b) it would wreck floating point numbers
-  command = command.replace /([+\-^*/()\[\]=←!%])([^+\-^*/()\[\]=←!%])/g, "$1 $2"
+  command = command.replace /([+\-^*/<>()\[\]=←!%])([^+\-^*/<>()\[\]=←!%])/g, "$1 $2"

   # things that are now a / _2 become a /_ 2
   # this is so we can have an "underscore" version of
   # an operator, in this case floor division
-  command = command.replace /([+\-^*/=←!%_])[ ]+_/g, "$1_ "
+  command = command.replace /([+\-^*/<>=←!%_])[ ]+_/g, "$1_ "

   command = command.replace /'/g, " ' "
   command = command.replace /:/g, " : "


Scale max width based on zoom level

Scale max width based on zoom level

--- app/lib/mark.coffee
+++ app/lib/mark.coffee
@@ -36,7 +36,7 @@

   minWidth: -> 10

-  maxWidth: -> 200
+  maxWidth: -> 200 * @canvasGraph.scale

   handleWidth: -> 16 * (@canvasGraph.scale || 1)


More robust errors when selecting a variable with spaces to trace

--- lib/atom-tracer.coffee
+++ lib/atom-tracer.coffee
@@ -131,7 +131,7 @@

     parseData = @parseScripts[fileType]
     #If found, please call the parse script to check if it's a real variable
-    command = parseData.runCommand.replace("[file]",parseData.parseScript) + " \"" + filePath + "\" " + varName + " " + line
+    command = parseData.runCommand.replace("[file]",parseData.parseScript) + " \"" + filePath + "\" \"" + varName + "\" " + line
     child_process.exec(command, (error, stdout, stderr) ->
       #If all good, send the scope info the inject
       if(error || stderr)


First show LazyLoader then emit LazyLoadThresholdReached, hide logs

--- components/list/KDListViewController.coffee
+++ components/list/KDListViewController.coffee
@@ -31,9 +31,9 @@
     listView.on 'ItemWasAdded', (view, index)=> @registerItem view, index
     listView.on 'ItemIsBeingDestroyed', (itemInfo)=> @unregisterItem itemInfo
     if options.keyNav
-      log "hev hev", @
+      # log "hev hev", @
       listView.on 'KeyDownOnList', (event)=>
-        log "alo"
+        # log "alo"
         @keyDownPerformed listView, event

   loadView:(mainView)->
@@ -302,7 +302,6 @@
   showLazyLoader:->

     unless @lazyLoader
-      @propagateEvent KDEventType : 'LazyLoadThresholdReached'
       @scrollView.addSubView @lazyLoader = new KDCustomHTMLView cssClass : "lazy-loader", partial : "Loading..."
       @lazyLoader.addSubView @lazyLoader.canvas = new KDLoaderView
         size          :
@@ -316,9 +315,9 @@
           FPS         : 24

       @lazyLoader.canvas.show()
+      @propagateEvent KDEventType : 'LazyLoadThresholdReached'

   hideLazyLoader:->
-
     if @lazyLoader
       @lazyLoader.canvas.hide()
       @lazyLoader.destroy()


Start hooking up Google Books

Start hooking up Google Books

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -84,6 +84,8 @@
     success: (data) ->
       console.log data
       $("##{html_id(ia_id)}").append($('<span/>').text(" - #{data.metadata.title}, #{data.metadata.year}, v.#{data.metadata.volume}"))
+      if data.metadata.source? && data.metadata.source.match(GB_REGEX)
+        process_gb(data.metadata.source)

 process_ia = (identifier_string) ->
   console.log 'process_ia'
@@ -109,7 +111,16 @@
 process_gb = (identifier_string) ->
   console.log 'process_gb'
   match = identifier_string.match(GB_REGEX)
-  console.log match[1]
+  gb_id = match[1].split('&')[0]
+  process_gb_id(gb_id)
+
+gb_url = (gb_id) ->
+  "https://books.google.com/books?id=#{gb_id}"
+
+process_gb_id = (gb_id) ->
+  gb_link = $('<a/>',{href: gb_url(gb_id),target: '_blank'}).text(gb_id)
+  $('#results_list').append($('<li/>', {id: html_id(gb_id)}).append(gb_link))
+  console.log gb_id

 process_identifier = (identifier_string) ->
   $('#results').empty()


add pause and play classes from FontAwesome; add ability to pause and restart the timer

--- lib/dashing-contrib/assets/widgets/switcher/switcher.coffee
+++ lib/dashing-contrib/assets/widgets/switcher/switcher.coffee
@@ -92,12 +92,18 @@
 #   - show the name of the next dashboard
 #   - add controls for manually cycling through dashboards
 class SwitcherControls
+  arrowContent = "￫"
+  stopTimerContent = "stop timer"
+
   constructor: (interval=60000, nextDashboardName) ->
     @currentTime = parseInt(interval, 10)
     @interval = parseInt(interval, 10)
     @$elements = $('#dc-switcher-controls')
     @nextDashboardName = nextDashboardName
     @incrementTime = 1000 # refresh every 1000 milliseconds
+    @arrowContent = @$elements.data('arrow-content') || SwitcherControls.arrowContent
+    @stopTimerContent = @$elements.data('stop-timer-content') || SwitcherControls.stopTimerContent
+    @

   present: () ->
     @$elements.length
@@ -105,14 +111,23 @@
   start: () ->
     @addElements()
     @$timer = $.timer(@updateTimer, @incrementTime, true)
+    @paused = false

   addElements: () ->
     @$nextDashboardNameContainer = $("")
     @$countdown = $("")
-    manualSwitcher = $(" ￫")
-    @$elements.append(@$nextDashboardNameContainer).append(@$countdown).append(manualSwitcher).click( () =>
-      location.href = "/#{@nextDashboardName}"
-    )
+    @$manualSwitcher = $("").
+      html(@arrowContent).
+      click () =>
+        location.href = "/#{@nextDashboardName}"
+    @$switcherStopper = $("").
+      html(@stopTimerContent).
+      click(@pause)
+    @$elements.
+      append(@$nextDashboardNameContainer).
+      append(@$countdown).
+      append(@$manualSwitcher).
+      append(@$switcherStopper)

   formatTime: (time) ->
     time = time / 10;
@@ -125,6 +140,13 @@
     while str.length < length
       str = "0#{str}"
     str
+
+  pause: () =>
+    @$timer.toggle()
+    if @$switcherStopper.hasClass('fa-pause')
+      @$switcherStopper.removeClass('fa-pause').addClass('fa-play')
+    else
+      @$switcherStopper.removeClass('fa-play').addClass('fa-pause')

   resetCountdown: () ->
     # Get time from form
@@ -144,7 +166,7 @@

     # If timer is complete, trigger alert
     if @currentTime is 0
-      @$timer.stop()
+      @pause()
       @resetCountdown()
       return

@@ -153,9 +175,9 @@
     if @currentTime < 0
       @currentTime = 0

-
 # Dashboard loaded and ready
 Dashing.on 'ready', ->
+  window.SwitcherControls = SwitcherControls
   # If multiple widgets per list item, switch them periodically
   $('.gridster li').each (index, listItem) ->
     $listItem = $(listItem)


Declare intended visibility of methods

Declare intended visibility of methods

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -3,11 +3,15 @@

 module.exports =
 class GitHubFile
+
+  # Public
   @fromSession: (editSession) ->
     new GitHubFile(editSession)

+  # Internal
   constructor: (@editSession) ->

+  # Public
   open: ->
     unless @isOpenable()
       console.warn error for error in @validationErrors()
@@ -15,40 +19,11 @@

     @openUrlInBrowser(@blobUrl())

-  openUrlInBrowser: (url) ->
-    child_process.exec "open #{url}", (error, stdout, stderr) ->
-      throw error if error?
-
-  blobUrl: ->
-    "#{@githubRepoUrl()}/blob/#{@branch()}/#{@filePath()}"
-
-  gitUrl: ->
-    git.getRepo().getConfigValue("remote.#{@remoteName()}.url")
-
-  githubRepoUrl: ->
-    url = @gitUrl()
-    if url.match /https:\/\/github.com\// # e.g., https://github.com/foo/bar.git
-      url.replace(/\.git$/, '')
-    else if url.match /git@github.com/    # e.g., git@github.com:foo/bar.git
-      url.
-        replace(/^git@github.com:/, 'https://github.com/').
-        replace(/\.git$/, '')
-
-  filePath: ->
-    git.relativize(@editSession.getBuffer().getPath())
-
-  remoteName: ->
-    refName = git.getRepo().getUpstreamBranch() # e.g., "refs/remotes/origin/master"
-    return null unless refName?
-
-    refName.match(/^refs\/remotes\/(.*)\/.*$/)[1]
-
-  branch: ->
-    git.getShortHead()
-
+  # Public
   isOpenable: ->
     @validationErrors().length == 0

+  # Public
   validationErrors: ->
     unless @remoteName()
       return ["No remote tracking branch exists for current branch (#{@branch()})"]
@@ -60,3 +35,41 @@
       return ["Remote URL is not hosted on GitHub.com (#{@gitUrl()})"]

     []
+
+  # Internal
+  openUrlInBrowser: (url) ->
+    child_process.exec "open #{url}", (error, stdout, stderr) ->
+      throw error if error?
+
+  # Internal
+  blobUrl: ->
+    "#{@githubRepoUrl()}/blob/#{@branch()}/#{@filePath()}"
+
+  # Internal
+  gitUrl: ->
+    git.getRepo().getConfigValue("remote.#{@remoteName()}.url")
+
+  # Internal
+  githubRepoUrl: ->
+    url = @gitUrl()
+    if url.match /https:\/\/github.com\// # e.g., https://github.com/foo/bar.git
+      url.replace(/\.git$/, '')
+    else if url.match /git@github.com/    # e.g., git@github.com:foo/bar.git
+      url.
+        replace(/^git@github.com:/, 'https://github.com/').
+        replace(/\.git$/, '')
+
+  # Internal
+  filePath: ->
+    git.relativize(@editSession.getBuffer().getPath())
+
+  # Internal
+  remoteName: ->
+    refName = git.getRepo().getUpstreamBranch() # e.g., "refs/remotes/origin/master"
+    return null unless refName?
+
+    refName.match(/^refs\/remotes\/(.*)\/.*$/)[1]
+
+  # Internal
+  branch: ->
+    git.getShortHead()


Minor fixes and cleanup

Minor fixes and cleanup

--- lib/ide-backend.coffee
+++ lib/ide-backend.coffee
@@ -15,6 +15,8 @@

   constructor: (@upi) ->
     @disposables = new CompositeDisposable
+
+    @buildTarget = {name: 'All'}

     @disposables.add @upi.addPanelControl @targetElem = (document.createElement 'ide-haskell-target'),
       events:
@@ -203,14 +205,9 @@
       @targetElem.innerText = "#{name}"

   setTarget: ->
-    @getTargets().then (targets) =>
+    [cabalRoot, cabalFile] = @findCabalFile @getActiveProjectPath()
+    @parseCabalFile (path.join cabalRoot, cabalFile), (targets) =>
       new TargetListView
         items: targets.targets
         onConfirmed: (@buildTarget) =>
           @showTarget()
-
-  getTargets: ->
-    [cabalRoot, cabalFile] = @findCabalFile @getActiveProjectPath()
-    new Promise (resolve) =>
-      @parseCabalFile (path.join cabalRoot, cabalFile), (cabalParsed) ->
-        resolve cabalParsed


ignore in-building jobs

ignore in-building jobs

--- lib/jenkins-lib.coffee
+++ lib/jenkins-lib.coffee
@@ -67,9 +67,15 @@
             jobName : jobName
             requestUrl : url
             buildNumber : responseJson.number
-            result : responseJson.result
+            building : responseJson.building is true
+            result : unless responseJson.building is true then responseJson.result else 'BUILDING'
             oldBuildNumber : oldResult?.buildNumber
             oldResult : oldResult?.result
+
+          # If the job is currently building, we ignore the new state "BUILDING"
+          if newResult.building
+            newResult.result = newResult.oldResult
+
           JobState[jobName] = newResult
           deferred.resolve newResult
         else


:art: Use MessagesRegistry in Linter

:art: Use MessagesRegistry in Linter

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -4,6 +4,7 @@
 EditorLinter = require './editor-linter'
 Helpers = require './helpers'
 Commands = require './commands'
+Messages = require './messages'

 class Linter
   constructor:(@state)  ->
@@ -16,9 +17,9 @@
     @subscriptions = new CompositeDisposable
     @emitter = new Emitter
     @editorLinters = new Map
-    @messages = new Map # Values set in editor-linter and consumed in views.render
     @linters = new Set # Values are pushed here from Main::consumeLinter

+    @messages = new Messages(this)
     @views = new LinterViews @state, this
     @commands = new Commands this

@@ -75,18 +76,16 @@
     @linters

   setMessages: (linter, messages) ->
-    @messages.set(linter, Helpers.validateResults(messages))
-    @emitter.emit 'did-change-messages', @messages
+    @messages.set(linter, messages)

   deleteMessages: (linter, messages) ->
     @messages.delete(linter)
-    @emitter.emit 'did-change-messages', @messages

   getMessages: ->
-    return @messages
+    return @messages.get()

   onDidChangeMessages: (callback)->
-    return @emitter.on 'did-change-messages', callback
+    return @messages.onDidChange(callback)

   onDidChangeProjectMessages: (callback) ->
     console.warn("Linter::onDidChangeProjectMessages is deprecated, use Linter::onDidChangeMessages instead")


call anaytics.identify on fetch 'me' callback

--- src/remote/services/hull.coffee
+++ src/remote/services/hull.coffee
@@ -3,6 +3,15 @@
   (app)->

     config = app.config
+
+    identify = (me)->
+      return unless me
+      analytics = require('analytics')
+      ident = _.pick(me, 'name', 'email', 'id', 'picture')
+      ident.distinct_id = me.id
+      ident.$name       = me.name
+      analytics.identify(me.id, ident)
+

     handler = (req, route, callback, errback)=>
       path = req.path.replace(/^\/?hull\//, '')
@@ -21,7 +30,10 @@
         headers:
           'Hull-App-Id': config.appId

-      request.done((res)-> callback(res))
+      request.done (res)->
+        identify(res) if path == 'me'
+        callback(res)
+
       request.fail(errback)

       return
@@ -38,8 +50,6 @@
       paths:
         analytics: 'components/analytics/analytics'
         base64:    'components/base64/base64'
-      shim:
-        analytics: { exports: 'analytics' }

     initialize: (app)->
       analytics = require('analytics')
@@ -50,12 +60,9 @@
         analyticsSettings[_service.name] = _service

       analytics.initialize(analyticsSettings)
+
       if app.config.data.me?.id?
-        me = app.config.data.me
-        ident = _.pick(me, 'name', 'email', 'id', 'picture')
-        ident.distinct_id = me.id
-        ident.$name       = me.name
-        analytics.identify(ident.$name, ident)
+        identify(app.config.data.me)

       analytics.track("init", { appId: config.appId })
       app.core.services.add([ { path: 'hull/*path', handler: handler } ])


Parsing JSON 3 times in one line is for suckers!


JAccount:fetchMail : use Relationship.cursor/each instead ‘all’ correctly.

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -836,20 +836,13 @@
           sourceId    :
             $ne       : delegate.getId()

-        jraphical.Relationship.cursor query, {}, (err, cursor)->
-
+        jraphical.Relationship.cursor query, (err, cursor)->
           return callback err  if err
-
           message.participants = []
-          cursor.nextObject (err, rel)->
-            if err
-              # console.log "Cursor.each failed:", err
-              callback err
-              fin()
-            else if rel
+          cursor.each (err, rel)->
+            unless rel then fin()
+            else
               message.participants.push rel  if register.sign rel.sourceId
-            else
-              fin()

       , callback


added tooltip handling for images in the embedBox

--- client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
+++ client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
@@ -308,6 +308,9 @@
     #     size:
     #       height : 300
     #       width : 300
+    #     bind : 'load'
+    #     load :=>
+    #       @image.tooltip.setPosition(undefined,yes)
     #     attributes :
     #       src  : (@utils.proxifyUrl @getData().link_embed?.images?[0]?.url)
     #     delegate      : @
@@ -319,10 +322,24 @@
       #   view      : @imageTooltip
       tagName     : 'img'
       bind        : "error load"
-      load  : (event)=>
-        # @imageTooltip.options.size =
-        #   height : $(event.target).attr 'y'
-        #   width : $(event.target).attr 'x'
+      # load  : (event)=>
+
+      #   x = event.srcElement.naturalWidth
+      #   y = event.srcElement.naturalHeight
+
+
+      #   if y > (window.innerHeight*3/4) or x > (window.innerWidth*3/4)
+
+      #     factor = Math.max (window.innerWidth/x) ,(window.innerHeight/y)
+
+      #     x = factor * x 2
+      #     y = factor * y/2
+
+      #   @imageTooltip.options.size =
+      #     height : y
+      #     width : x
+
+
       error       : =>
         unless @getDelegate().getOptions().hasConfig # do not hide for widgets
           @getDelegate().hide()


<tests> Adding controls on driver state for Sensor Controller Test.


fixed startup

fixed startup

--- lib/atom-ternjs.coffee
+++ lib/atom-ternjs.coffee
@@ -18,6 +18,7 @@

   activate: (state) ->
     @startServer()
+    @registerEvents()

   activatePackage: ->
     atom.packages.activatePackage('autocomplete-plus')
@@ -63,9 +64,6 @@
       console.error 'error', err

   registerEvents: ->
-    atom.workspace.onDidAddTextEditor ({item, pane, index}) =>
-      @registerEditor(pane.items[index])
-
     @disposables.push atom.workspace.onDidOpen (e) =>
       if e.item.getGrammar().name is 'JavaScript'
         @startServer()


factor out common functionality

factor out common functionality

--- lib/understream.coffee
+++ lib/understream.coffee
@@ -18,15 +18,18 @@
   F.prototype = constructor.prototype
   new F()

+pipe_streams_together = (streams...) ->
+  streams[i].pipe streams[i + 1] for i in [0..streams.length - 2]
+
 # Based on: http://stackoverflow.com/questions/17471659/creating-a-node-js-stream-from-two-piped-streams
 # The version there was broken and needed some changes, we just kept the concept of using the 'pipe'
 # event and overriding the pipe method
 class StreamCombiner extends PassThrough
-  constructor: (@streams...) ->
+  constructor: (streams...) ->
     super objectMode: true
-    @head = @streams[0]
-    @tail = @streams[@streams.length - 1]
-    @streams[i].pipe @streams[i + 1] for i in [0..@streams.length - 2]
+    @head = streams[0]
+    @tail = streams[streams.length - 1]
+    pipe_streams_together streams...
     @on 'pipe', (source) => source.unpipe(@).pipe @head
   pipe: (dest, options) =>
     @tail.pipe dest, options
@@ -79,9 +82,7 @@
     dmn.run =>
       debug 'running'
       return unless @read_streams.length > 1
-      _([0..@read_streams.length-2]).each (i) =>
-        debug 'piping', @read_streams[i]?.constructor.name, '-->', @read_streams[i+1]?.constructor.name
-        @read_streams[i].pipe @read_streams[i+1]
+      pipe_streams_together @read_streams...
     @
   readable: => @read_stream # if you want to get out of understream and access the raw stream
   duplex: => new StreamCombiner @read_streams...


social: add more email domains to blocked list

--- workers/social/lib/social/models/user/emailchecker.coffee
+++ workers/social/lib/social/models/user/emailchecker.coffee
@@ -13,13 +13,16 @@
   "10minutemail.de",
   "123-m.com",
   "126.com",
+  "12minutemail.com",
   "139.com",
   "163.com",
+  "1ce.us",
   "1chuan.com",
   "1pad.de",
   "1zhuan.com",
   "20minutemail.com",
   "21cn.com",
+  "24hourmail.com",
   "2prong.com",
   "30minutemail.com",
   "33mail.com",
@@ -41,6 +44,10 @@
   "7tags.com",
   "99experts.com",
   "9ox.net",
+  "SendSpamHere.com",
+  "SpamHereLots.com",
+  "SpamHerePlease.com",
+  "TempEMail.net",
   "a-bc.net",
   "a45.in",
   "afrobacon.com",
@@ -64,6 +71,7 @@
   "baxomale.ht.cx",
   "beddly.com",
   "beefmilk.com",
+  "big1.us",
   "bigprofessor.so",
   "bigstring.com",
   "binkmail.com",
@@ -123,6 +131,7 @@
   "dbunker.com",
   "dcemail.com",
   "deadaddress.com",
+  "deadchildren.org",
   "deadspam.com",
   "deagot.com",
   "dealja.com",
@@ -130,6 +139,7 @@
   "despammed.com",
   "devnullmail.com",
   "dfgh.net",
+  "dharmatel.net",
   "digitalsanctuary.com",
   "dingbone.com",
   "discard.email",
@@ -172,6 +182,7 @@
   "emailias.com",
   "emailigo.de",
   "emailinfive.com",
+  "emailisvalid.com",
   "emaillime.com",
   "emailmiser.com",
   "emailsensei.com",
@@ -199,6 +210,7 @@
   "fakeinbox.com",
   "fakeinformation.com",
   "fakemail.fr",
+  "fakemailgenerator.com",
   "fakemailz.com",
   "fammix.com",
   "fansworldwide.de",
@@ -214,7 +226,7 @@
   "fastsuzuki.com",
   "fasttoyota.com",
   "fastyamaha.com",
-  "fatflap.com ",
+  "fatflap.com",
   "fdfdsfds.com",
   "fightallspam.com",
   "filzmail.com",
@@ -224,8 +236,10 @@
   "flyspam.com",
   "footard.com",
   "forgetmail.com",
+  "fornow.eu",
   "fr33mail.info",
   "frapmail.com",
+  "freemail.ms",
   "freundin.ru",
   "friendlymail.co.uk",
   "front14.org",
@@ -233,6 +247,7 @@
   "fudgerub.com",
   "fux0ringduh.com",
   "garliclife.com",
+  "gawab.com",
   "gelitik.in",
   "get1mail.com",
   "get2mail.fr",
@@ -283,6 +298,7 @@
   "herp.in",
   "hidemail.de",
   "hidzz.com",
+  "hmamail.com",
   "hochsitze.com",
   "hotpop.com",
   "hulapla.de",
@@ -305,8 +321,10 @@
   "incognitomail.org",
   "insorg-mail.info",
   "instant-mail.de",
+  "instantemailaddress.com",
   "ipoo.org",
   "irish2me.com",
+  "iroid.com",
   "iwi.net",
   "jetable.com",
   "jetable.fr.nf",
@@ -342,8 +360,10 @@
   "lopl.co.cc",
   "lortemail.dk",
   "lovemeleaveme.com",
+  "lr7.us",
   "lr78.com",
   "lroid.com",
+  "luv2.us",
   "m4ilweb.info",
   "maboard.com",
   "mail-filter.com",
@@ -374,15 +394,19 @@
   "mailinator2.com",
   "mailincubator.com",
   "mailismagic.com",
+  "mailmate.com",
   "mailme.ir",
   "mailme.lv",
+  "mailme24.com",
   "mailmetrash.com",
   "mailmoat.com",
   "mailnator.com",
   "mailnesia.com",
   "mailnull.com",
   "mailquack.com",
+  "mailsac.com",
   "mailscrap.com",
+  "mailseal.de",
   "mailshell.com",
   "mailsiphon.com",
   "mailslapping.com",
@@ -395,6 +419,7 @@
   "manifestgenerator.com",
   "manybrain.com",
   "mbx.cc",
+  "mciek.com",
   "mega.zik.dj",
   "meinspamschutz.de",
   "meltmail.com",
@@ -411,6 +436,7 @@
   "monemail.fr.nf",
   "monmail.fr.nf",
   "monumentmail.com",
+  "ms9.mailslite.com",
   "msa.minsmail.com",
   "mt2009.com",
   "mt2014.com",
@@ -448,6 +474,7 @@
   "nonspam.eu",
   "nonspammer.de",
   "noref.in",
+  "nospam.wins.com.br",
   "nospam.ze.tc",
   "nospam4.us",
   "nospamfor.us",
@@ -456,6 +483,7 @@
   "notsharingmy.info",
   "nowhere.org",
   "nowmymail.com",
+  "ntlhelp.net",
   "nurfuerspam.de",
   "nus.edu.sg",
   "nwldx.com",
@@ -486,6 +514,7 @@
   "politikerclub.de",
   "poofy.org",
   "pookmail.com",
+  "postacin.com",
   "privacy.net",
   "privy-mail.com",
   "privymail.de",
@@ -499,19 +528,24 @@
   "quickinbox.com",
   "rcpt.at",
   "reallymymail.com",
+  "receiveee.chickenkiller.com",
+  "receiveee.com",
   "recode.me",
   "recursor.net",
   "reconmail.com",
+  "recursor.net",
   "recyclemail.dk",
   "regbypass.com",
   "regbypass.comsafe-mail.net",
   "rejectmail.com",
   "rhyta.com",
+  "rk9.chickenkiller.com",
   "rklips.com",
   "rmqkr.net",
   "royal.net",
   "rppkn.com",
   "rtrtr.com",
+  "ruffrey.com",
   "s0ny.net",
   "safe-mail.net",
   "safersignup.de",
@@ -519,7 +553,8 @@
   "safetypost.de",
   "sandelf.de",
   "saynotospams.com",
-  "schafmail.de ",
+  "scatmail.com",
+  "schafmail.de",
   "selfdestructingmail.com",
   "selfdestructingmail.org",
   "SendSpamHere.com",
@@ -535,6 +570,7 @@
   "siteposter.net",
   "skeefmail.com",
   "slaskpost.se",
+  "slave-auctions.net",
   "slopsbox.com",
   "slushmail.com",
   "smashmail.de",
@@ -546,7 +582,10 @@
   "sofort-mail.de",
   "sogetthis.com",
   "sohu.com",
+  "soisz.com",
+  "soodomail.com",
   "soodonims.com",
+  "spam-be-gone.com",
   "spam.la",
   "spam.su",
   "spam4.me",
@@ -570,6 +609,7 @@
   "spamcowboy.net",
   "spamcowboy.org",
   "spamday.com",
+  "spamdecoy.net",
   "spamex.com",
   "spamfree.eu",
   "spamfree24.com",
@@ -646,6 +686,7 @@
   "thecloudindex.com",
   "thisisnotmyrealemail.com",
   "throwawayemailaddress.com",
+  "throwawaymail.com",
   "tilien.com",
   "tittbit.in",
   "tmailinator.com",
@@ -671,6 +712,7 @@
   "trashmailer.com",
   "trashymail.com",
   "trashymail.net",
+  "trayna.com",
   "trbvm.com",
   "trillianpro.com",
   "tryalert.com",
@@ -679,6 +721,7 @@
   "tyldd.com",
   "uggsrock.com",
   "umail.net",
+  "unmail.ru",
   "upliftnow.com",
   "uplipht.com",
   "uroid.com",
@@ -696,8 +739,8 @@
   "webm4il.info",
   "webuser.in",
   "weg-werf-email.de",
-  "wegwerf-email-addressen.de ",
-  "wegwerf-emails.de ",
+  "wegwerf-email-addressen.de",
+  "wegwerf-emails.de",
   "wegwerfadresse.de",
   "wegwerfemail.de",
   "wegwerfmail.de",
@@ -723,6 +766,7 @@
   "xemaps.com",
   "xents.com",
   "xmaily.com",
+  "xoxox.cc",
   "xoxy.net",
   "xyzfree.net",
   "yahoo.com.ph",


social: add missing KodingError

social: add missing KodingError

--- workers/social/lib/social/models/verificationtoken.coffee
+++ workers/social/lib/social/models/verificationtoken.coffee
@@ -1,4 +1,5 @@
 {Module} = require 'jraphical'
+KodingError = require '../error'

 class PINExistsError extends Error
   constructor:(message)->
@@ -46,7 +47,6 @@

     if not email

-      KodingError = require '../error'
       callback new KodingError "E-mail is not provided!"

     else


STL/dom moving the element/commons_test from vows to mocha

--- stl/dom/test/unit/element/commons_test.coffee
+++ stl/dom/test/unit/element/commons_test.coffee
@@ -1,11 +1,11 @@
 #
 # The Element common methods section unit tests
 #
-# Copyright (C) 2011 Nikolay Nemshilov
+# Copyright (C) 2011-2012 Nikolay Nemshilov
 #
-{describe, assert, server, load_element} = require('../../test_helper')
-
-server.respond "/commons.html": """
+{Browser} = require('../../test_helper')
+
+Browser.respond "/commons.html": """
   <html>
     <head>
       <script src="/core.js"></script>
@@ -17,163 +17,155 @@
   </html>
   """

-test_element = ->
-  load_element("/commons.html", this, 'test')
-
-
-describe 'Element Commons', module,
-  "#attr":
-
-    "\b('name')":
-      topic: test_element
-
-      "should read a property attribute": (element) ->
-        assert.equal element.attr('id'), 'test'
-
-      "should read the 'data-test' attribute": (element) ->
-        assert.equal element.attr('data-test'), 'test'
-
-      "should return 'null' for non existing attributes": (element) ->
-        assert.isNull element.attr('nonexistent')
-
-
-    "\b('name', 'value')":
-      topic: test_element
-
-      "should return the element back": (element) ->
-        assert.same element.attr('title', 'text'), element
-
-      "should set property attributes": (element) ->
+
+describe 'Element Commons', ->
+  get = (callback)->
+    (done)->
+      Browser.open "/commons.html", ($, window)->
+        element = new $.Element(window.document.getElementById('test'))
+        callback(element, $, window, window.document)
+        done()
+
+  describe "#attr", ->
+
+    describe "\b('name')", ->
+
+      it "should read a property attribute", get (element) ->
+        element.attr('id').should.equal 'test'
+
+      it "should read the 'data-test' attribute", get (element) ->
+        element.attr('data-test').should.eql 'test'
+
+      it "should return 'null' for non existing attributes", get (element) ->
+        (element.attr('nonexistent') is null).should.be.true
+
+
+    describe "\b('name', 'value')", ->
+
+      it "should return the element back", get (element) ->
+        element.attr('title', 'text').should.equal element
+
+      it "should set property attributes", get (element) ->
         element.attr('title', 'new value')
-        assert.equal element._.title, 'new value'
-
-      "should set non-property attributes": (element) ->
+        element._.title.should.eql 'new value'
+
+      it "should set non-property attributes", get (element) ->
         element.attr('data-new', 'something')
-        assert.equal element._.getAttribute('data-new'), 'something'
-
-
-    "\b({name: 'value'})":
-      topic: test_element
-
-      "should return the element back afterwards": (element) ->
-        assert.same element.attr(smth: 'value'), element
-
-      "should set all the attributes from the hash": (element) ->
+        element._.getAttribute('data-new').should.eql 'something'
+
+
+    describe "\b({name: 'value'})", ->
+
+      it "should return the element back afterwards", get (element) ->
+        element.attr(smth: 'value').should.equal element
+
+      it "should set all the attributes from the hash", get (element) ->
         element.attr
           test_attr1: 'value1'
           test_attr2: 'value2'

-        assert.equal element._.getAttribute('test_attr1'), 'value1'
-        assert.equal element._.getAttribute('test_attr2'), 'value2'
-
-
-    "\b('name', null)":
-      topic: test_element
-
-      "should remove the attribute": (element) ->
+        element._.getAttribute('test_attr1').should.eql 'value1'
+        element._.getAttribute('test_attr2').should.eql 'value2'
+
+
+    describe "\b('name', null)", ->
+
+      it "should remove the attribute", get (element) ->
         element.attr('something', 'something')
-        assert.equal element.attr('something'), 'something'
+        element.attr('something').should.eql 'something'

         element.attr('something', null)
-        assert.isNull element.attr('something')
-
-
-  "#hidden()":
-    topic: test_element
-
-    "should say 'true' when the element is hidden": (element)->
-      element._.style.display = 'none'
-      assert.isTrue element.hidden()
-
-    "should say 'false' when the element is visible": (element)->
-      element._.style.display = 'block'
-      assert.isFalse element.hidden()
-
-  "#visible()":
-    topic: test_element
-
-    "should say 'false' when the element is hidden": (element)->
-      element._.style.display = 'none'
-      assert.isFalse element.visible()
-
-    "should say 'true' when the element is visible": (element)->
-      element._.style.display = 'block'
-      assert.isTrue element.visible()
-
-  "#hide()":
-    topic: test_element
-
-    "should hide the element when it is visible": (element)->
+        (element.attr('something') is null).should.be.true
+
+
+  describe "#hidden()", ->
+
+    it "should say 'true' when the element is hidden", get (element)->
+      element._.style.display = 'none'
+      element.hidden().should.be.true
+
+    it "should say 'false' when the element is visible", get (element)->
+      element._.style.display = 'block'
+      element.hidden().should.be.false
+
+  describe "#visible()", ->
+
+    it "should say 'false' when the element is hidden", get (element)->
+      element._.style.display = 'none'
+      element.visible().should.be.false
+
+    it "should say 'true' when the element is visible", get (element)->
+      element._.style.display = 'block'
+      element.visible().should.be.true
+
+  describe "#hide()", ->
+
+    it "should hide the element when it is visible", get (element)->
       element._.style.display = 'block'
       element.hide()
-      assert.equal element._.style.display, 'none'
-
-    "should leave element hidden when it is not visible": (element)->
+      element._.style.display.should.eql 'none'
+
+    it "should leave element hidden when it is not visible", get (element)->
       element._.style.display = 'none'
       element.hide()
-      assert.equal element._.style.display, 'none'
-
-    "should return the element reference back": (element)->
-      assert.same element.hide(), element
-
-  "#show()":
-    topic: test_element
-
-    "should show an element if it's hidden": (element)->
+      element._.style.display.should.eql 'none'
+
+    it "should return the element reference back", get (element)->
+      element.hide().should.equal element
+
+  describe "#show()", ->
+
+    it "should show an element if it's hidden", get (element)->
       element._.style.display = 'none'
       element.show()
-      assert.equal element._.style.display, 'block'
-
-    "should leave a visible element visible": (element)->
+      element._.style.display.should.eql 'block'
+
+    it "should leave a visible element visible", get (element)->
       element._.style.display = 'inline'
       element.show()
-      assert.equal element._.style.display, 'inline'
-
-    "should return the element reference back": (element)->
-      assert.same element.show(), element
-
-  "#toggle()":
-    topic: test_element
-
-    "should show an element if it is hidden": (element)->
+      element._.style.display.should.eql 'inline'
+
+    it "should return the element reference back", get (element)->
+      element.show().should.equal element
+
+  describe "#toggle()", ->
+
+    it "should show an element if it is hidden", get (element)->
       element._.style.display = 'none'
       element.toggle()
-      assert.equal element._.style.display, 'block'
-
-    "should hide an element if it's visible": (element)->
+      element._.style.display.should.eql 'block'
+
+    it "should hide an element if it's visible", get (element)->
       element._.style.display = 'block'
       element.toggle()
-      assert.equal element._.style.display, 'none'
-
-    "should return back a reference to the element": (element)->
-      assert.same element.toggle(), element
-
-
-  "#document()":
-    topic: test_element
-
-    "should return the owner document wrapper": (element)->
+      element._.style.display.should.eql 'none'
+
+    it "should return back a reference to the element", get (element)->
+      element.toggle().should.equal element
+
+
+  describe "#document()", ->
+
+    it "should return the owner document wrapper", get (element, $, window, raw_document)->
       document = element.document()

-      assert.instanceOf document, this.Document
-      assert.same       document._, this.document
-
-    "should return the same wrapper all the time": (element)->
-      assert.same element.document(), element.document()
-
-  "#window()":
-    topic: test_element
-
-    "should return the owner window wrapper": (element)->
+      document.should.be.instanceOf $.Document
+      document._.should.equal raw_document
+
+    it "should return the same wrapper all the time", get (element)->
+      element.document().should.equal element.document()
+
+  describe "#window()", ->
+
+    it "should return the owner window wrapper", get (element, $, raw_window)->
       window = element.window()

-      assert.instanceOf window,          this.Window
-      assert.same       window._.window, this.window.window
-
-  "#data()":
-    topic: test_element
-
-    "should read data- attributes": (element)->
+      window.should.be.instanceOf $.Window
+      window._.should.be.same     raw_window
+
+  describe "#data()", ->
+
+    it "should read data- attributes", get (element)->
       element.attr({
         'data-false':  'false'
         'data-true':   'true'
@@ -184,57 +176,57 @@
         'data-plain':  'plain text'
       })

-      assert.equal     element.data('false'), false
-      assert.equal     element.data('true'), true
-      assert.equal     element.data('number'), 1.23
-      assert.equal     element.data('string'), 'string'
-      assert.deepEqual element.data('array'), [1,2,3]
-      assert.deepEqual element.data('object'), {boo: "hoo"}
-      assert.equal     element.data('plain'), 'plain text'
-      assert.isNull    element.data('non-existing')
-
-    "should read nested attributes": (element)->
+      element.data('false').should.equal  false
+      element.data('true').should.equal   true
+      element.data('number').should.equal 1.23
+      element.data('string').should.equal 'string'
+      element.data('array').should.eql    [1,2,3]
+      element.data('object').should.eql   {boo: "hoo"}
+      element.data('plain').should.equal  'plain text'
+      (element.data('non-existing') is null).should.be.true
+
+    it "should read nested attributes", get (element)->
       element.attr({
         'data-thing-one': '1'
         'data-thing-two': '2'
         'data-thing-three-one': '3.1'
       })

-      assert.deepEqual element.data('thing'), {
+      element.data('thing').should.eql {
         one: 1, two: 2, threeOne: 3.1
       }

-    "should write data- attributes": (element)->
-      assert.same  element,  element.data('string', 'string')
-      assert.equal 'string', element._.getAttribute('data-string')
-      assert.isTrue element['data-string'] is undefined
-
-      assert.equal 'false',   element.data('false', false)._.getAttribute('data-false')
-      assert.equal 'true',    element.data('true', true)._.getAttribute('data-true')
-      assert.equal '1.23',    element.data('number', 1.23)._.getAttribute('data-number')
-      assert.equal '[1,2,3]', element.data('array', [1,2,3])._.getAttribute('data-array')
-
-    "should allow to write data as a plain hash": (element)->
-      assert.same element, element.data({
+    it "should write data- attributes", get (element)->
+      element.data('string', 'string').should.equal element
+      element._.getAttribute('data-string').should.equal 'string'
+      (element['data-string'] is undefined).should.be.true
+
+      element.data('false', false)._.getAttribute('data-false').should.equal   'false'
+      element.data('true', true)._.getAttribute('data-true').should.equal      'true'
+      element.data('number', 1.23)._.getAttribute('data-number').should.equal  '1.23'
+      element.data('array', [1,2,3])._.getAttribute('data-array').should.equal '[1,2,3]'
+
+    it "should allow to write data as a plain hash", get (element)->
+      element.data({
         one: 1, two: 2, three: 3
-      })
-
-      assert.equal '1', element._.getAttribute('data-one')
-      assert.equal '2', element._.getAttribute('data-two')
-      assert.equal '3', element._.getAttribute('data-three')
-
-    "should allow to write data as a nested hash": (element)->
-      assert.same element, element.data('test', {
+      }).should.equal element
+
+      element._.getAttribute('data-one').should.equal   '1'
+      element._.getAttribute('data-two').should.equal   '2'
+      element._.getAttribute('data-three').should.equal '3'
+
+    it "should allow to write data as a nested hash", get (element)->
+      element.data('test', {
         'one': 1, two: 2, 'three-one': 3.1, 'threeTwo': 3.2
-      })
-
-      assert.equal '1',   element._.getAttribute('data-test-one')
-      assert.equal '2',   element._.getAttribute('data-test-two')
-      assert.equal '3.1', element._.getAttribute('data-test-three-one')
-      assert.equal '3.2', element._.getAttribute('data-test-three-two')
-
-    "should allow to remove data- attributes": (element)->
+      }).should.equal element
+
+      element._.getAttribute('data-test-one').should.equal       '1'
+      element._.getAttribute('data-test-two').should.equal       '2'
+      element._.getAttribute('data-test-three-one').should.equal '3.1'
+      element._.getAttribute('data-test-three-two').should.equal '3.2'
+
+    it "should allow to remove data- attributes", get (element)->
       element.attr {'data-something': 'something'}

-      assert.equal  element, element.data('something', null)
-      assert.equal '', element._.getAttribute('data-something')
+      element.data('something', null).should.equal          element
+      (element._.getAttribute('data-something') is null).should.be.true


Add support for numbered and lettered lists

--- lib/mixins/text.coffee
+++ lib/mixins/text.coffee
@@ -71,16 +71,20 @@
   list: (list, x, y, options, wrapper) ->
     options = @_initOptions(x, y, options)

-    midLine = Math.round (@_font.ascender / 1000 * @_fontSize) / 2
-    r = options.bulletRadius or Math.round (@_font.ascender / 1000 * @_fontSize) / 3
-    indent = options.textIndent or r * 5
-    itemIndent = options.bulletIndent or r * 8
+    listType = options.listType or 'bullet'
+    unit = Math.round (@_font.ascender / 1000 * @_fontSize)
+    midLine = unit / 2
+    r = options.bulletRadius or unit / 3
+    indent = options.textIndent or if listType is 'bullet' then r * 5 else unit * 2
+    itemIndent = options.bulletIndent or if listType is 'bullet' then r * 8 else unit * 2

     level = 1
     items = []
     levels = []
+    numbers = []

     flatten = (list) ->
+      n = 1
       for item, i in list
         if Array.isArray(item)
           level++
@@ -89,8 +93,19 @@
         else
           items.push(item)
           levels.push(level)
+          numbers.push(n++) unless listType is 'bullet'

     flatten(list)
+
+    label = (n) ->
+      switch listType
+        when 'numbered'
+          "#{n}."
+        when 'lettered'
+          letter = String.fromCharCode (n - 1) % 26 + 65
+          times = Math.floor (n - 1) / 26 + 1
+          text = Array(times + 1).join(letter)
+          "#{text}."

     wrapper = new LineWrapper(this, options)
     wrapper.on 'line', @_line.bind(this)
@@ -104,8 +119,13 @@
         wrapper.lineWidth -= diff
         level = l

-      @circle @x - indent + r, @y + midLine, r
-      @fill()
+      switch listType
+        when 'bullet'
+          @circle @x - indent + r, @y + midLine, r
+          @fill()
+        when 'numbered', 'lettered'
+          text = label numbers[i - 1]
+          @_fragment text, @x - indent, @y, options

     wrapper.on 'sectionStart', =>
       pos = indent + itemIndent * (level - 1)


make JSLint happy about the while condition

--- optparse.coffee
+++ optparse.coffee
@@ -22,7 +22,7 @@
   parse: (args) ->
     options: {arguments: []}
     args: normalize_arguments args
-    while arg: args.shift()
+    while (arg: args.shift())
       is_option: !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
       matched_rule: no
       for rule in @rules


Add ignore extensions

Add ignore extensions

--- lib/path_finder.coffee
+++ lib/path_finder.coffee
@@ -21,7 +21,7 @@
     extension: ['app/extensions']
     batch: ['app/batches']
     root: ['']
-  ignores: /(?:\/.git\/|\.keep$)/
+  ignores: /(?:\/.git\/|\.keep$|\.DS_Store$|\.eot$|\.otf$|\.ttf$|\.woff$|\.png$|\.svg$|\.jpg$|\.gif$|\.mp4$|\.eps$|\.psd$)/

   constructor: (currentPath) ->
     @railsRootPath = @getRailsRootPath(currentPath)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera_fade.coffee
+++ src/camera_fade.coffee
@@ -65,7 +65,7 @@
   @param {Color} [color="transparent"] The color to fade to
   ###
   fadeOut: (duration = 30, color = 'transparent') ->
-    {alpha, color, duration} = Object.reverseMerge(options, fadeInDefaults)
+    {alpha, color, duration} = Object.reverseMerge(options, fadeOutDefaults)

     configureFade(duration, color, 1)


intro popover hides on sidebar cloase

intro popover hides on sidebar cloase

--- tutor/src/components/course-calendar/add-assignment-sidebar.cjsx
+++ tutor/src/components/course-calendar/add-assignment-sidebar.cjsx
@@ -93,7 +93,7 @@
         >
           {@renderAddActions()}
         </ul>
-        <IntroPopover onClose={@onPopoverClose} show={@state.showPopover} />
+        <IntroPopover onClose={@onPopoverClose} show={@state.showPopover and @props.isOpen} />
       </div>
       <PastAssignments
         className='sidebar-section'


format time.

format time.

--- assets/front.coffee
+++ assets/front.coffee
@@ -1,11 +1,11 @@
 window.log = ->
-  log.history = log.history or []
-  log.history.push arguments
-  console.log Array::slice.call(arguments)  if @console
+	log.history = log.history or []
+	log.history.push arguments
+	console.log Array::slice.call(arguments)  if @console

 soundManager.setup
-  url: '/static/flash/'
+	url: '/static/flash/'

 TIMING_INTERVAL = 30000 # ms between checking server ping
 NUM_TRACKS = 5
@@ -20,266 +20,267 @@
 MAGIC_REGEX = /(\s*-*\s*((\[|\(|\*|~)[^\)\]]*(mp3|dl|description|free|download|comment|out now|clip|bonus|preview|teaser|in store|follow me|follow on|prod|full|snip|exclusive|beatport|original mix)+[^\)\]]*(\]|\)|\*|~)|((OUT NOW( ON \w*)?|free|download|preview|follow me|follow on|teaser|in store|mp3|dl|description|full|snip|exclusive|beatport|original mix).*$))\s*|\[(.*?)\])/i

 comma = (x) ->
-  if x? then x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') else x
+	if x? then x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') else x

 window.ping = 0
 window.serverTime = -> (+new Date) - window.ping

 class Frame
-  constructor: (init, is_new) ->
-    for k, v of init
-      this[k] = v
-    @image = null
-    @div = null
-    @id = "track_#{@tracks[0].metadata.id}"
-    @new = if is_new then 'new' else ''
-    # hack for development... don't wanna do this
-    while document.getElementById(@id)
-      @id += "_"
-
-    @parseMetaData()
-
-  parseMetaData: ->
-    matches = @tracks[0].metadata.title.match(/(.*?) by (.*)/i)
-    if matches?
-      [_, @title, @artist] = matches
-      matches = @artist.match(/(.*?)\s+-\s+(.*)/i)
-      [_, @artist, other] = matches if matches?
-    else
-      matches = @tracks[0].metadata.title.match(/(.*?)\s*-\s+(.*)/i)
-      if matches?
-        [_, @artist, @title] = matches
-      else
-        @title = @tracks[0].metadata.title
-        @artist = @tracks[0].metadata.artist
-    console.log(matches)
-    console.log(@artist)
-    console.log(@title)
-
-    matches = @title.match(/([^\[\]]*?)( - ([^\[\]\(\)]*)|(\[.*\]))/i)
-    if matches?
-      [_, @title, _, other] = matches
-
-    #   Try to remove "Free Download," "Follow Me" and the like
-    _title = @title.replace MAGIC_REGEX, ""
-    @title = _title if _title.length > 0
-
-    _artist = @artist.replace MAGIC_REGEX, ""
-    @artist = _artist if _artist.length > 0
-
-    if @title[0] == '"' and @title[@title.length - 1] == '"'
-      @title = @title[1...@title.length - 1].trim()
-
-    #@img = if @tracks[0].metadata.artwork_url?
-    #         @tracks[0].metadata.artwork_url
-    #       else
-    #         @tracks[0].metadata.user.avatar_url
-
-
-    # Buttons
-    @buttons = true
-    @nid = @tracks[0].metadata.id
-
-
-    @url = @tracks[0].metadata.permalink_url
-
-
-  html: (first) ->
-    first = false if not first?
-    _new = @new
-    @new = ''
-    """
-

-
-

-        #{@title}
-        #{@artist}
-

-
-      #{if @stats then "
-
NOTHING

-      " else ""}
-

-    """
-
-  played: ->
-    (@time + @duration + BUFFERED) < (window.serverTime() / 1000)
-
-  playing: ->
-    ((@time + BUFFERED) < (window.serverTime() / 1000)) and not @played()
-
-  intendedParent: ->
-    document.getElementById( if @played() then "done" else "tracks" )
-
-  render: ->
-    return if @action != "Playback"
-    return @relayout() if @div?
-
-    parent = @intendedParent()
-    $(parent).prepend @html(true)
-    id = @id
-    setTimeout((-> $("##{id}").removeClass 'new hidden'), 100)
-    @div = document.getElementById @id
-
-  relayout: ->
-    @div = document.getElementById(@id) if not @div.parentNode?
-    newparent = document.getElementById( if @played() then "done" else "tracks" )
-    if @div.parentNode != newparent
-      neighbour = @div.parentNode.children[@div.parentNode.children.length - 2]
-      $(@div).addClass("ending")
-      $(neighbour).addClass("next")
-
-      div = @div
-      html = @html()
-      setTimeout ->
-        $(neighbour).removeClass("next")
-        div.parentNode.removeChild div
-        newparent.innerHTML = html + newparent.innerHTML
-        setTimeout ->
-          $(".hidden", newparent).removeClass('hidden')
-          if newparent.children.length > DONE_TRACKS_LIMIT
-            end = newparent.children[newparent.children.length - 1]
-            $(end).addClass('hidden')
-            setTimeout ->
-              newparent.removeChild end
-            , 1000
-        , 100
-      , 1400
+	constructor: (init, is_new) ->
+		for k, v of init
+			this[k] = v
+		@image = null
+		@div = null
+		@id = "track_#{@tracks[0].metadata.id}"
+		@new = if is_new then 'new' else ''
+		# hack for development... don't wanna do this
+		while document.getElementById(@id)
+			@id += "_"
+
+		@parseMetaData()
+
+	parseMetaData: ->
+		matches = @tracks[0].metadata.title.match(/(.*?) by (.*)/i)
+		if matches?
+			[_, @title, @artist] = matches
+			matches = @artist.match(/(.*?)\s+-\s+(.*)/i)
+			[_, @artist, other] = matches if matches?
+		else
+			matches = @tracks[0].metadata.title.match(/(.*?)\s*-\s+(.*)/i)
+			if matches?
+				[_, @artist, @title] = matches
+			else
+				@title = @tracks[0].metadata.title
+				@artist = @tracks[0].metadata.artist
+		console.log(matches)
+		console.log(@artist)
+		console.log(@title)
+
+		matches = @title.match(/([^\[\]]*?)( - ([^\[\]\(\)]*)|(\[.*\]))/i)
+		if matches?
+			[_, @title, _, other] = matches
+
+		#   Try to remove "Free Download," "Follow Me" and the like
+		_title = @title.replace MAGIC_REGEX, ""
+		@title = _title if _title.length > 0
+
+		_artist = @artist.replace MAGIC_REGEX, ""
+		@artist = _artist if _artist.length > 0
+
+		if @title[0] == '"' and @title[@title.length - 1] == '"'
+			@title = @title[1...@title.length - 1].trim()
+
+		#@img = if @tracks[0].metadata.artwork_url?
+		#         @tracks[0].metadata.artwork_url
+		#       else
+		#         @tracks[0].metadata.user.avatar_url
+
+
+		# Buttons
+		@buttons = true
+		@nid = @tracks[0].metadata.id
+
+
+		@url = @tracks[0].metadata.permalink_url
+
+
+	html: (first) ->
+		first = false if not first?
+		_new = @new
+		@new = ''
+		"""
+

+
+

+				#{@title}
+				#{@artist}
+

+
+			#{if @stats then "
+
NOTHING

+			" else ""}
+

+		"""
+
+	played: ->
+		(@time + @duration + BUFFERED) < (window.serverTime() / 1000)
+
+	playing: ->
+		((@time + BUFFERED) < (window.serverTime() / 1000)) and not @played()
+
+	intendedParent: ->
+		document.getElementById( if @played() then "done" else "tracks" )
+
+	render: ->
+		return if @action != "Playback"
+		return @relayout() if @div?
+
+		parent = @intendedParent()
+		$(parent).prepend @html(true)
+		id = @id
+		setTimeout((-> $("##{id}").removeClass 'new hidden'), 100)
+		@div = document.getElementById @id
+
+	relayout: ->
+		@div = document.getElementById(@id) if not @div.parentNode?
+		newparent = document.getElementById( if @played() then "done" else "tracks" )
+		if @div.parentNode != newparent
+			neighbour = @div.parentNode.children[@div.parentNode.children.length - 2]
+			$(@div).addClass("ending")
+			$(neighbour).addClass("next")
+
+			div = @div
+			html = @html()
+			setTimeout ->
+				$(neighbour).removeClass("next")
+				div.parentNode.removeChild div
+				newparent.innerHTML = html + newparent.innerHTML
+				setTimeout ->
+					$(".hidden", newparent).removeClass('hidden')
+					if newparent.children.length > DONE_TRACKS_LIMIT
+						end = newparent.children[newparent.children.length - 1]
+						$(end).addClass('hidden')
+						setTimeout ->
+							newparent.removeChild end
+						, 1000
+				, 100
+			, 1400

 class Titular
-  char: "\u25b6"
-  constructor: ->
-    @title = document.title
-    @__title = ""
-    @rotation = 0
-    @drawloop()
-
-  drawloop: ->
-    @draw()
-    me = this
-    return if @stop?
-    setTimeout((-> me.drawloop()), 400)
-
-  draw: (playing) ->
-    if not playing?
-      s = window.soundManager.sounds.ui360Sound0
-      playing = (s? and s.playState == 1 and not s.paused)
-    document.title = @title
-
-  rot: (title) ->
-    if title != @__title
-      @rotation = 0
-      @__title = title
-    if @rotation == @__title.length
-      @rotation = 0
-    r = @__title[@rotation...@__title.length] + " " + @__title[0...@rotation]
-    @rotation += 1
-    r
+	char: "\u25b6"
+	constructor: ->
+		@title = document.title
+		@__title = ""
+		@rotation = 0
+		@drawloop()
+
+	drawloop: ->
+		@draw()
+		me = this
+		return if @stop?
+		setTimeout((-> me.drawloop()), 400)
+
+	draw: (playing) ->
+		if not playing?
+			s = window.soundManager.sounds.ui360Sound0
+			playing = (s? and s.playState == 1 and not s.paused)
+		document.title = @title
+
+	rot: (title) ->
+		if title != @__title
+			@rotation = 0
+			@__title = title
+		if @rotation == @__title.length
+			@rotation = 0
+		r = @__title[@rotation...@__title.length] + " " + @__title[0...@rotation]
+		@rotation += 1
+		r

 window.replace_h2 = (tag, text) ->
-  return if tag.html() == text
-  tag.css 'opacity', 0
-  setTimeout ->
-    tag.html(text)
-    tag.css 'opacity', 1
-  , 500
+	return if tag.html() == text
+	tag.css 'opacity', 0
+	setTimeout ->
+		tag.html(text)
+		tag.css 'opacity', 1
+	, 500

 format_uptime = (seconds) ->
-  hours = Math.round(seconds / 3600)
-  return "#{hours} hour#{if hours == 1 then '' else 's'}" if hours < 24
-  days = Math.round(seconds / 86400)
-  return "#{days} day#{if days == 1 then '' else 's'}" if days < 7
-  weeks = Math.round(seconds / 604800)
-  return "#{weeks} week#{if weeks == 1 then '' else 's'}" if weeks < 4
-  months = Math.round(seconds / 2419200)
-  return "#{months} month#{if months == 1 then '' else 's'}" if months < 12
-  years = Math.round(seconds / 29030400)
-  return "#{years} year#{if years == 1 then '' else 's'}"
+	hours = Math.round(seconds / 3600)
+	return "#{hours} hour#{if hours == 1 then '' else 's'}" if hours < 24
+	days = Math.round(seconds / 86400)
+	return "#{days} day#{if days == 1 then '' else 's'}" if days < 7
+	weeks = Math.round(seconds / 604800)
+	return "#{weeks} week#{if weeks == 1 then '' else 's'}" if weeks < 4
+	months = Math.round(seconds / 2419200)
+	return "#{months} month#{if months == 1 then '' else 's'}" if months < 12
+	years = Math.round(seconds / 29030400)
+	return "#{years} year#{if years == 1 then '' else 's'}"

 window.rotate_h2 = ->
-  tag = $('h2')
-  window.__original_h2 = tag.html() if not window.__original_h2?
-  setInterval ->
-    toggle = tag.data('toggle')
-    if toggle?
-      switch toggle
-        when 0
-          window.replace_h2 tag, window.__original_h2
-        when 1
-          if window._listeners? and window._listeners > MIN_LISTENERS
-            window.replace_h2 tag, "#{window._listeners} listeners"
-        when 2
-          if window._started_at?
-            uptime = ((+new Date) / 1000) - window._started_at
-            if uptime > 86400
-              window.replace_h2 tag, "up for #{format_uptime uptime}"
-    else
-      toggle = 0
-    toggle = (toggle + 1) % 3
-    tag.data('toggle', toggle)
-  , 5000
+	tag = $('h2')
+	window.__original_h2 = tag.html() if not window.__original_h2?
+	setInterval ->
+		toggle = tag.data('toggle')
+		if toggle?
+			switch toggle
+				when 0
+					window.replace_h2 tag, window.__original_h2
+				when 1
+					if window._listeners? and window._listeners > MIN_LISTENERS
+						window.replace_h2 tag, "#{window._listeners} listeners"
+				when 2
+					if window._started_at?
+						uptime = ((+new Date) / 1000) - window._started_at
+						if uptime > 86400
+							window.replace_h2 tag, "up for #{format_uptime uptime}"
+		else
+			toggle = 0
+		toggle = (toggle + 1) % 3
+		tag.data('toggle', toggle)
+	, 5000

 $(document).ready ->
-  window.rotate_h2()
-  window.__spinner.spin document.getElementById('content')
-  window.__spinning = true
-  window.__titular = new Titular
-  window.__heartbeat = $('#endpoint_link').attr('href').replace('all.mp3', 'heartbeat')
-
-  $('body').keyup (e) ->
-    s = window.soundManager.sounds.ui360Sound0
-    if e.keyCode == 32
-      if s?
-        s.toggleMute()
-      else
-        window.threeSixtyPlayer.handleClick {target: $('a.sm2_link')[0]}
-
-  $.getJSON "all.json", (segments) ->
-    console.log("InGetJSON")
-    console.log(segments)
-    for segment in segments
-      length = segment.tracks[0].metadata.length
-	  minutes = Math.floor(length/60)
-      seconds = length%60
-      if seconds < 10: seconds = "0" + seconds
-      document.getElementById('artist').innerHTML = window._next_artist = segment.tracks[0].metadata.artist
-      document.getElementById('length').innerHTML = window._next_title = minutes + ":" + seconds
-      window._track_id = segment.tracks[0].metadata.id
-      console.log("Segment" + window._count_getJSON + ": ")
-      console.log(segment)
-      window._count_getJSON++
-
-  getPing = ->
-    start_time = +new Date
-    $.getJSON "timing.json", (data) ->
-      window.ping = start_time - data.time
-  window.getPing = getPing
-  setInterval getPing, TIMING_INTERVAL
-  getPing()
-
-
-  getTrackDetails = ->
-    console.log("You pressed me.")
-    s = io.connect ":8193/info.websocket"
-    s.on 'message', (data) ->
-      if typeof data is "string"
-        data = JSON.parse(data)
-        if data.segment.tracks[0].metadata.id != window._track_id
-          window._track_id = data.segment.tracks[0].metadata.id
-          # TODO Be safe against embedded HTML tags
-          document.getElementById('artist').innerHTML = window._next_artist
-          window._next_artist = data.segment.tracks[0].metadata.artist
-          document.getElementById('artist_next').innerHTML = "Up next: " + window._next_artist
-        console.log("GetTrackDets Data " + window._count_TrackDeets + ": ")
-        console.log(data)
-      if data.listener_count?
-        window._listeners = data.listener_count
-    window._socket = s
-    window._count_TrackDeets++
+	window.rotate_h2()
+	window.__spinner.spin document.getElementById('content')
+	window.__spinning = true
+	window.__titular = new Titular
+	window.__heartbeat = $('#endpoint_link').attr('href').replace('all.mp3', 'heartbeat')

-  setTimeout getTrackDetails, 1000
-  window._count_getJSON = 1
-  if not window._count_TrackDeets?
-    window._count_TrackDeets = 1+	$('body').keyup (e) ->
+		s = window.soundManager.sounds.ui360Sound0
+		if e.keyCode == 32
+			if s?
+				s.toggleMute()
+			else
+				window.threeSixtyPlayer.handleClick {target: $('a.sm2_link')[0]}
+
+	$.getJSON "all.json", (segments) ->
+		console.log("InGetJSON")
+		console.log(segments)
+		for segment in segments
+			length = segment.tracks[0].metadata.length
+			minutes = Math.floor(length/60)
+			seconds = Math.floor(length%60)
+			if seconds < 10
+				seconds = "0" + seconds
+			document.getElementById('artist').innerHTML = window._next_artist = segment.tracks[0].metadata.artist
+			document.getElementById('length').innerHTML = window._next_title = minutes + ":" + seconds
+			window._track_id = segment.tracks[0].metadata.id
+			console.log("Segment" + window._count_getJSON + ": ")
+			console.log(segment)
+			window._count_getJSON++
+
+	getPing = ->
+		start_time = +new Date
+		$.getJSON "timing.json", (data) ->
+			window.ping = start_time - data.time
+	window.getPing = getPing
+	setInterval getPing, TIMING_INTERVAL
+	getPing()
+
+
+	getTrackDetails = ->
+		console.log("You pressed me.")
+		s = io.connect ":8193/info.websocket"
+		s.on 'message', (data) ->
+			if typeof data is "string"
+				data = JSON.parse(data)
+				if data.segment.tracks[0].metadata.id != window._track_id
+					window._track_id = data.segment.tracks[0].metadata.id
+					# TODO Be safe against embedded HTML tags
+					document.getElementById('artist').innerHTML = window._next_artist
+					window._next_artist = data.segment.tracks[0].metadata.artist
+					document.getElementById('artist_next').innerHTML = "Up next: " + window._next_artist
+				console.log("GetTrackDets Data " + window._count_TrackDeets + ": ")
+				console.log(data)
+			if data.listener_count?
+				window._listeners = data.listener_count
+		window._socket = s
+		window._count_TrackDeets++
+
+	setTimeout getTrackDetails, 1000
+	window._count_getJSON = 1
+	if not window._count_TrackDeets?
+		window._count_TrackDeets = 1


Fix ordering of series in merit supply chart

* Sort series only by the visible data

* Prevent NaNs resulting from division-by-zero (when sum(values) === 0)

* Optimise determining the series min, max, and sum by iterating through
  the data only once.

--- app/assets/javascripts/d3/merit_order_hourly_supply.coffee
+++ app/assets/javascripts/d3/merit_order_hourly_supply.coffee
@@ -35,11 +35,6 @@
       @stackedData = @stack(@chartData[1..@chartData.length])
       @totalDemand = [@chartData[0]]

-    getSpikiness: (serie) ->
-      values = serie.future_value()
-
-      Math.abs((d3.max(values) - d3.min(values)) / d3.sum(values))
-
     getLegendSeries: ->
       legendSeries = []
       @series.forEach (serie) =>
@@ -49,8 +44,21 @@
       legendSeries

     getSeries: ->
-      @model.non_target_series().sort (a, b) =>
-        @getSpikiness(a) > @getSpikiness(b)
+      weekNum = this.dateSelect && this.dateSelect.val() || 0
+
+      _.sortBy @model.non_target_series(), (serie) =>
+        values = MeritTransformator.sliceValues(serie.future_value(), weekNum)
+
+        min = max = sum = 0
+
+        for value in values
+          if value < min then min = value
+          if value > max then max = value
+          sum += value
+
+        return 0 if sum == 0
+
+        Math.abs((max - min) / sum)

     drawData: (xScale, yScale, area, line) ->
       @svg.selectAll('path.serie')


Add oembed adapter

Add oembed adapter

--- app/assets/javascripts/embeditor/adapters/oembed.js.coffee
+++ app/assets/javascripts/embeditor/adapters/oembed.js.coffee
@@ -1 +1,17 @@
 class Embeditor.Adapters.Oembed extends Embeditor.Adapter
+    # Override this. Oembed endpoint.
+    @Path = null
+
+    constructor: (@element, options={}) ->
+        @dataOptions = @_extractData()
+        @queryParams = @_buildParams(@dataOptions, options)
+
+        super
+
+
+    swap: ->
+        $.getJSON(
+            Embeditor.Adapters[@constructor.name].Path,
+            _.extend({url: @href}, @queryParams),
+            (data, textStatus, jqXHR) => @element.after(data.html)
+        )


refactor duplicate code that checks multiple conditions and handles all and any (and or) logic checking to single function checkConditionsAndHideShow

--- app/assets/javascripts/hanuman/conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/conditional-logic.js.coffee
@@ -39,26 +39,8 @@
               do (element) ->
                 self.bindConditions(rule.question_id, $(element), condition.operator, condition.answer)
         # hide show questions
-        #self.hideShowQuestions(hideQuestions, ancestorId, $ruleElement)
         if rule.conditions.length > 1
-          conditionMetTracker = []
-          _.each rule.conditions, (condition) ->
-            $conditionElement = $("[data-question-id=" + condition.question_id + "]").find('.form-control')
-            hideQuestions = self.evaluateCondition(condition.operator, condition.answer, self.getValue($conditionElement))
-            conditionMet = !hideQuestions
-            conditionMetTracker.push conditionMet
-          # match type any (or)
-          if matchType == "any"
-            if _.indexOf(conditionMetTracker, true) > 0
-              self.hideShowQuestions(false, ancestorId, $ruleElement)
-            else
-              self.hideShowQuestions(true, ancestorId, $ruleElement)
-          # match type all
-          if matchType == "all"
-            if _.indexOf(conditionMetTracker, false) == -1
-              self.hideShowQuestions(false, ancestorId, $ruleElement)
-            else
-              self.hideShowQuestions(true, ancestorId, $ruleElement)
+          self.checkConditionsAndHideShow(rule.conditions, ancestorId, $ruleElement, matchType)
         else
           self.hideShowQuestions(hideQuestions, ancestorId, $ruleElement)
     return
@@ -75,29 +57,34 @@
         conditions = rule.conditions
         ancestorId = rule.question_id
         matchingCondition = _.where(conditions, {question_id: Number(questionId)})
+        hideQuestions = self.evaluateCondition(operator, answer, self.getValue($triggerElement))
         if matchingCondition.length > 0
           if conditions.length > 1
-            conditionMetTracker = []
-            _.each conditions, (condition) ->
-              $conditionElement = $("[data-question-id=" + condition.question_id + "]").find('.form-control')
-              hideQuestions = self.evaluateCondition(condition.operator, condition.answer, self.getValue($conditionElement))
-              conditionMet = !hideQuestions
-              conditionMetTracker.push conditionMet
-            # match type any (or)
-            if matchType == "any"
-              if _.indexOf(conditionMetTracker, true) > 0
-                self.hideShowQuestions(false, ancestorId, $ruleElement)
-              else
-                self.hideShowQuestions(true, ancestorId, $ruleElement)
-            # match type all
-            if matchType == "all"
-              if _.indexOf(conditionMetTracker, false) == -1
-                self.hideShowQuestions(false, ancestorId, $ruleElement)
-              else
-                self.hideShowQuestions(true, ancestorId, $ruleElement)
+            self.checkConditionsAndHideShow(conditions, ancestorId, $ruleElement, matchType)
           else
-            self.hideShowQuestions(self.evaluateCondition(operator, answer, self.getValue($triggerElement)), ancestor_id, $ruleElement)
+            self.hideShowQuestions(hideQuestions, ancestor_id, $ruleElement)
     return
+
+  checkConditionsAndHideShow: (conditions, ancestorId, $ruleElement, matchType) ->
+    conditionMetTracker = []
+    _.each conditions, (condition) ->
+      $conditionElement = $("[data-question-id=" + condition.question_id + "]").find('.form-control')
+      hideQuestions = self.evaluateCondition(condition.operator, condition.answer, self.getValue($conditionElement))
+      conditionMet = !hideQuestions
+      conditionMetTracker.push conditionMet
+    # match type any (or)
+    if matchType == "any"
+      if _.indexOf(conditionMetTracker, true) > 0
+        self.hideShowQuestions(false, ancestorId, $ruleElement)
+      else
+        self.hideShowQuestions(true, ancestorId, $ruleElement)
+    # match type all
+    if matchType == "all"
+      if _.indexOf(conditionMetTracker, false) == -1
+        self.hideShowQuestions(false, ancestorId, $ruleElement)
+      else
+        self.hideShowQuestions(true, ancestorId, $ruleElement)
+

   #hide or show questions
   hideShowQuestions: (hide_questions, ancestor_id, $ruleElement) ->


Update app/assets/javascripts/reimagine2/mobile_global_nav.coffee

Co-Authored-By: niuage <590a63ab04627224a263e63cbf98a0573891789a@gmail.com>

--- app/assets/javascripts/reimagine2/mobile_global_nav.coffee
+++ app/assets/javascripts/reimagine2/mobile_global_nav.coffee
@@ -1,5 +1,4 @@
 @Reimagine2.mobileGlobalNav = ->
-  # $(".toggle-topbar").on()
   $(document).on "click.fndtn.topbar", '.top-bar .toggle-topbar, [data-topbar] .toggle-topbar', (e) ->
     e.preventDefault()


moved JS functions out of document.ready() in families.js

--- app/assets/javascripts/summer_residents/families.js.coffee
+++ app/assets/javascripts/summer_residents/families.js.coffee
@@ -1,47 +1,48 @@
 SR = window.SummerResidents
-$ ->
-  edit_link = -> $('div.summer_residents_family div.summer_residents_resident a.edit_resident_link')
-  type = (id) -> $("div.summer_residents_family div#summer_residents_resident_"+id+" input#resident_type_"+id).val()
-  update_link = (id) -> $('div.summer_residents_family div.summer_residents_resident a#update_resident_link_'+id)
-  show_link = (id) -> $('div.summer_residents_family div.summer_residents_resident a#show_resident_link_'+id)
-  update_form = (id) -> $('div.summer_residents_family div.summer_residents_resident form#update_resident_form_'+id)
-  delete_link = $('div.summer_residents_family a.delete_family_link')
-  url = (id) -> "/summer_residents/residents/"+id
-  edit_or_new_url = (id) -> if id then (url(id) + "/edit") else (url("new"))

-  delete_link.keep_centered()
+edit_link = -> $('div.summer_residents_family div.summer_residents_resident a.edit_resident_link')
+type = (id) -> $("div.summer_residents_family div#summer_residents_resident_"+id+" input#resident_type_"+id).val()
+update_link = (id) -> $('div.summer_residents_family div.summer_residents_resident a#update_resident_link_'+id)
+show_link = (id) -> $('div.summer_residents_family div.summer_residents_resident a#show_resident_link_'+id)
+update_form = (id) -> $('div.summer_residents_family div.summer_residents_resident form#update_resident_form_'+id)
+delete_link = $('div.summer_residents_family a.delete_family_link')
+url = (id) -> "/summer_residents/residents/"+id
+edit_or_new_url = (id) -> if id then (url(id) + "/edit") else (url("new"))

-  ajax_show = (id, update) ->
-    xhr
-    if update
-      xhr = $.ajax
-        url: url(id),
-        type: if id then "PUT" else "POST",
-        data: update_form(id).serialize()
-    else
-      xhr = $.ajax
-        url:  edit_or_new_url(id)
-        type: "GET",
-        data: "type="+type(id)+"&cancel=true"
-    xhr.done ->
-      xhr.responseText
-      edit_link().click -> ajax_edit this
-    xhr.fail -> SR.ajax_fail(xhr)
-    false
+delete_link.keep_centered()

-  ajax_edit = (elem) ->
-    id = SR.item_id('edit_resident_link', elem.id)
+ajax_show = (id, update) ->
+  xhr
+  if update
+    xhr = $.ajax
+      url: url(id),
+      type: if id then "PUT" else "POST",
+      data: update_form(id).serialize()
+  else
     xhr = $.ajax
       url:  edit_or_new_url(id)
       type: "GET",
-      data: "type="+type(id)
-    xhr.done ->
-      xhr.responseText
-      update_link(id).click -> ajax_show(id, true)
-      show_link(id).click -> ajax_show(id)
-    xhr.fail -> SR.ajax_fail(xhr)
-    false
+      data: "type="+type(id)+"&cancel=true"
+  xhr.done ->
+    xhr.responseText
+    edit_link().click -> ajax_edit this
+  xhr.fail -> SR.ajax_fail(xhr)
+  false

+ajax_edit = (elem) ->
+  id = SR.item_id('edit_resident_link', elem.id)
+  xhr = $.ajax
+    url:  edit_or_new_url(id)
+    type: "GET",
+    data: "type="+type(id)
+  xhr.done ->
+    xhr.responseText
+    update_link(id).click -> ajax_show(id, true)
+    show_link(id).click -> ajax_show(id)
+  xhr.fail -> SR.ajax_fail(xhr)
+  false
+
+$ ->
   edit_link().click -> ajax_edit this

   delete_link.click ->


add comment about smc_gcloud not having deps installed

--- src/smc-hub/smc_gcloud.coffee
+++ src/smc-hub/smc_gcloud.coffee
@@ -11,6 +11,12 @@
 https://github.com/GoogleCloudPlatform/gcloud-node

 npm install --save gcloud
+
+or maybe
+
+npm install --save google-cloud
+
+these days...

 TODO:


cache the bucket, which avoids huge number of python subprocesses

--- src/smc-hub/smc_gcloud.coffee
+++ src/smc-hub/smc_gcloud.coffee
@@ -1124,9 +1124,13 @@
             name : required
         return new Bucket(@, opts.name)

+gcloud_bucket_cache = {}
+
 class Bucket
     constructor: (@gcloud, @name) ->
-        @_bucket = @gcloud._gcloud.storage().bucket(@name)
+        @_bucket = gcloud_bucket_cache[@name]
+        # if not defined, define it:
+        @_bucket ?= gcloud_bucket_cache[@name] = @gcloud._gcloud.storage().bucket(@name)

     dbg: (f) -> @gcloud.dbg("Bucket.#{f}")


Only instantiate `DocumentStoreShareProvider` when using the `DocumentStoreProvider`

--- src/code/client.coffee
+++ src/code/client.coffee
@@ -59,6 +59,7 @@

     # check the providers
     availableProviders = []
+    shareProvider = null
     for provider in @appOptions.providers
       [providerName, providerOptions] = if isString provider then [provider, {}] else [provider.name, provider]
       # merge in other options as needed
@@ -71,6 +72,9 @@
           Provider = allProviders[providerName]
           provider = new Provider providerOptions, @
           @providers[providerName] = provider
+          # if we're using the DocumentStoreProvider, instantiate the ShareProvider
+          if providerName is DocumentStoreProvider.Name
+            shareProvider = new DocumentStoreShareProvider(@, provider)
           if provider.urlDisplayName        # also add to here in providers list so we can look it up when parsing url hash
             @providers[provider.urlDisplayName] = provider
           availableProviders.push provider
@@ -78,7 +82,7 @@
           @alert "Unknown provider: #{providerName}"
     @_setState
       availableProviders: availableProviders
-      shareProvider: new DocumentStoreShareProvider(@, @providers[DocumentStoreProvider.Name])
+      shareProvider: shareProvider

     @appOptions.ui or= {}
     @appOptions.ui.windowTitleSuffix or= document.title


expose scope.adaptiveBackgroundColors

expose scope.adaptiveBackgroundColors

--- src/angular-adaptive-backgrounds.coffee
+++ src/angular-adaptive-backgrounds.coffee
@@ -61,6 +61,7 @@
           paletteSize: 20
           exclude: options.exclude
           success: (colors) ->
+            scope.adaptiveBackgroundColors = colors
             element.css 'backgroundColor', colors.dominant

       childElement = findImage()


[grunt] predefine better `src` for Gettext

--- modules/gettext.coffee
+++ modules/gettext.coffee
@@ -20,9 +20,9 @@
       @gt = new NodeGettext()

       @load = ({
+        localeDir
         cwd = path.join(@localesDir, '/')
-        localeDir
-        src = '**/*.po'
+        src = '{,**/}*.{po,mo}'
         domain = false
         defaultDomain = 'messages'
       }) ->
@@ -36,7 +36,7 @@
           @gt.addTextdomain(resolvedDomain, messages)

       @locales.forEach (locale) =>
-        @load({ localeDir: locale, src: '**/*.po' })
+        @load({ localeDir: locale})

     resolveDomain: (domain) ->
       if domain.charAt(0) == ':' then @gt.textdomain() + domain else domain


Fix one of the cyclomatic complexity warnings

--- src/commandline.coffee
+++ src/commandline.coffee
@@ -118,6 +118,13 @@
     }
     return reporter

+coreReporters =
+    default: require(path.join(thisdir, 'reporters', 'default'))
+    csv: require(path.join(thisdir, 'reporters', 'csv'))
+    jslint: require(path.join(thisdir, 'reporters', 'jslint'))
+    checkstyle: require(path.join(thisdir, 'reporters', 'checkstyle'))
+    raw: require(path.join(thisdir, 'reporters', 'raw'))
+
 # Publish the error report and exit with the appropriate status.
 reportAndExit = (errorReport, options) ->
     strReporter = if options.argv.jslint
@@ -129,26 +136,16 @@
     else
         options.argv.reporter

-    DefaultReporter = require(path.join(thisdir, 'reporters', 'default'))
-    CSVReporter = require(path.join(thisdir, 'reporters', 'csv'))
-    JSLintReporter = require(path.join(thisdir, 'reporters', 'jslint'))
-    CheckstyleReporter = require(path.join(thisdir, 'reporters', 'checkstyle'))
-    RawReporter = require(path.join(thisdir, 'reporters', 'raw'))
-
-    SelectedReporter = switch strReporter
-        when undefined, 'default' then DefaultReporter
-        when 'jslint' then JSLintReporter
-        when 'csv' then CSVReporter
-        when 'checkstyle' then CheckstyleReporter
-        when 'raw' then RawReporter
-        else
-            try
-                reporterPath = resolve strReporter, {
-                    basedir: process.cwd()
-                }
-            catch
-                reporterPath = strReporter
-            require reporterPath
+
+    strReporter ?= 'default'
+    SelectedReporter = coreReporters[strReporter] ? do ->
+        try
+            reporterPath = resolve strReporter, {
+                basedir: process.cwd()
+            }
+        catch
+            reporterPath = strReporter
+        require reporterPath

     options.argv.color ?= if options.argv.nocolor then "never" else "auto"