Fix insert-type for operators

Fix insert-type for operators

--- lib/haskell-ghc-mod.coffee
+++ lib/haskell-ghc-mod.coffee
@@ -209,15 +209,15 @@
       'haskell-ghc-mod:insert-type': ({target, detail}) =>
         Util = require './util'
         editor = target.getModel()
-        upi.withEventRange {editor, detail}, ({crange}) =>
+        upi.withEventRange {editor, detail}, ({crange, pos}) =>
           @process.getTypeInBuffer(editor.getBuffer(), crange)
           .then (o) ->
             {type} = o
             n = editor.indentationForBufferRow(o.range.start.row)
             indent = ' '.repeat n * editor.getTabLength()
             {scope, range, symbol} =
-              Util.getSymbolAtPoint editor, o.range.start
-            symbol = "(#{symbol})" if range is 'keyword.operator.haskell'
+              Util.getSymbolAtPoint editor, pos
+            symbol = "(#{symbol})" if scope is 'keyword.operator.haskell'
             pos = [range.start.row, 0]
             editor.setTextInBufferRange [pos, pos],
               indent + symbol + " :: " + type + "\n"


Fix new assignment dragging behavior

Fix new assignment dragging behavior

--- tutor/src/components/course-calendar/task-dnd.cjsx
+++ tutor/src/components/course-calendar/task-dnd.cjsx
@@ -59,7 +59,9 @@
       <GrabbyDots/>
       <a
         href={props.link.pathname}
-        onClick={props.goToBuilder(props.link)} >
+        onClick={props.goToBuilder(props.link)}
+        draggable="false"
+      >
         {props.link.text}
       </a>
     </div>


Remove FE offering sort by title

Remove FE offering sort by title

--- tutor/src/components/new-course/select-course.cjsx
+++ tutor/src/components/new-course/select-course.cjsx
@@ -31,11 +31,7 @@
       TutorRouter.currentParams().sourceId

   getInitialState: ->
-    offerings =
-      sortBy(
-        OfferingsStore.filter(is_concept_coach: NewCourseStore.get('course_type') is 'cc'),
-      'title')
-
+    offerings = OfferingsStore.filter(is_concept_coach: NewCourseStore.get('course_type') is 'cc')
     {offerings}

   onSelect: (id) ->


More testing with Zombie.js.

More testing with Zombie.js.

--- spec/admin-spec.coffee
+++ spec/admin-spec.coffee
@@ -15,5 +15,24 @@
       "should be looking at all clients": (browser)-> assert.equal browser.text("title"), "OAuth Admin -  All Clients"
       "should have table with clients": (browser)-> assert.ok browser.html("table.clients")[0]
       "should have one active client": (browser)-> assert.ok browser.html("tr.active").length > 0
+      "active clients":
+        topic: (browser)-> browser.querySelectorAll("tr.active").toArray()
+        "should include the practice server": (clients)->
+          assert.length clients.filter((e)-> e.querySelector(".name").textContent.trim() == "Practice OAuth Console"), 1
+        "practice server":
+          topic: (clients, browser)->
+            clients.filter((e)-> e.querySelector(".name").textContent.trim() == "Practice OAuth Console")[0]
+          "name":
+            topic: (client)-> client.querySelector(".name")
+            "should show service image": (name)->
+              assert.ok name.querySelector("img[src='http://localhost:8080/oauth/admin/images/oauth-2.png']")
+          "secrets":
+            topic: (client)-> client.querySelector(".secrets")
+            "should show client ID": (secrets)->
+              assert.ok secrets.querySelector("dt:contains('ID') + dd:contains('4d0ee1633321e869ad000001')")
+            "should show client secret": (secrets)->
+              assert.ok secrets.querySelector("dt:contains('Secret') + dd:contains('74e3f0e33203d79f5e2e404e81daab23929a0112b9bea9afbfff7433bbfaa9cb')")
+            "should show redirect URI": (secrets)->
+              assert.ok secrets.querySelector("dt:contains('Redirect') + dd:contains('http://localhost:8080/oauth/admin')")

 ).export(module);


esl documentation

esl documentation

--- lib/esl.coffee
+++ lib/esl.coffee
@@ -1,15 +1,50 @@
-###
-(c) 2010 Stephane Alnet
-Released under the AGPL3 license
-###
+# esl is a client and a server library for FreeSwitch's ESL protocol
+# (c) 2010 Stephane Alnet
+# Released under the AGPL3 license
+#
+
+#### Overview
+# esl is modelled after Node.js' own httpServer and client.
+# It offers two low-level ESL handlers, createClient() and
+# createServer(), and a higher-level CallServer class.
+#
+# For more information about ESL consult the FreeSwitch wiki
+# [Event Socket](http://wiki.freeswitch.org/wiki/Event_Socket)
+#
+# Typically a client would be used to trigger calls asynchronously
+# (for example in a click-to-dial application); this mode of operation is
+# called "inbound" (to FreeSwitch) in the FreeSwitch documentation.
+#
+# A server will handle calls sent to it using the "socket" diaplan
+# application (called "outbound" mode in the FreeSwitch documentation).
+# The server is available at a pre-defined port which
+# the socket application will specify. See
+# [Event Socket Outbound](http://wiki.freeswitch.org/wiki/Event_Socket_Outbound)
+
+#### Usage
+#
+#     esl = require 'esl'
+#
+# (The library is a plain Node.js module so you can also call
+# it from Javascript. All examples are given using CoffeeScript
+# for simplicity.)

 net         = require 'net'
 querystring = require 'querystring'
 util        = require 'util'
 assert      = require 'assert'

-# Change from the calling code to enable debugging.
+# If you ever need to debug esl, set
+#
+#     esl.debug = true
+#
+
 exports.debug = false
+
+#### Headers parser
+# ESL framing contains headers and a body.
+# The header must be decoded first to learn
+# the presence and length of the body.

 parse_header_text = (header_text) ->
   header_lines = header_text.split("\n")
@@ -27,18 +62,28 @@

   return headers

+#### ESL stream parser
+# The ESL parser will parse an incoming ESL stream, whether
+# your code is acting as a client (connected to the FreeSwitch
+# ESL server) or as a server (called back by FreeSwitch due to the
+# "socket" application command).
 class eslParser
   constructor: (@socket) ->
     @body_left = 0
     @buffer = ""

+  # When capturing the body, buffer contains the current data
+  # (text), and body_left contains how many bytes are left to
+  # be read in the body.
   capture_body: (data) ->
+    # As long as the whole body hasn't been received, keep
+    # adding the new data into the buffer.
     if data.length < @body_left
       @buffer    += data
       @body_left -= data.length
       return

-    # Consume the body
+    # Consume the body once it has been fully received.
     body = @buffer + data.substring(0,@body_left)
     extra = data.substring(@body_left)
     @body_left = 0
@@ -48,12 +93,15 @@
     @process @headers, body
     @headers = {}

-    # Re-parse
+    # Re-parse whatever data was left after the body was
+    # fully consumed.
     @capture_headers extra

+  # Capture headers, meaning up to the first blank line.
   capture_headers: (data) ->
     header_end = data.indexOf("\n\n")

+    # Wait until we reach the end of the header.
     if header_end < 0
       @buffer += data
       return
@@ -69,17 +117,19 @@
     # Figure out whether a body is expected
     if @headers["Content-Length"]
       @body_left = @headers["Content-Length"]
-      # Re-parse
+      # Re-parse (and eventually process)
       @capture_body extra

     else
-      # Process the content
+      # Process the (header-only) content
       @process @headers
       @headers = {}

-      # Re-parse
+      # Re-parse whatever data was left after the headers
+      # were fully consumed.
       @capture_headers extra

+  # Dispatch incoming data into the header or body parsers.
   on_data: (data) ->

     # Capture the body as needed
@@ -88,27 +138,36 @@
     else
       return @capture_headers data

+  # For completeness provide an on_end() method.
+  # TODO: it probably should make sure the buffer is empty?
   on_end: () ->

-
+#### ESL request
 class eslRequest
   constructor: (@headers,@body) ->

+#### ESL response and associated API
 class eslResponse
   constructor: (@socket) ->

-  # send (string,hash,function(req,res))
+  # A generic way of sending commands back to FreeSwitch.
+  #
+  #      send (string,hash,function(req,res))
+  #
+  # is normally not used directly.

   send: (command,args,cb) ->
       # Make sure we are the only one receiving command replies
       @socket.removeAllListeners('esl_command_reply')
       @socket.removeAllListeners('esl_api_response')
+      # Register the callback for the proper event types.
       if cb?
         @socket.on 'esl_command_reply', (req,res) ->
           cb(req,res)
         @socket.on 'esl_api_response', (req,res) ->
           cb(req,res)

+      # Send the command out.
       @socket.write "#{command}\n"
       if args?
         for key, value of args
@@ -119,28 +178,41 @@

   end: () -> @socket.end()

-  # Channel-level commands:
-
+  #### Channel-level commands
+
+  # Send an API command, see [Mod commands](http://wiki.freeswitch.org/wiki/Mod_commands)
   api: (command,cb) ->
     @send "api #{command}", null, cb

+  # Send an API command in the background.
+  # The callback will receive the Job UUID (instead of the usual request/response pair).
   bgapi: (command,cb) ->
     @send "bgapi #{command}", null, (req,res) ->
       if cb?
         r = res.header['Reply-Text']?.match /\+OK Job-UUID: (.+)$/
         cb r[1]

-  # Event reception and filtering
-
+  #### Event reception and filtering
+
+  # Request that the server send us events in JSON format.
+  # (For all useful purposes this is the only supported format
+  # in this module.)
+  # For example:
+  #
+  #     res.event_json 'HEARTBEAT'
+  #
   event_json: (events...,cb) ->
     @send "event json #{events.join(' ')}", null, cb

+  # Remove the given event types from the events ACL.
   nixevent: (events...,cb) ->
     @send "nixevent #{events.join(' ')}", null, cb

+  # Remove all events types.
   noevents: (cb) ->
     @send "noevents", null, cb

+  # Generic event filtering
   filter: (header,value,cb) ->
     @send "filter #{header} #{value}", null, cb

@@ -150,17 +222,29 @@
     else
       @send "filter #{header}", null, cb

+  # Send an event into the FreeSwitch event queue.
   sendevent: (event_name,args,cb) ->
     @send "sendevent #{event_name}", args, cb

+  # Authenticate, typically used in a client:
+  #
+  #     client = esl.createClient()
+  #     client.on 'esl_auth_request', (req,res) ->
+  #       res.auth 'ClueCon', (req,res) ->
+  #         # Start sending other commands here.
+  #     client.connect ...
+  #
   auth: (password,cb)       -> @send "auth #{password}", null, cb

+  # connect() and linger() are used in server mode.
   connect: (cb)             -> @send "connect", null, cb    # Outbound mode

   linger: (cb)              -> @send "linger", null, cb     # Outbound mode

+  # Send the exit command to the FreeSwitch socket.
   exit: (cb)                -> @send "exit", null, cb

+  #### Event logging commands
   log: (level,cb) ->
     [level,cb] = [null,level] if typeof(level) is 'function'
     if level?
@@ -170,7 +254,7 @@

   nolog: (cb)                 -> @send "nolog", null, cb

-
+  #### Message sending
   # Send Message (to a UUID)

   sendmsg_uuid: (uuid,command,args,cb) ->
@@ -183,6 +267,9 @@

   sendmsg: (command,args,cb) -> @sendmsg_uuid null, command, args, cb

+  #### Client-mode ("inbound") commands
+  # The target UUID must be specified.
+
   # Execute an application for the given UUID (in client mode)

   execute_uuid: (uuid,app_name,app_arg,cb) ->
@@ -191,6 +278,8 @@
       'execute-app-arg':  app_arg
     @sendmsg_uuid uuid, 'execute', options, cb

+  # Hangup a call
+
   hangup_uuid: (uuid,hangup_cause,cb) ->
     hangup_cause ?= 'NORMAL_UNSPECIFIED'
     options =
@@ -200,7 +289,10 @@
   unicast_uuid: (uuid,args,cb) ->
     @sendmsg_uuid uuid, 'unicast', args, cb

-  # nomedia_uuid:
+  # nomedia_uuid: TODO
+
+  #### Server-mode commands
+  # The target UUID is our (own) call UUID.

   # Execute an application for the current UUID (in server/outbound mode)

@@ -210,9 +302,9 @@

   unicast: (args,cb)              -> @unicast_uuid null, args, cb

-  # nomedia:
-
-
+  # nomedia: TODO
+
+#### Connection Listener (socket events handler)
 # This is modelled after Node.js' http.js

 connectionListener= (socket) ->
@@ -223,6 +315,10 @@
   parser.process = (headers,body) ->
     if exports.debug
       util.log util.inspect headers: headers, body: body
+
+    # Rewrite headers as needed to work around some weirdnesses in
+    # the protocol;
+    # and assign unified event IDs to the ESL Content-Types.

     switch headers['Content-Type']
       when 'auth/request'
@@ -254,20 +350,25 @@
         event = 'esl_api_response'
       else
         event = headers['Content-Type']
+    # Build request and response and send them out.
     req = new eslRequest headers,body
     res = new eslResponse socket
     socket.emit event, req, res
   # Get things started
   @emit 'esl_connect', new eslResponse socket if @emit?

+#### ESL Server
+
 class eslServer extends net.Server
   constructor: (requestListener) ->
     @on 'esl_connect', requestListener
     @on 'connection', connectionListener
     super()

+# You can use createServer(callback) from your code.
 exports.createServer = (requestListener) -> return new eslServer(requestListener)

+#### ESL client
 class eslClient extends net.Socket
   constructor: () ->
     @on 'connect', () -> connectionListener(this)


fix the problem of extra spaces insertion in transform string during editing

--- app/lib/cmx/entity.coffee
+++ app/lib/cmx/entity.coffee
@@ -10,7 +10,15 @@
     setFrame: (@t) ->

     getFrame: ->
-      _.str.trim(@t.replace("translate(0,0)", "").replace("rotate(0)", "").replace("skewX(0)","").replace("scale(1,1)", "").replace(")",") "))
+      # strip defaults and make the transformation human-readable
+      re = /\)([^ ])/
+      _.str.trim(
+        @t.replace("translate(0,0)", "")
+          .replace("rotate(0)", "")
+          .replace("skewX(0)","")
+          .replace("scale(1,1)", "")
+          .replace(re, ") $1")
+      )

     getEffectiveFrame: ->
       frame = []


Remove ember-auth patch for testing

Remove ember-auth patch for testing

--- spec/javascripts/support/ember-auth-patch.coffee
+++ spec/javascripts/support/ember-auth-patch.coffee
@@ -1,13 +0,0 @@
-Em.onLoad 'Ember.Application', (application) ->
-  application.initializer
-    name: "adapter-auth-injection"
-    before: "ember-auth-load"
-
-    initialize: (container, app) ->
-      app.inject 'adapter', 'auth', 'auth:main'
-
-Em.Auth.EmberDataAuthModule.reopen
-  patch: ->
-    DS.RESTAdapter.reopen
-      ajax: (url, type, settings) ->
-        @_super url, type, @auth.get("_strategy").serialize(settings || {})


Test that up.replace accepts :before and :after selectors for prepending and appending

--- spec_app/spec/javascripts/up/flow_spec.js.coffee
+++ spec_app/spec/javascripts/up/flow_spec.js.coffee
@@ -57,9 +57,32 @@
             expect($('.after')).toHaveText('new-after')
             done()

-        it 'prepends instead of replacing when the target has a :before pseudo-selector'
-
-        it 'appends instead of replacing when the target has a :after pseudo-selector'
+        it 'prepends instead of replacing when the target has a :before pseudo-selector', (done) ->
+          @request = up.replace('.middle:before', '/path')
+          @respond()
+          @request.then ->
+            expect($('.before')).toHaveText('old-before')
+            expect($('.middle')).toHaveText('new-middleold-middle')
+            expect($('.after')).toHaveText('old-after')
+            done()
+
+        it 'appends instead of replacing when the target has a :after pseudo-selector', (done) ->
+          @request = up.replace('.middle:after', '/path')
+          @respond()
+          @request.then ->
+            expect($('.before')).toHaveText('old-before')
+            expect($('.middle')).toHaveText('old-middlenew-middle')
+            expect($('.after')).toHaveText('old-after')
+            done()
+
+        it "lets the developer choose between replacing/prepending/appending for each selector", (done) ->
+          @request = up.replace('.before:before, .middle, .after:after', '/path')
+          @respond()
+          @request.then ->
+            expect($('.before')).toHaveText('new-beforeold-before')
+            expect($('.middle')).toHaveText('new-middle')
+            expect($('.after')).toHaveText('old-afternew-after')
+            done()

         it 'executes those script-tags in the response that get inserted into the DOM', (done) ->
           window.scriptTagExecuted = jasmine.createSpy('scriptTagExecuted')
@@ -121,7 +144,7 @@
         implant = -> up.flow.implant('.foo-bar', html)
         expect(implant).toThrowError(/Could not find selector ".foo-bar" in current body/i)

-      it "throws an error if the selector can't be found in te given HTML string", ->
+      it "throws an error if the selector can't be found in the given HTML string", ->
         affix('.foo-bar')
         implant = -> up.flow.implant('.foo-bar', '')
         expect(implant).toThrowError(/Could not find selector ".foo-bar" in response/i)


quit when all windows are closed

quit when all windows are closed

--- src/main.coffee
+++ src/main.coffee
@@ -133,3 +133,7 @@
   electronScreen = electron.screen
   primaryDisplay = electronScreen.getPrimaryDisplay()
   Launcher.show primaryDisplay
+
+# Quit when all windows are closed.
+app.on 'window-all-closed', ->
+  app.quit()


fixed typo italian translation Artisan page (#5822)


Chenzhiran patch 5 Update zh-HANS.coffee (#5306)

* Update zh-HANS.coffee

* fix supprt email address

update china support email with bill@codecombat.com


Fixed minor typo in Chinese (#4561)

Fixed minor typo in Chinese (#4561)

--- app/locale/zh-HANS.coffee
+++ app/locale/zh-HANS.coffee
@@ -971,7 +971,7 @@
     story_subtitle: "从2013年至今，CodeCombat已经从一组小草图成长为一个生动的、蓬勃发展的游戏。"
     story_statistic_1a: "5,000,000+"
     story_statistic_1b: "总玩家"
-    story_statistic_1c: "已经通过CodeCombat开始了他么的编程之旅"
+    story_statistic_1c: "已经通过CodeCombat开始了他们的编程之旅"
     story_statistic_2a: "我们将这个游戏翻译为超过 50 种语言 —— 得益于来自"
     story_statistic_2b: "200+ 个国家"
     story_statistic_3a: "的玩家，他们总共写下了"


fixed Angular scope/apply issue

fixed Angular scope/apply issue

--- src/scripts/controllers/tweetListController.coffee
+++ src/scripts/controllers/tweetListController.coffee
@@ -4,9 +4,19 @@
     $scope.tweets = []
     $scope.tweetLimit = 7;

+    safeApply = (scope, fn) ->
+      if (scope.$$phase || scope.$root.$$phase)
+        fn()
+      else
+        scope.$apply(fn)
+      return
+
     addNewTweet = (tweet) ->
-      $scope.tweets.unshift(tweet)
-      $scope.tweets = $scope.tweets.slice(0, $scope.tweetLimit)
+      safeApply $scope, () ->
+        $scope.tweets.unshift(tweet)
+        $scope.tweets = $scope.tweets.slice(0, $scope.tweetLimit)
+        return
+      return

     twitterwallModelHolder.onSearchValueChanged (newSearchValue) ->
       $location.path ('/3/' + newSearchValue)
@@ -19,12 +29,10 @@
             addNewTweet(tweet)
           return
         )
-        return
       return

     if $routeParams.query && $routeParams.query.length > 0
       twitterwallModelHolder.setSearchValue($routeParams.query)
-      return

-    return @
+    return
 ])


Updating xinetd_conf resource snippet

Updating xinetd_conf resource snippet

--- snippets/xinetd_conf.cson
+++ snippets/xinetd_conf.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe xinetd_conf':
     'prefix': 'xinetd_conf'
-    'body': "# http://inspec.io/docs/reference/resources/xinetd_conf/\ndescribe xinetd_conf('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/xinetd_conf/\ndescribe xinetd_conf.services('${1:service_name}') do\n  it { should${2:_not} ${3:be_disabled} }\n  its('${4:setting}') { should${5:_not} eq '${6:value}' }${7}\nend"


Print episodes

Print episodes

--- lib/add.coffee
+++ lib/add.coffee
@@ -1,7 +1,8 @@
 fs = require('fs')
 unzip = require('unzip')
+xml2js = require 'xml2js'

-{xmlReq, download} = require './utils'
+{xmlReq, download, error} = require './utils'

 KEY = process.env.THETVDB_API_KEY
 API_HOST = 'http://thetvdb.com'
@@ -9,6 +10,14 @@
 BASE_STORE = "#{BASE}/store"

 zip_url = (id) -> "#{API_HOST}/api/#{KEY}/series/#{id}/all/en.zip"
+
+printEpisode = (e) ->
+  id = e['id'][0]
+  season = e['SeasonNumber'][0]
+  number = e['EpisodeNumber'][0]
+  name = e['EpisodeName'][0]
+  aired = e['FirstAired'][0]
+  console.log("   S#{season}E#{number} #{name}, #{aired}")

 add = (id) ->
   console.log('Please specify Series id') unless id
@@ -19,10 +28,15 @@
   zipFile = "#{BASE_STORE}/#{id}.zip"

   download zip_url(id), zipFile, ->
-    console.log 'finished'
     fs.createReadStream(zipFile)
       .pipe(unzip.Extract(path: "#{BASE_STORE}/#{id}"))
       .on 'close', ->
-        console.log 'unzipped'
+        parser = new xml2js.Parser()
+        fs.readFile "#{BASE_STORE}/#{id}/en.xml", (err, data) ->
+          return error(err) if err
+          parser.parseString data, (err, result) ->
+            return error(err) if err
+            episodes = result['Data']['Episode']
+            episodes.forEach(printEpisode)

 module.exports = add


Fix several bug for search including #4

Fix several bug for search including #4

--- lib/provider/search.coffee
+++ lib/provider/search.coffee
@@ -30,23 +30,23 @@

   outputterForProject: (project, items) ->
     projectName = path.basename(project)
-    header = '# ' + projectName
+    console.log "CALLED for #{projectName}"
+    projectHeaderAdded = false
+    currentFile = null
     ({data}) =>
-      if header?
-        items.push({header, projectName, projectHeader: true, skip: true})
-        header = null
+      unless projectHeaderAdded?
+        items.push({header: '# ' + projectName, projectName, projectHeader: true, skip: true})
+        projectHeaderAdded = true

       lines = data.split("\n")

-      currentFilePath = null
       for line in lines when item = @parseLine(line)
         {relativePath} = item
         fullPath = path.join(project, relativePath)

         if currentFile isnt relativePath
           currentFile = relativePath
-          header = "  # " + currentFile
-          headerItem = {header, projectName, filePath: fullPath, skip: true}
+          headerItem = {header: "  # " + currentFile, projectName, filePath: fullPath, skip: true}
           items.push(headerItem)

         item.filePath = fullPath
@@ -62,16 +62,12 @@
     new Promise (resolve) =>
       onFinish = (code) =>
         finished++
-        if finished is projects.length
-          resolve(@items)
-          console.log "#{finished} finished"
-        else
-          console.log "#{finished} yet finished"
+        resolve(@items) if finished is projects.length

       pattern = _.escapeRegExp(@options.word)
-      for project, i in projects
+      for project in projects
         onData = @outputterForProject(project, @items)
-        @searchersRunning.push(@search(pattern, {cwd: project, onData, onFinish}))
+        @search(pattern, {cwd: project, onData, onFinish})

   runCommand: ({command, args, options, onData, onFinish}) ->
     stdout = (output) -> onData({data: output})
@@ -110,32 +106,27 @@
     filterKey = @getFilterKey()
     filter = (items, pattern) ->
       _.filter items, (item) ->
-        if filterKey of item
-          item[filterKey].match(///#{pattern}///i)
-        else
-          # When item has no filterKey, it is special, always displayed.
-          true
+        item.skip or item[filterKey].match(///#{pattern}///i)

     for pattern in words.map(_.escapeRegExp)
       items = filter(items, pattern)

-    nonHeaderItems = _.filter items, (item) -> not item.header?
-    filePaths = _.uniq(_.pluck(nonHeaderItems, "filePath"))
-    projectNames = _.uniq(_.pluck(nonHeaderItems, "projectName"))
+    normalItems = _.filter(items, (item) -> not item.header?)
+    allFilePaths = _.uniq(_.pluck(normalItems, "filePath"))
+    allProjectNames = _.uniq(_.pluck(normalItems, "projectName"))

     items = _.filter items, (item) ->
       if item.header?
         if item.projectHeader
-          item.projectName in projectNames
+          item.projectName in allProjectNames
         else
-          item.filePath in filePaths
+          item.filePath in allFilePaths
       else
         true
-
     items

   viewForItem: (item) ->
-    unless item.text?
+    if item.header?
       item.header
     else
       {text, point} = item


Better usage message.

Better usage message.

--- lib/app.coffee
+++ lib/app.coffee
@@ -59,6 +59,7 @@
     # Create a new Commander instance so tests don't share state.
     new Commander.constructor()
       .version('0.0.0')
+      .usage('[options] [ show | list ] [ QUERY ]')
       .description('1Password command line client.')
       .option('-d, --data <data>', 'path to keychain')
       .option('-r, --raw', 'show raw item data (for bug reports)')


Favor length instead of _.size method.

Favor length instead of _.size method.

--- src/spec/integration/integration-order.spec.coffee
+++ src/spec/integration/integration-order.spec.coffee
@@ -143,7 +143,7 @@
       orderUpdated = body

       expect(orderUpdated).toBeDefined()
-      expect(_.size orderUpdated.shippingInfo.deliveries).toBe 1
+      expect(orderUpdated.shippingInfo.deliveries.length).toBe 1

       done()

@@ -215,7 +215,7 @@

           expect(orderUpdated3).toBeDefined()
           parcels = _.first(orderUpdated3.shippingInfo.deliveries).parcels
-          expect(_.size(parcels)).toBe 2
+          expect(parcels.length).toBe 2
           done()

 ###


Update keymap.cson

Update keymap.cson

--- dotfiles/.atom/keymap.cson
+++ dotfiles/.atom/keymap.cson
@@ -1,7 +1,3 @@
 'atom-text-editor.vim-mode-plus.normal-mode':
   ':': 'vim-mode-plus-ex-mode:open'
   '!': 'vim-mode-plus-ex-mode:toggle-setting'
-
-'body atom-text-editor.vim-mode-plus.autocomplete-active':
-  'tab': 'core:move-down'
-  'shift-tab': 'core:move-up'


Added small, random delay to hounding

Added small, random delay to hounding

--- src/models/user.coffee
+++ src/models/user.coffee
@@ -335,8 +335,11 @@
     logger.logToChannel msg, @slack, attachment, true
   hound: (msg, logger=Logger) ->
     now = moment.tz TIMEZONE
-    @directMessage msg, logger
     @settings?.lastPing = now
+    me = @
+    setTimeout ->
+      me.directMessage msg, logger
+    , 1000 * (Math.floor(Math.random() * 3) + 1)
     Logger.log "Hounded #{@slack} with '#{msg}'"
   hexColor: ->
     hash = 0


grid should check do_stroke before rendering

--- subtree/bokehjs/static/coffee/renderers/guide/grid.coffee
+++ subtree/bokehjs/static/coffee/renderers/guide/grid.coffee
@@ -26,11 +26,8 @@
     safebind(this, @model, 'change', @request_render)

   _draw_grids: (ctx) ->
-<<<<<<< HEAD
     if not @grid_props.do_stroke
       return
-=======
->>>>>>> 1b484bc882efbdc009b201e54db7a66022b89cbb
     [xs, ys] = @mget('grid_coords')
     @grid_props.set(ctx, @)
     for i in [0..xs.length-1]


Checkout and push source directly

Checkout and push source directly

--- lib/git.coffee
+++ lib/git.coffee
@@ -40,7 +40,7 @@
 checkout = (repo, branch) ->
   new Promise (resolve, reject) ->
     ref = null
-    repo.getReference "origin/#{branch}"
+    repo.getReference branch
     .then (reference) ->
       ref = reference
       console.log "ref checkout #{ref}"
@@ -169,7 +169,7 @@
     .then (oid) ->
       tag opts.repo, oid
     .then (tag) ->
-      push opts.repo, tag, opts.ref
+      push opts.repo, opts.ref, opts.ref
     .then () ->
       callback null
     .catch (err) ->


Add GitRepository module reference in git.coffee

--- lib/git.coffee
+++ lib/git.coffee
@@ -1,4 +1,4 @@
-{BufferedProcess} = require 'atom'
+{BufferedProcess, GitRepository} = require 'atom'
 RepoListView = require './views/repo-list-view'
 notifier = require './notifier'


fixed broken '--languages' default filepath, which previously did not work in development mode (= having 'groc' cloned from git and not installed in './node_modules/…')

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -112,7 +112,7 @@

     languages:
       describe: "Path to language definition file."
-      default:  "groc/lib/languages"
+      default:  "#{__dirname}/languages"
       type:     'path'

     silent:


[node_modules_koding][jraphical] required bongo-mixin

--- node_modules_koding/jraphical/lib/jraphical.coffee
+++ node_modules_koding/jraphical/lib/jraphical.coffee
@@ -7,4 +7,5 @@
 @Reply          = require './reply'
 @Attachment     = require './attachment'
 @Message        = require './message'
-@Snapshot       = require './snapshot'+@Snapshot       = require './snapshot'
+do require './bongo-mixin'


sax parser stuff from saturday

sax parser stuff from saturday

--- node_modules_koding/kdml/src/kdml/saxparser.coffee
+++ node_modules_koding/kdml/src/kdml/saxparser.coffee
@@ -1,6 +1,7 @@
 {EventEmitter} = require 'microemitter'

 module.exports = class KDSaxParser extends EventEmitter
+
   constructor:(options)->
     {@api, @prefix} = options  if options.api?
     @api        ?= options
@@ -23,12 +24,14 @@

   createSaxParser =(handlers)->
     {SaxParser} = require 'node-xml'
-    return new SaxParser (cb)=>
+    return new SaxParser (cb)->
       cb[ev] handler  for own ev, handler of handlers

   createErr =(message)->
     return new Error message  if 'string' is typeof message
     return message
+
+  bound: require 'koding-bound'

   warn:(message)->
     @emit 'warning', createErr message
@@ -44,10 +47,6 @@

   resume:-> @parser.resume()

-  bound: require 'koding-bound'
-
   parse:(xmlStr)->
     {parser} = this
-    @parser.parseString xmlStr
-
-
+    @parser.parseString xmlStr


Remove this one

Remove this one

--- node_modules_koding/processes/src/processes.coffee
+++ node_modules_koding/processes/src/processes.coffee
@@ -185,28 +185,6 @@
           workerExchange.publish "input.worker", {worker: @workerProcess[name].worker},
             appId: "#{@workerProcess[name].worker.uuid}"

-
-    @on 'childData',(msg) =>
-      onMessage msg if "function" is typeof onMessage
-      switch msg
-        when 'startDone'
-          @workerProcess[name].worker.status = 1
-          console.log "[PROCESSES][#{name}] received 'startDone' message from child_process. Worker is ready and running." if verbose
-        when 'stopDone'
-          @workerProcess[name].worker.status = 0
-          console.log "[PROCESSES][#{name}] received 'stopDone' message from child_process." if verbose
-          console.log "[PROCESSES][#{name}] sending SIGSTOP to the child_process. Worker is stopped." if verbose
-          @child.kill 'SIGSTOP'
-
-    @on 'monitorData', (data) =>
-      # console.log "[PROCSSES][#{name}] Got monitor data from child process" if verbose
-      delete data['process-monitor']
-      data['uuid'] = @workerProcess[name].worker.uuid
-      @workerProcess[name].fetchWorkerExchange (workerExchange) =>
-        @workerProcess[name].worker.message.command = "ack"
-        workerExchange.publish "input.worker", {monitor: data},
-          appId: "#{@workerProcess[name].worker.uuid}"
-
     ################################
     ### HANDLE SIGNALS and KILLS ###
     ################################


constructor

constructor

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -1,4 +1,8 @@
 @Editor = class Editor
+	articleCreateURL: ''
+	articleSaveURL: ''
+	articleDeleteURL: ''
+	id: ''
 	promptMessage: 'Type your article here'
 	status:
 		cmd: false
@@ -59,7 +63,9 @@
 			$(@).attr 'name', self.rand()
 		$('#debug').text $('#editor').html()
 	checkNew: ->
-		@status.new = false if $('#editor').text().length > 5
+		if $('#editor').text().length > 5
+			@status.new = false
+			$('#editor').trigger 'askid'
 	checkEmpty: ->
 		@status.empty = $('#editor').text() == '' || $('#editor').html() == @promptMessage
 	toggleFormatBlock: (tag)->
@@ -125,8 +131,14 @@
 			when 91
 				@status.cmd = false
 				$('.cmd').addClass('hidden')
+	constructor: (options={})->
+		@id = options.id
+		@status.new = !@id
+		@articleCreateURL = options.articleCreateURL
+		@articleSaveURL = options.articleSaveURL
+		@articleDeleteURL = options.articleDeleteURL

-	init: ->
+	init: ()->
 		@update()
 		@displayPrompt()

@@ -141,6 +153,8 @@
 			@selection()
 		.on 'mouseup', (e)=>
 			@selection()
+		.on 'askid', ->
+			console.log 'askid'
 		.blur =>
 			@checkEmpty()
 			@displayPrompt() if @status.empty


janitor: add comment for schema

janitor: add comment for schema

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -146,8 +146,14 @@
       lastLoginDate :
         type        : Date
         default     : -> new Date
+
+      # store fields for janitor worker. see go/src/koding/db/models/user.go for more details.
       inactive      : Object
+
+      # stores user preference for how often email should be sent.
+      # see go/src/koding/db/models/user.go for more details.
       emailFrequency: Object
+
       onlineStatus  :
         actual      :
           type      : String


Return early if a direct liberty is found (no expensive search)

* 137 --> 180
* 57 --> 69
* 19 --> 23

--- lib/javascripts/move.coffee
+++ lib/javascripts/move.coffee
@@ -53,6 +53,9 @@
     _.any unvisited_neighbours, (stone)->
       is_liberty(stone, board, visited_map, color)

+  direct_liberty = _.any neighbouring_stones(stone.x, stone.y, board), (stone)->
+    stone.color == EMPTY
+  return true if direct_liberty
   visited_map = initBoard(board.length)
   visit stone, visited_map
   search_for_liberties(stone.x, stone.y, board, visited_map, stone.color)


followable: static follow/unfollow events

--- workers/social/lib/social/traits/followable.coffee
+++ workers/social/lib/social/traits/followable.coffee
@@ -141,13 +141,17 @@
               if err then log err
             action = "follow"
             @emit 'FollowCountChanged',
-              followerCount   : @getAt('counts.followers')
-              followingCount  : @getAt('counts.following')
+              followerCount   : @getAt 'counts.followers'
+              followingCount  : @getAt 'counts.following'
               follower        : follower
               action          : action

+            @constructor.emit 'FollowHappened',
+              followee  : this
+              follower  : follower
+
             @emit 'FollowHappened',
-              origin    : @
+              origin    : this
               actorType : 'follower'
               follower  : ObjectRef(follower).data

@@ -175,6 +179,11 @@
           throw err if err
         callback err, count
         action = "unfollow"
+
+        @constructor.emit 'UnfollowHappened',
+          followee        : this
+          follower        : follower
+
         @emit 'FollowCountChanged',
           followerCount   : @getAt('counts.followers')
           followingCount  : @getAt('counts.following')


help + ignore self

help + ignore self

--- botart.coffee
+++ botart.coffee
@@ -79,7 +79,7 @@
 dispatch = (message) ->
   for pair in handlers
     [ pattern, handler ] = pair
-    if match = message.message.match(pattern)
+    if message.user.username isnt username and match = message.message.match(pattern)
       message.match = match
       message.say = (thing) -> say(message.topic.id, thing)
       handler(message)
@@ -122,6 +122,9 @@
 hear /about/, (message) ->
   message.say "I am learning to love."

+hear /help/, (message) ->
+  message.say "I listen for '@#{username} image me PHRASE' and '@#{username} wiki me PHRASE'"
+
 hear /wiki me (.*)/i, (message) ->
   term = escape(message.match[1])
   url  = "http://en.wikipedia.org/w/api.php?action=opensearch&search=#{term}&format=json"


Improve popup passthrough check performance

Commit 468ed83 sure made the popup passthrough check more robust.
However, querying for all popups (hundreds) and looping them through on
each keypress is not optimal for performance. The solution? Use the best
of both of the old and new passthrough check.

Looking back, the issue with the old event-based passthrough check was
not that passthrough sometimes wasn’t triggered. It was that passthrough
sometimes got stuck, effectively disabling the extension. So it is not
the 'popupshowing' event that is unreliable, it is the 'popuphidden'
event. So we now use the events for toggling passthrough mode, just like
before. But when `popupPassthrough == true`, we don’t trust it. Then we
loop through all popups to see if any of them actually is open. This
means that it is only when a popup (might) be open that we use the more
expensive check, which is a good tradeoff. Even then, you probably won’t
even notice a lag or anything.

--- extension/packages/events.coffee
+++ extension/packages/events.coffee
@@ -18,6 +18,15 @@
     return keyStr

   return null
+
+# When a menu or panel is shown VimFx should temporarily stop processing keyboard input, allowing
+# accesskeys to be used.
+popupPassthrough = false
+checkPassthrough = (event) ->
+  if event.target.nodeName in ['menupopup', 'panel']
+    popupPassthrough = switch event.type
+      when 'popupshown'  then true
+      when 'popuphidden' then false

 suppress = false
 suppressEvent = (event) ->
@@ -42,10 +51,16 @@

       return if getPref('disabled')

-      return unless rootWindow = utils.getEventRootWindow(event)
-      popups = rootWindow.document.querySelectorAll('menupopup, panel')
-      for popup in popups
-        return if popup.state == 'open'
+      if popupPassthrough
+        # The `popupPassthrough` flag is set a bit unreliably. Sometimes it can be stuck as `true`
+        # even though no popup is shown, effectively disabling the extension. Therefore we check
+        # if there actually _are_ any open popups before stopping processing keyboard input. This is
+        # only done when popups (might) be open (not on every keystroke) of performance reasons.
+        return unless rootWindow = utils.getEventRootWindow(event)
+        popups = rootWindow.document.querySelectorAll('menupopup, panel')
+        for popup in popups
+          return if popup.state == 'open'
+        popupPassthrough = false # No popup was actually open: Reset the flag.

       return unless window = utils.getEventCurrentTabWindow(event)
       return unless vim = vimBucket.get(window)
@@ -68,6 +83,9 @@
   # highest priority.
   keypress: suppressEvent
   keyup:    suppressEvent
+
+  popupshown:  checkPassthrough
+  popuphidden: checkPassthrough

   # When the top level window closes we should release all Vims that were
   # associated with tabs in this window


fixes #20

fixes #20

--- lib/line-diff-worker.coffee
+++ lib/line-diff-worker.coffee
@@ -30,7 +30,6 @@
             line = parseInt $(event.target).text()
             return if isNaN(line)
             details = @calculateDiffDetails(line)
-            console.log line
             @decorateDiffMarkers(details)

     clearMarkers: ->
@@ -83,7 +82,6 @@

     calculateDiffDetails: (lineNumber) ->
         fileDiffs = @findFileDiffs()
-        console.log fileDiffs
         lineStuff = {}
         index = 0
         newDiff = @findDiffForLine(lineNumber)


fixing #194

fixing #194

--- src/ClassLoader.coffee
+++ src/ClassLoader.coffee
@@ -128,6 +128,11 @@
   define_class: (rs, type_str, data, success_fn, failure_fn, parallel=false, explicit=false) ->
     trace "Defining class #{type_str}..."
     cdata = new ReferenceClassData(data, @)
+    if (type = cdata.get_type()) isnt type_str
+      msg = "#{util.descriptor2typestr type_str} (wrong name: #{util.descriptor2typestr type})"
+      return failure_fn (=>
+        rs.java_throw @get_initialized_class('Ljava/lang/NoClassDefFoundError;'), msg
+      )
     # Add the class before we fetch its super class / interfaces.
     @_add_class type_str, cdata
     # What classes are we fetching?


BUG-FIX/NEW-FUNC Simplify reserved keywords recognition

--- src/neck.coffee
+++ src/neck.coffee
@@ -34,16 +34,11 @@
 class Neck.Controller extends Backbone.View
   REGEXPS:
     TEXTS: /\'[^\']+\'/g
-    RESERVED_KEYWORDS: new RegExp """
-      ^(do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|
-      void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|
-      return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|
-      function|arguments|interface|protected|implements|instanceof|undefined|window)($|\.)"""
+    RESERVED_KEYWORDS: /(^|\ )(true|false|undefined|null|NaN|window)($|\.|\ )/g
     SCOPE_PROPERTIES: /([a-zA-Z$_\@][^\ \[\]\:\(\)\{\}]*)/g
     TWICE_SCOPE: /(scope\.[^\ ]*\.)scope\./
     OBJECT: /^\{.+\}$/g
     ONLY_PROPERTY: /^[a-zA-Z$_][^\ \(\)\{\}\:]*$/g
-    SLASHES: /\//g

   divWrapper: true
   template: false
@@ -146,7 +141,7 @@

   _setAccessor: (key, value, controller = @parent)->
     scope = controller.scope
-    [value, resolves] = @_parseValue value
+    console.log [value, resolves] = @_parseValue value

     options = enumerable: true, get: ->
       try


value+unit pattern & # pattern & rescoped magic comments

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -18,6 +18,7 @@
   { include: '#metaUnderscore' }
   { include: '#metaCaret' }
   { include: '#metaHashtag' }
+  { include: '#metaOther' } # for specific things that do not fit in the above
 ]

 repository:
@@ -108,6 +109,15 @@
       1: name: 'variable.parameter.hashtag.3.latex'
       2: name: 'variable.parameter.hashtag.2.latex'
       3: name: 'variable.parameter.hashtag.1.latex'
+  }
+  metaOther: {
+    patterns: [
+      {
+        comment: 'Finds value + unit pairs and scopes them for spell checkers.'
+        match: '\\d+(pt|mm|cm|in|ex|em|bp|pc|dd|cc|sp)\\b'
+        name: 'meta.definition.value.latex'
+      }
+    ]
   }

 # Other meta groups
@@ -376,6 +386,7 @@
         patterns: [
           { include: '#controlWord' }
           { include: '#controlSymbol' }
+          { include: '#metaHashtag' }
           {
             match: '[!\u0028-\u007A\u00A1-\u017F\u3001-\u30FF\u0391-\u03CE\u0410-\u044F\u4E00-\u9FFF\uFF0C\uFF0E]'
             name: 'constant.other.reference.citation.latex'
@@ -494,7 +505,8 @@
   }
   magicComment: {
     comment: 'Can be improved to change color depending if the command matches a known string'
-    name: 'keyword.control.magic.latex'
+    name: 'comment.line.percentage.latex'
+    captures: 0: name: 'keyword.control.magic.latex'
     match: '''(?x)   # Allow comments like this. More of an educational thing, really, for anyone reading this.
      ((        # Capture groups 1 & 2
        (%)     # Capture percent sign


General bugfixes

General bugfixes

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -431,7 +431,7 @@
         'name': 'keyword.operator.assignment.stata'
   }
   {
-    'match': '\\b(set ty(pe|p)?)\\s+((byte|int|long|float|double|str[1-9]?[0-9]?[0-9]?[0-9]?|strL)?\\s+)'
+    'match': '\\b(set ty(pe|p)?)\\s+((byte|int|long|float|double|str[1-9]?[0-9]?[0-9]?[0-9]?|strL)?\\s+)\\b'
     'captures':
       '1':
         'name': 'keyword.functions.data.stata'
@@ -439,7 +439,7 @@
         'name': 'support.type.stata'
   }
   {
-    'match': '\\b(la(bel|be|b)?)\\s+(da(ta|t)?|var(iable|iabl|iab|ia|i)?|de(f|fi|fin|fine)?|val(ues|ue|u)?|di(r)?|l(ist|is|i)?|copy|drop|save|lang(uage|uag|ua|u)?)'
+    'match': '\\b(la(bel|be|b)?)\\s+(da(ta|t)?|var(iable|iabl|iab|ia|i)?|de(f|fi|fin|fine)?|val(ues|ue|u)?|di(r)?|l(ist|is|i)?|copy|drop|save|lang(uage|uag|ua|u)?)\\b'
     'captures':
       '1':
         'name': 'keyword.functions.data.stata'
@@ -1199,20 +1199,16 @@
         'name': 'constant.numeric.float.stata'
       }
       {
-        'match': '\\b(?i:([1-9]+[0-9]*|0)L)'
-        'name': 'constant.numeric.integer.long.decimal.stata'
-      }
-      {
-        'match': '\\b([0-9]+|0)'
+        'match': '\\b(\\d+)\\b'
         'name': 'constant.numeric.integer.decimal.stata'
       }
       {
         'match': '(?<![a-zA-Z0-9])(\\.(?![\\./]))(?![a-zA-Z0-9])'
-        'name': 'constant.language.stata'
+        'name': 'constant.language.missing.stata'
       }
       {
         'match': '_all'
-        'name': 'constant.language.stata'
+        'name': 'constant.language.allvars.stata'
       }
     ]
   'operators':


Alert when variable labels are >80 characters. Fixes #72

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -544,6 +544,79 @@
         'name': 'support.type.stata'
   }
   {
+    'match': '\\b(la(bel|be|b)?)\\s+(var(iable|iabl|iab|ia|i)?)\\s+([a-zA-Z_][0-9a-zA-Z_]+)\\s+(`")(.+)("\')'
+    'captures':
+      '1':
+        'name': 'keyword.functions.data.stata'
+      '3':
+        'name': 'keyword.functions.data.stata'
+      '6':
+        'name': 'punctuation.definition.string.begin.stata'
+      '7':
+        'patterns': [
+          {
+            'include': '#string-compound'
+          }
+          {
+            'include': '#macro-local-escaped'
+          }
+          {
+            'include': '#macro-global-escaped'
+          }
+          {
+            'include': '#macro-local'
+          }
+          {
+            'include': '#macro-global'
+          }
+          {
+            'match': '[^`\\$]{81,}'
+            'name': 'invalid.illegal.name.stata'
+          }
+          {
+            'match': '.'
+            'name': 'string.quoted.double.compound.stata'
+          }
+        ]
+      '8':
+        'name': 'punctuation.definition.string.begin.stata'
+  }
+  {
+    'match': '\\b(la(bel|be|b)?)\\s+(var(iable|iabl|iab|ia|i)?)\\s+([a-zA-Z_][0-9a-zA-Z_]+)\\s+(")(.+)(")'
+    'captures':
+      '1':
+        'name': 'keyword.functions.data.stata'
+      '3':
+        'name': 'keyword.functions.data.stata'
+      '6':
+        'name': 'punctuation.definition.string.begin.stata'
+      '7':
+        'patterns': [
+          {
+            'include': '#macro-local-escaped'
+          }
+          {
+            'include': '#macro-global-escaped'
+          }
+          {
+            'include': '#macro-local'
+          }
+          {
+            'include': '#macro-global'
+          }
+          {
+            'match': '[^`\\$]{81,}'
+            'name': 'invalid.illegal.name.stata'
+          }
+          {
+            'match': '.'
+            'name': 'string.quoted.double.stata'
+          }
+        ]
+      '8':
+        'name': 'punctuation.definition.string.begin.stata'
+  }
+  {
     'match': '\\b(la(bel|be|b)?)\\s+(da(ta|t)?|var(iable|iabl|iab|ia|i)?|de(f|fi|fin|fine)?|val(ues|ue|u)?|di(r)?|l(ist|is|i)?|copy|drop|save|lang(uage|uag|ua|u)?)\\b'
     'captures':
       '1':


Patch to previous fix

Patch to previous fix

--- lib/git.coffee
+++ lib/git.coffee
@@ -40,16 +40,19 @@
     stdout: (data) -> stdout(if data.length > 2 then data.split('\0') else [])

 gitStagedFiles = (stdout) ->
-  files = null
+  files = []
   gitCmd
     args: ['diff-index', '--cached', 'HEAD', '--name-status', '-z']
-    stdout: (data) -> files = _prettify(data)
+    stdout: (data) ->
+      files = _prettify(data)
     stderr: (data) ->
       # edge case of no HEAD at initial commit
       if data.toString().contains "ambiguous argument 'HEAD'"
-        stdout [1]
+        files = [1]
       else
-        stdout files
+        new StatusView(type: 'alert', message: data.toString())
+        files = []
+    exit: (code) -> stdout(files)

 gitUnstagedFiles = (stdout, showUntracked=false) ->
   gitCmd


latest

latest

--- lib/nap.coffee
+++ lib/nap.coffee
@@ -60,12 +60,12 @@
 # @param {String} pkg The name of the package to output
 # @return {String} Script tag pointing to the ouput package(s)

-module.exports.js = (pkg, gzip = @gzip) =>
+module.exports.js = (pkg) =>
   throw new Error "Cannot find package '#{pkg}'" unless @assets.js[pkg]?

   if @mode is 'production'
     src = (@cdnUrl ? @_assetsDir) + '/' + pkg + '.js'
-    src += '.jgz' if gzip
+    src += '.jgz' if @acceptsGzip
     return "<script src='#{src}' type='text/javascript'></script>"

   output = ''
@@ -80,12 +80,12 @@
 # @param {String} pkg The name of the package to output
 # @return {String} Style tag pointing to the ouput package(s)

-module.exports.css = (pkg, gzip = @gzip) =>
+module.exports.css = (pkg) =>
   throw new Error "Cannot find package '#{pkg}'" unless @assets.css[pkg]?

   if @mode is 'production'
     src = (@cdnUrl ? @_assetsDir) + '/' + pkg + '.css'
-    src += '.cgz' if gzip
+    src += '.cgz' if @acceptsGzip
     return "<link href='#{src}' rel='stylesheet' type='text/css'>"

   output = ''
@@ -183,6 +183,8 @@

 module.exports.middleware = (req, res, next) =>

+  @gzip = req.headers['accept-encoding'].toLowerCase().indexOf('gzip') isnt -1
+
   return unless @mode is 'development'

   @usingMiddleware = true


fix bad res.send

fix bad res.send

--- services/spelling/app/coffee/SpellingAPIController.coffee
+++ services/spelling/app/coffee/SpellingAPIController.coffee
@@ -10,8 +10,7 @@
 			if error?
 				logger.err err:error, user_id:req?.params?.user_id, word_count: req?.body?.words?.length, "error processing spelling request"
 				return res.sendStatus(500)
-			# res.send(result)
-			res.writeHead 200
+			res.send(result)

 	learn: (req, res, next) ->
 		metrics.inc "spelling-learn", 0.1


Adds tests for expression component id capabilities

--- math/expression_components_spec.coffee
+++ math/expression_components_spec.coffee
@@ -7,6 +7,8 @@
     it "returns the correct repsonse for isOperator", ->
       expect(@comp.isOperator()).toEqual opts.is_operator

+    it "responds to ID", ->
+      expect(@comp.id()).toEqual opts.id

 describe "Expression Components", ->
   beforeEach ->
@@ -18,8 +20,9 @@
   describe "Expression", ->
     it_adheres_to_the_expression_component_interface {
       instance_fn: ->
-        @comps.build_expression()
+        @comps.build_expression(id: 9876)
       is_operator: false
+      id: 9876
     }

     it "assigns components from its construtor", ->
@@ -79,16 +82,18 @@
   describe "division", ->
     it_adheres_to_the_expression_component_interface {
       instance_fn: ->
-        @comps.classes.division.build()
+        @comps.classes.division.build(id: 12345)
       is_operator: true
-      }
+      id: 12345
+    }

   describe "variables", ->
     it_adheres_to_the_expression_component_interface {
       instance_fn: ->
-        @comps.classes.variable.build(name: "example")
+        @comps.classes.variable.build(name: "example", id: 678)
       is_operator: false
-      }
+      id: 678
+    }

     it "will tell you its name", ->
       @variable = @comps.classes.variable.build(name: "doot")


Change render

Change render

--- source/core/output.coffee
+++ source/core/output.coffee
@@ -34,20 +34,18 @@
   ###
   output: ->
     throw "No template defined." unless @constructor.template?
-    throw "No parent assigned." unless @attributes?.parent?
+    throw "No parent assigned." unless @parent?

     @_createIfBindings()
     # Public attributes
-    @el = Atoms.$(@_mustache(@constructor.template)(@attributes))
+    @el = Atoms.$(_mustache(@constructor.template)(@attributes))
     @el.attr "data-#{@constructor.type}", @constructor.name.toLowerCase()
     # Attributes for constructor
     @constructor.method =  @attributes.method or Atoms.Core.Constants.APPEND

-    if typeof(@attributes.parent) is "string" or not @attributes.parent.length?
-      @attributes.parent = Atoms.$(@attributes.parent)
-    @attributes.parent.first()[@constructor.method] @el
-    @attributes.parent = null
-    delete @attributes.parent
+    if typeof(@parent.el) is "string" or not @parent.el.length?
+      @parent.el = Atoms.$(@parent.el)
+    @parent.el.first()[@constructor.method] @el

   # Private Methods
   _createIfBindings: ->
@@ -55,38 +53,38 @@
     for key of @attributes when key not in Atoms.Core.Constants.EXCLUDED_IF_KEYS
       @attributes.if[key] = true if @attributes[key]?

-  ###
-  The fastest and smallest Mustache compliant Javascript templating library
-  templayed.js 0.2.1 - (c) 2012 Paul Engel
-  http://archan937.github.io/templayed.js/
-  ###
-  _mustache: (template, data) ->
-    get = (path, i) ->
-      i = 1
-      path = path.replace(/\.\.\//g, ->
-        i++
-        ""
-      )
-      js = ["data[data.length - ", i, "]"]
-      keys = ((if path is "." then [] else path.split(".")))
-      index = 0
-      while index < keys.length
-        js.push "." + keys[index]
-        index++
-      js.join ""
+###
+The fastest and smallest Mustache compliant Javascript templating library
+templayed.js 0.2.1 - (c) 2012 Paul Engel
+http://archan937.github.io/templayed.js/
+###
+_mustache = (template, data) ->
+  get = (path, i) ->
+    i = 1
+    path = path.replace(/\.\.\//g, ->
+      i++
+      ""
+    )
+    js = ["data[data.length - ", i, "]"]
+    keys = ((if path is "." then [] else path.split(".")))
+    index = 0
+    while index < keys.length
+      js.push "." + keys[index]
+      index++
+    js.join ""

-    tag = (template) ->
-      template.replace /\{\{(!|&|\{)?\s*(.*?)\s*}}+/g, (match, operator, context) ->
-        return ""    if operator is "!"
-        i = inc++
-        ["\"; var o", i, " = ", get(context), ", s", i, " = (((typeof(o", i, ") == \"function\" ? o", i, ".call(data[data.length - 1]) : o", i, ") || \"\") + \"\"); s += ", ((if operator then ("s" + i) else "(/[&\"><]/.test(s" + i + ") ? s" + i + ".replace(/&/g,\"&\").replace(/\"/g,\""\").replace(/>/g,\">\").replace(/</g,\"<\") : s" + i + ")")), " + \""].join ""
+  tag = (template) ->
+    template.replace /\{\{(!|&|\{)?\s*(.*?)\s*}}+/g, (match, operator, context) ->
+      return ""    if operator is "!"
+      i = inc++
+      ["\"; var o", i, " = ", get(context), ", s", i, " = (((typeof(o", i, ") == \"function\" ? o", i, ".call(data[data.length - 1]) : o", i, ") || \"\") + \"\"); s += ", ((if operator then ("s" + i) else "(/[&\"><]/.test(s" + i + ") ? s" + i + ".replace(/&/g,\"&\").replace(/\"/g,\""\").replace(/>/g,\">\").replace(/</g,\"<\") : s" + i + ")")), " + \""].join ""

-    block = (template) ->
-      tag template.replace(/\{\{(\^|#)(.*?)}}(.*?)\{\{\/\2}}/g, (match, operator, key, context) ->
-        i = inc++
-        ["\"; var o", i, " = ", get(key), "; ", ((if operator is "^" then ["if ((o", i, " instanceof Array) ? !o", i, ".length : !o", i, ") { s += \"", block(context), "\"; } "] else ["if (typeof(o", i, ") == \"boolean\" && o", i, ") { s += \"", block(context), "\"; } else if (o", i, ") { for (var i", i, " = 0; i", i, " < o", i, ".length; i", i, "++) { data.push(o", i, "[i", i, "]); s += \"", block(context), "\"; data.pop(); }}"])).join(""), "; s += \""].join ""
-      )
+  block = (template) ->
+    tag template.replace(/\{\{(\^|#)(.*?)}}(.*?)\{\{\/\2}}/g, (match, operator, key, context) ->
+      i = inc++
+      ["\"; var o", i, " = ", get(key), "; ", ((if operator is "^" then ["if ((o", i, " instanceof Array) ? !o", i, ".length : !o", i, ") { s += \"", block(context), "\"; } "] else ["if (typeof(o", i, ") == \"boolean\" && o", i, ") { s += \"", block(context), "\"; } else if (o", i, ") { for (var i", i, " = 0; i", i, " < o", i, ".length; i", i, "++) { data.push(o", i, "[i", i, "]); s += \"", block(context), "\"; data.pop(); }}"])).join(""), "; s += \""].join ""
+    )

-    inc = 0
-    new Function("data", "data = [data], s = \"" + block(template.replace(/"/g, "\\\"").replace(/\n/g, "\\n")) + "\"; return s;")
+  inc = 0
+  new Function("data", "data = [data], s = \"" + block(template.replace(/"/g, "\\\"").replace(/\n/g, "\\n")) + "\"; return s;")


run: previous log filename

run: previous log filename

--- lib/run.coffee
+++ lib/run.coffee
@@ -122,7 +122,7 @@
     error = false
     context.call ->
       @log.cli host: context.config.host, pad: host: 20, header: 60
-      @log.md filename: context.config.host, basedir: './log'
+      @log.md basename: context.config.shortname
       for id in context.services then do (id) =>
         service = config.services[id]
         return if !service.required and params.modules and multimatch(service.module, params.modules).length is 0


Request error messages shows full URL rather than the first character.

--- lib/zombie/resources.coffee
+++ lib/zombie/resources.coffee
@@ -263,7 +263,7 @@

     Request params, (error, response)=>
       if error
-        browser.log -> "#{method} #{URL.format(url).slice("#")[0]} => #{error.message}"
+        browser.log -> "#{method} #{URL.format(url).split("#")[0]} => #{error.message}"
         callback error
         return


fix dragging event multiple times

fix dragging event multiple times

--- app/assets/javascripts/bookings/application.js.coffee
+++ app/assets/javascripts/bookings/application.js.coffee
@@ -15,10 +15,11 @@

 @FormHandler =
   setEvent: (event, data)->
+    console.log event
     event.recurring = data.recurring if data.recurring
     event.id = data.id if data.id
-    event.start = moment(data.from_time).format("MMM DD, YYYY HH:mm Z")
-    event.end =   moment(data.to_time).format("MMM DD, YYYY HH:mm Z")
+    event.start = new Date(moment(data.from_time).format("MMM DD, YYYY HH:mm Z"))
+    event.end =  new Date(moment(data.to_time).format("MMM DD, YYYY HH:mm Z"))
     console.log event
     event

@@ -45,16 +46,17 @@

     unless event.isNew # refresh events if update
       calendar.fullCalendar 'rerenderEvents'
+    else
+      event.isNew = false
+      calendar.fullCalendar "renderEvent",
+        title: data.id.toString()
+        start: moment(event.start).format("MMM DD, YYYY HH:mm Z")
+        end: moment(event.end).format("MMM DD, YYYY HH:mm Z")
+        id: data.id
+        recurring: data.recurring
+        allDay: false
+      , true # make the event "stick"

-    event.isNew = false
-    calendar.fullCalendar "renderEvent",
-      title: data.id.toString()
-      start: moment(event.start).format("MMM DD, YYYY HH:mm Z")
-      end: moment(event.end).format("MMM DD, YYYY HH:mm Z")
-      id: data.id
-      recurring: data.recurring
-      allDay: false
-    , true # make the event "stick"
     $('#template_form').css('display', 'block')
     calendar.fullCalendar "unselect"
     event


oops: delete misplaced question mark

oops: delete misplaced question mark

--- app/assets/javascripts/components/subject-viewer.cjsx
+++ app/assets/javascripts/components/subject-viewer.cjsx
@@ -167,7 +167,6 @@
       @destroyMark @props.annotation, mark

     mark.isUncommitted = true
-    console.log 'MARK: ', mark
     @setUncommittedMark mark

   setUncommittedMark: (mark) ->
@@ -230,12 +229,13 @@

   getCurrentMarks: ->
     # Previous marks are really just the region hashes of all child subjects
+    console.log 'CHILD SUBHECTS: ', @props.subject.child_subjects
     marks = []
     for child_subject, i in @props.subject.child_subjects
       child_subject.region.subject_id = child_subject.id # copy id field into region (not ideal)
       marks[i] = child_subject.region
       marks[i].isTranscribable = !child_subject.user_has_classified && child_subject.status != "retired"
-      marks[i].belongsToUser = child_subject.user_has_classified?
+      marks[i].belongsToUser = child_subject.user_has_classified
     # marks = (s for s in (@props.subject.child_subjects ? [] ) when s?.region?).map (m) ->
     #   # {userCreated: false}.merge
     #   m?.region ? {}
@@ -251,29 +251,24 @@
     otherMarks = []
     for mark in marks
       if mark.isTranscribable
-        console.log 'TRANSCRIBABLE: ', mark
         transcribableMarks.push mark
       else
-        console.log 'OTHER: ', mark
         otherMarks.push mark
-
-    console.log '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MARKS: ', transcribableMarks, otherMarks

     console.log '{transcribableMarks, otherMarks} = ', {transcribableMarks, otherMarks}
     return {transcribableMarks, otherMarks}

   renderMarks: (marks) ->
-    console.log 'renderMarks() ', marks
+    console.log 'renderMarks(), marks: ', marks
     return unless marks.length > 0
     scale = @getScale()
     marksToRender = for mark in marks
-      console.log 'mark.isUncommited? = ', mark.isUncommitted?
       mark._key ?= Math.random()
       continue if ! mark.x? || ! mark.y? # if mark hasn't acquired coords yet, don't draw it yet

-      # hide others' marks, if button is toggled
-      console.log 'KLJDHSKLJDHSKLJDHS: ', @props.hideOtherMarks
-      continue unless mark.belongsToUser if @props.hideOtherMarks
+      console.log 'MARK BELONGS TO USER? ', mark.belongsToUser
+      if @props.hideOtherMarks
+        continue unless mark.belongsToUser

       isPriorMark = ! mark.userCreated
       <g key={mark._key} className="marks-for-annotation" data-disabled={isPriorMark or null}>


Revert "Refactored .prev and .next methods"

This reverts commit ec37d9553274e3bca58e4de44b932cdc72a37325.

--- app/assets/javascripts/files_comment_button.js.coffee
+++ app/assets/javascripts/files_comment_button.js.coffee
@@ -7,7 +7,6 @@
   UNFOLDABLE_LINE_CLASS = 'js-unfold'
   EMPTY_CELL_CLASS = 'empty-cell'
   OLD_LINE_CLASS = 'old_line'
-  NEW_CLASS = 'new'
   LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
   TEXT_FILE_SELECTOR = '.text-file'
   DEBOUNCE_TIMEOUT_DURATION = 100
@@ -64,20 +63,17 @@
   getLineContent: (hoveredElement) ->
     return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS

-    $(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
+    $(hoveredElement).next ".#{LINE_CONTENT_CLASS}"

   getButtonParent: (hoveredElement) ->
     if @VIEW_TYPE is 'inline'
       return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS

-      $(".#{OLD_LINE_CLASS}", hoveredElement.parent())
+      hoveredElement.parent().find ".#{OLD_LINE_CLASS}"
     else
       return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS

-      $(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
-
-  diffTypeClass: (hoveredElement) ->
-    if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
+      $(hoveredElement).prev ".#{LINE_NUMBER_CLASS}"

   isMovingToSameType: (e) ->
     newButtonParent = @getButtonParent $(e.toElement)


Fix merging of parameters for granule queries

--- app/assets/javascripts/models/data/granules.js.coffee
+++ app/assets/javascripts/models/data/granules.js.coffee
@@ -24,7 +24,7 @@
     params: ->
       parentParams = @parentQuery.params()
       params = {}
-      for param in ['spatial', 'temporal']
+      for param in ['bounding_box', 'point', 'line', 'polygon', 'temporal']
         parentValue = parentParams[param]
         params[param] = parentValue if parentValue?
       extend(params, @query.params())


Orders of magnitude faster rendering

Orders of magnitude faster rendering

--- app/assets/javascripts/player/terminal_view.js.coffee
+++ app/assets/javascripts/player/terminal_view.js.coffee
@@ -37,41 +37,51 @@
       @renderLine n, data || [], c

   renderLine: (n, data, cursorX) ->
-    html = []
+    html = ''
     i = 0
+    prevBrush = undefined

     for d in data
       if d
         [char, brush] = d
-        html[i] = @createSpan(char, brush, i is cursorX)
+
+        if brush != prevBrush or i is cursorX or i is (cursorX + 1)
+          if prevBrush
+            html += ''
+
+          html += @createSpan(brush, i is cursorX)
+
+          prevBrush = brush
+
+        html += char
       else
-        html[i] = ' '
+        html += ' '
       i++

-    @$el.find(".line:eq(" + n + ")").html html.join("")
+    html += '' if html.length > 0

-  createSpan: (char, brush, hasCursor) ->
-    prefix = ""
-    postfix = ""
+    @$el.find(".line:eq(" + n + ")").html html #.join("")
+
+  createSpan: (brush, hasCursor) ->
+    span = ""

     if hasCursor or brush.fg isnt undefined or brush.bg isnt undefined or brush.bright or brush.underline
-      prefix = "<span class=\""
+      span = "<span class=\""
       brightOffset = (if brush.bright then 8 else 0)

       if brush.fg isnt undefined
-        prefix += " fg" + (brush.fg + brightOffset)
+        span += " fg" + (brush.fg + brightOffset)
       else if brush.bright
-        prefix += " bright"
+        span += " bright"

       if brush.underline
-        prefix += " underline"
+        span += " underline"

-      prefix += " bg" + brush.bg if brush.bg isnt undefined
-      prefix += " cursor visible" if hasCursor
-      prefix += "\">"
-      postfix = ""
+      span += " bg" + brush.bg if brush.bg isnt undefined
+      span += " cursor visible" if hasCursor
+      span += "\">"

-    prefix + char + postfix
+    span

   showCursor: (show) ->
     if show


A little faster renderLine

A little faster renderLine

--- app/assets/javascripts/player/terminal_view.js.coffee
+++ app/assets/javascripts/player/terminal_view.js.coffee
@@ -43,7 +43,7 @@
     @$el.css(width: width + 'px', height: height + 'px')

   renderLine: (n, data, cursorX) ->
-    html = ''
+    html = []
     prevBrush = undefined

     for i in [0...@cols]
@@ -56,19 +56,19 @@

         if brush != prevBrush or i is cursorX or i is (cursorX + 1)
           if prevBrush
-            html += ''
+            html.push ''

-          html += @spanFromBrush(brush, i is cursorX)
+          html.push @spanFromBrush(brush, i is cursorX)

           prevBrush = brush

-        html += char
+        html.push char
       else
-        html += ' '
+        html.push ' '

-    html += '' if prevBrush
+    html.push '' if prevBrush

-    @$el.find(".line:eq(" + n + ")").html html
+    @$el.find(".line:eq(" + n + ")").html html.join('')

   spanFromBrush: (brush, hasCursor) ->
     key = "#{brush.hash()}_#{hasCursor}"


Fix missing result return causing rebuy product addition to fail

--- app/assets/javascripts/services/basket_service.coffee
+++ app/assets/javascripts/services/basket_service.coffee
@@ -47,6 +47,7 @@
     setFromBuy = (buy)->
       entryToProductNameHash = (result, entry)->
         result[entry.product.name] = entry.amount
+        result

       search = _.chain($location.search())
         .assign buyer: buy.buyer.username, basket: true


Pass checklist items in as a collection

Pass checklist items in as a collection

--- app/assets/javascripts/views/checklist_view.js.coffee
+++ app/assets/javascripts/views/checklist_view.js.coffee
@@ -9,6 +9,6 @@

   render: =>
     @$el.html @template @context()
-    view = new ChecklistItemsListView model: @model.checklist_items
+    view = new ChecklistItemsListView collection: @model.checklist_items
     @$el.append view.render().$el
     this


Fix Test For GOPATH Token Replacement When Running On Travis

--- spec/dispatch-spec.coffee
+++ spec/dispatch-spec.coffee
@@ -55,4 +55,4 @@
     it "replaces the $GOPATH token with the first element from the gopath", ->
       cmd = atom.config.get('go-plus.gofmtPath')
       cmd = dispatch.replaceTokensInPath(cmd)
-      expect(cmd).toBe path.join(process.env.HOME, "go", "bin", "goimports")
+      expect(cmd).toBe path.join(dispatch.buildGoPath(), "bin", "goimports")


Update spec/grammars-spec.coffee

Update spec/grammars-spec.coffee

--- spec/grammars-spec.coffee
+++ spec/grammars-spec.coffee
@@ -11,8 +11,7 @@
   it 'has a command and an args function set for each grammar\'s mode', ->
     @codeContext.textSource = @dummyTextSource
     for lang,modes of grammarMap
-      for mode of modes
-        mode.command
-        expect(mode.command).toBeDefined()
-        argList = mode.args(@codeContext)
+      for mode,commandContext of modes
+        expect(commandContext.command).toBeDefined()
+        argList = commandContext.args(@codeContext)
         expect(argList).toBeDefined()


Update Reporter specs

Update Reporter specs

--- spec/reporter_spec.coffee
+++ spec/reporter_spec.coffee
@@ -33,9 +33,26 @@
     @scr_len_before = document.getElementsByTagName('script').length

     @url = 'foo.bar'
-    @payload = {k1: 'v1', k2: 'v2'}
-    @payload2 = {k3: 'v3', k4: 'v4'}
-    @url_with_payload = 'foo.bar?k1=v1&k2=v2'
+    @payload = {
+      url: 'http://www.yogurt.foo/products/show/15400722'
+      shop_code: 'SA-XXXX-Y'
+      actions: [
+        {
+          category: 'site'
+          type: 'sendPageView'
+        },
+        {
+          category: 'yogurt'
+          type: 'productClick'
+          data: {
+            product_id: '15400722'
+            shop_product_id: '752'
+            shop_id: '2032'
+          }
+        },
+      ]
+    }
+    @serialized_data = 'foo.bar?url=http%3A%2F%2Fwww.yogurt.foo%2Fproducts%2Fshow%2F15400722&shop_code=SA-XXXX-Y&actions=%5B%7B%22category%22%3A%22site%22%2C%22type%22%3A%22sendPageView%22%7D%2C%7B%22category%22%3A%22yogurt%22%2C%22type%22%3A%22productClick%22%2C%22data%22%3A%7B%22product_id%22%3A%2215400722%22%2C%22shop_product_id%22%3A%22752%22%2C%22shop_id%22%3A%222032%22%7D%7D%5D'

   describe 'instance', ->
     beforeEach (done) ->
@@ -62,48 +79,42 @@

   describe '#report', ->
     beforeEach ->
+      @subject = new @reporter()
       @pr = new @promise()
-      @subject = new @reporter()
+      @spy = sinon.spy(@subject, 'report')
+      @subject.report(@url, @payload)
+
+    afterEach ->
+      @spy.restore()
+
+    it 'accepts as 1st argument the url to report to', ->
+      expect(@spy.args[0][0]).to.equal @url
+
+    it 'accepts as 2nd argument the payload to report', ->
+      expect(@spy.args[0][1]).to.deep.equal @payload
+
+    describe 'payload argument', ->
+      it 'has property String url', ->
+        payload = @spy.args[0][1]
+        expect(payload)
+          .to.have.property('url')
+          .that.is.a('string')
+
+      it 'has property String shop_code', ->
+        payload = @spy.args[0][1]
+        expect(payload)
+          .to.have.property('shop_code')
+          .that.is.a('string')
+
+      it 'has property Array actions', ->
+        payload = @spy.args[0][1]
+        expect(payload)
+          .to.have.property('actions')
+          .that.is.an('array')

     it 'returns a promise', ->
-      res = @subject.report(@url, @payload)
-      expect(res).to.be.an.instanceof @promise
-
-    context 'when Settings.single_beacon is true', ->
-      beforeEach ->
-        @settings.single_beacon = true
-        @spy = sinon.spy(@subject, '_handleJob')
-        @actions = [@payload, @payload, @payload]
-
-        @subject.report(@url, @actions)
-
-      afterEach ->
-        @settings.single_beacon = false
-        @spy.restore()
-
-      it 'sends a beacon for all actions', ->
-        expect(@spy).to.be.calledOnce
-
-      it 'sends the proper arguments', ->
-        expect(@spy).to.be.calledWith(@url, @actions)
-
-    context 'when Settings.single_beacon is false', ->
-      beforeEach ->
-        @settings.single_beacon = false
-        @spy = sinon.spy(@subject, '_handleJob')
-        @actions = [@payload, @payload, @payload2]
-
-        @subject.report(@url, @actions)
-
-      afterEach ->
-        @settings.single_beacon = false
-        @spy.restore()
-
-      it 'sends a beacon for each action', ->
-        expect(@spy.callCount).to.equal @actions.length
-
-      it 'sends the proper arguments', ->
-        expect(@spy.args[2]).to.contain @actions[2]
+      expect(@subject.report(@url, @payload))
+        .to.be.an.instanceof @promise

     context 'when transport uses image', ->
       beforeEach ->
@@ -221,4 +232,4 @@
       expect(@stub).to.be.calledOnce

     it 'calls #_createTransport with proper args', ->
-      expect(@stub).to.be.calledWith(@pr, @url_with_payload)
+      expect(@stub).to.be.calledWith(@pr, @serialized_data)


Support logging longest suites

Support logging longest suites

--- spec/time-reporter.coffee
+++ spec/time-reporter.coffee
@@ -4,6 +4,7 @@
 class TimeReporter  extends jasmine.Reporter

   timedSpecs: []
+  timedSuites: {}

   constructor: ->
     window.logLongestSpec = -> window.logLongestSpecs(1)
@@ -13,11 +14,19 @@
         console.log "#{spec.time}ms"
         console.log spec.description

+    window.logLongestSuite = -> window.logLongestSuites(1)
+    window.logLongestSuites = (number=10) =>
+      console.log "#{number} longest running suites:"
+      suites = _.map(@timedSuites, (key, value) -> [value, key])
+      for suite in _.sortBy(suites, (suite) => -suite[1])[0...number]
+        console.log suite[0], suite[1]
+
   reportSpecStarting: (spec) ->
     stack = [spec.description]
     suite = spec.suite
     while suite
       stack.unshift suite.description
+      @suite = suite.description
       suite = suite.parentSuite

     @time = new Date().getTime()
@@ -27,8 +36,14 @@

   reportSpecResults: ->
     return unless @time? and @description?
+
+    duration = new Date().getTime() - @time
     @timedSpecs.push
       description: @description
-      time: new Date().getTime() - @time
+      time: duration
+    if @timedSuites[@suite]
+      @timedSuites[@suite] += duration
+    else
+      @timedSuites[@suite] = duration
     @time = null
     @description = null


Added Mailer service

Added Mailer service

--- lib/zen.coffee
+++ lib/zen.coffee
@@ -23,6 +23,7 @@
 appnima       = require "./services/appnima" if ZEN.appnima?
 mongo         = require "./services/mongo" if ZEN.mongo?
 redis         = require "./services/redis" if ZEN.redis?
+mailer        = require "./services/mailer" if ZEN.mailer?
 # -- Middlewares
 request       = require "./middlewares/request"
 firewall      = require "./middlewares/firewall" if ZEN.firewall?
@@ -46,7 +47,6 @@
         process.exit() if error
         ZEN.br()
         console.log " Listening at :#{ZEN.port}", "(CTRL + C to shutdown)".grey
-        ZEN.br()
         @

     use: (callback, system = false) =>
@@ -197,14 +197,14 @@
     services: ->
       promise = new Hope.Promise()
       tasks = []
-      if ZEN.mongo? or ZEN.redis? or ZEN.appnima
+      if ZEN.mongo? or ZEN.redis? or ZEN.mailer?
         ZEN.br "SERVICES"
         for connection in (ZEN.mongo or [])
           tasks.push do (connection) -> -> mongo.open connection
         if ZEN.redis?
           tasks.push => redis.open ZEN.redis
-        if ZEN.appnima
-          tasks.push => appnima.open ZEN.appnima
+        if ZEN.mailer?
+          tasks.push => mailer.open ZEN.mailer

       if tasks.length > 0
         Hope.shield(tasks).then (error, value) =>
@@ -240,11 +240,11 @@
     http.createServer()

 __serverClose = ->
-  console.log ""
   ZEN.br "ZENserver shutdown..."
   if ZEN.mongo? then mongo.close()
   if ZEN.redis? then redis.close()
-  console.log " ✓".green, "shutdown ok!".grey
+  if ZEN.mailer?then mailer.close()
+  console.log "✓".green, " Zen shutdown ok!".grey
   ZEN.br()

 __cast = (values) ->


update menu labels

update menu labels

--- menus/sftp-deployment.cson
+++ menus/sftp-deployment.cson
@@ -1,30 +1,49 @@
-# See https://atom.io/docs/latest/creating-a-package#menus for more details
 'context-menu':
-  '.atom-text-editor':
-    'FTP/SFTP - Upload current file': 'sftp-deployment:uploadCurrentFile'
-    'FTP/SFTP - Download current file': 'sftp-deployment:downloadCurrentFile'
-  '.tab':
-    'FTP/SFTP - Upload open files': 'sftp-deployment:uploadOpenFiles'
-  '.tab.active':
-    'FTP/SFTP - Upload current file': 'sftp-deployment:uploadCurrentFile'
-    'FTP/SFTP - Download current file': 'sftp-deployment:downloadCurrentFile'
-  '.tree-view':
-    'FTP/SFTP - Upload selection': 'sftp-deployment:uploadSelection'
-    'FTP/SFTP - Download selection': 'sftp-deployment:downloadSelection'
+    'atom-text-editor': [
+        { 'type': 'separator' }
+        { 'label': 'FTP/SFTP', 'submenu': [
+            { 'label': 'Upload current file', 'command': 'sftp-deployment:uploadCurrentFile' }
+            { 'label': 'Download current file', 'command': 'sftp-deployment:downloadCurrentFile' }
+        ]}
+        { 'type': 'separator' }
+    ]
+    'atom-pane .tab': [
+        { 'type': 'separator' }
+        { 'label': 'FTP/SFTP', 'submenu': [
+            { 'label': 'Upload open files', 'command': 'sftp-deployment:uploadOpenFiles' }
+        ]}
+        { 'type': 'separator' }
+    ]
+    'atom-pane .tab.active': [
+        { 'type': 'separator' }
+        { 'label': 'FTP/SFTP', 'submenu': [
+            { 'label': 'Upload current file', 'command': 'sftp-deployment:uploadCurrentFile' }
+            { 'label': 'Download current file', 'command': 'sftp-deployment:downloadCurrentFile' }
+        ]}
+        { 'type': 'separator' }
+    ]
+    '.tree-view.full-menu': [
+        { 'type': 'separator' }
+        { 'label': 'FTP/SFTP', 'submenu': [
+            { 'label': 'Upload selection', 'command': 'sftp-deployment:uploadSelection' }
+            { 'label': 'Download selection', 'command': 'sftp-deployment:downloadSelection' }
+        ]}
+        { 'type': 'separator' }
+    ]

-'menu': [
-  {
-    'label': 'Packages'
-    'submenu': [
-      'label': 'FTP/SFTP'
-      'submenu': [
-        { 'label': 'Map to remote...', 'command': 'sftp-deployment:mapToRemote' }
-        { 'type': 'separator' }
-        { 'label': 'Upload current file', 'command': 'sftp-deployment:uploadCurrentFile' }
-        { 'label': 'Upload open files', 'command': 'sftp-deployment:uploadOpenFiles' }
-        { 'type': 'separator' }
-        { 'label': 'Download current file', 'command': 'sftp-deployment:downloadCurrentFile' }
-      ]
+    'menu': [
+        {
+            'label': 'Packages'
+            'submenu': [
+                'label': 'FTP/SFTP'
+                'submenu': [
+                    { 'label': 'Map to remote...', 'command': 'sftp-deployment:mapToRemote' }
+                    { 'type': 'separator' }
+                    { 'label': 'Upload current file', 'command': 'sftp-deployment:uploadCurrentFile' }
+                    { 'label': 'Upload open files', 'command': 'sftp-deployment:uploadOpenFiles' }
+                    { 'type': 'separator' }
+                    { 'label': 'Download current file', 'command': 'sftp-deployment:downloadCurrentFile' }
+                ]
+            ]
+        }
     ]
-  }
-]


social: rename 'invitee' to 'inviter'

social: rename 'invitee' to 'inviter'

--- workers/social/lib/social/models/invitation.coffee
+++ workers/social/lib/social/models/invitation.coffee
@@ -243,11 +243,12 @@

   # sendInvitationEmail sends email according to given JInvitation
   @sendInvitationEmail: (client, invitation, callback) ->
-    invitee      = getName client.connection.delegate
+    inviter      = getName client.connection.delegate

     properties =
       groupName: invitation.groupName
-      invitee  : invitee
+      inviter  : inviter
+      invitee  : inviter # TODO: this field is deprecated, remove after updating template
       link     : "#{protocol}//#{invitation.groupName}.#{hostname}/Invitation/#{encodeURIComponent invitation.code}"

     Tracker.identifyAndTrack invitation.email, { subject : Tracker.types.INVITED_GROUP }, properties


Social: followable new notification changes are reverted

--- workers/social/lib/social/traits/followable.coffee
+++ workers/social/lib/social/traits/followable.coffee
@@ -160,8 +160,11 @@
               followee  : this
               follower  : follower

-            SocialNotification = require '../models/socialapi/notification'
-            SocialNotification.follow client, this, (err) -> warn err  if err
+            @emit 'FollowHappened',
+              origin    : this
+              actorType : 'follower'
+              follower  : ObjectRef(follower).data
+              group     : group

             follower.updateFollowingCount @, action


Default onScreenElements to empty array

Default onScreenElements to empty array

It'll be overwritten by ScrollSpy, but is needed to silence propType warning

--- src/components/questions/questions-controls.cjsx
+++ src/components/questions/questions-controls.cjsx
@@ -57,7 +57,7 @@
         </BS.Button>
       </BS.ButtonGroup>
       <ScrollSpy dataSelector='data-section' >
-        <Sectionizer chapter_sections={@getSections()} />
+        <Sectionizer onScreenElements={[]} chapter_sections={@getSections()} />
       </ScrollSpy>
       <div className="save-cancel">
         {@renderSaveCancelButtons() if ExerciseStore.hasUnsavedExclusions()}


Default welcome should announce 'Master' not 'Cluster' when at cluster.master process

--- src/ReplrClient.coffee
+++ src/ReplrClient.coffee
@@ -189,7 +189,7 @@
     if cluster.isMaster
       @getWorkersDescription (description)=>
         callback  """
-                  #{title} #{@options.name}[Cluster]
+                  #{title} #{@options.name}[Master]

                   #{@indent(description, 2)}


bind event after document is ready

bind event after document is ready

--- app/assets/javascripts/hosts.js.coffee
+++ app/assets/javascripts/hosts.js.coffee
@@ -1,10 +1,11 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
-$('a.edit').on 'click', (e)->
-	e.preventDefault()
-	e.stopPropagation()
-	$('span.delete').toggleClass('show')
+$(document).on 'ready', ->
+	$('a.edit').on 'click', (e)->
+		e.preventDefault()
+		e.stopPropagation()
+		$('span.delete').toggleClass('show')

-$('.item').on 'ajax:success', (e)->
-	$(@).parent().remove()+	$('.item').on 'ajax:success', (e)->
+		$(@).parent().remove()


add css style to span operation

add css style to span operation

--- MediaAdminBundle/Resources/public/coffee/tinyMCE/loadHtml2bbcodeMedia.coffee
+++ MediaAdminBundle/Resources/public/coffee/tinyMCE/loadHtml2bbcodeMedia.coffee
@@ -1,5 +1,6 @@
 ((html2bbcode) ->
   mediaTransformation =
+    '' : '',
     '' : '[media=$2]$1[/media]',
     '' : '[media]$1[/media]',


fix bad ID being passed on animal selector

--- app/controllers/animal_selector.coffee
+++ app/controllers/animal_selector.coffee
@@ -5,6 +5,7 @@
 User = require 'zooniverse/lib/models/user'
 AnalyticsLogger = require 'lib/analytics'
 columnize = require 'lib/columnize'
+Subject = require 'models/subject'
 AnimalDetails = require './animal_details'
 getTutorialSubject = require 'lib/get_tutorial_subject'
 getPhysicallyAdjacentSibling = require 'lib/get_physically_adjacent_sibling'
@@ -140,7 +141,7 @@
     setTimeout details.show, 125

   onClickClearFilters: ->
-    AnalyticsLogger.logEvent User.current?.zooniverse_id,@zooniverseId,'clear',''
+    AnalyticsLogger.logEvent User.current?.zooniverse_id,Subject.current?.zooniverseId,'clear',''
     @clearFilters()

   clearFilters: =>
@@ -149,7 +150,7 @@
     @searchInput.trigger 'keydown'

   onClickStartTutorial: ->
-    AnalyticsLogger.logEvent User.current?.zooniverse_id,@zooniverseId,'tutorial',''
+    AnalyticsLogger.logEvent User.current?.zooniverse_id,Subject.current?.zooniverseId,'tutorial',''
     getTutorialSubject().select()

 module.exports = AnimalSelector


update to inline comments...

update to inline comments...

--- marktab.coffee
+++ marktab.coffee
@@ -12,15 +12,19 @@
 			@json[key] ?= []
 		this

+	# parses marktab notes into json
 	parseNotes: (notes) ->
 		""

+	# parses marktab chords into json
 	parseChord: (chord) ->
 		""

+	# parses marktab riffs into json
 	parseRiff: (riff) ->
 		""

+	# parses marktab variables into json
 	parseVariable: (variable) ->
 		""

@@ -35,7 +39,7 @@
 			json[stringNum][max-1] ?= undefined
 		json

-	# generates tab from a json tab map
+	# generates tab from a json note map
 	generate: (json = @json) ->
 		result = ""
 		this.normalizeJson(json)


Fix test issues with older versions of TDS protocol: - DateTime2 is only supported in 7_3_A and later. - VarChar max length is 8000 before 7_2. - NVarChar max length is 4000 before 7_2.

--- test/integration/parameterised-statements-test.coffee
+++ test/integration/parameterised-statements-test.coffee
@@ -105,8 +105,12 @@
 exports.varChar = (test) ->
   execSql(test, TYPES.VarChar, 'qaz')

+### Per 2.2.5.4.3, lengths greater than 8000 only supported version 7.2 and beyond. ###
 exports.varCharN = (test) ->
-  execSql(test, TYPES.VarChar, 'qaz', null, {length: 8001})
+  execSql(test, TYPES.VarChar, 'qaz', null, {length: 8000})
+
+exports.varCharN_7_2_AndLater = (test) ->
+  execSql(test, TYPES.VarChar, 'qaz', '7_2', {length: 8001})

 exports.varCharEmptyString = (test) ->
   execSql(test, TYPES.VarChar, '')
@@ -122,13 +126,24 @@
   execSql(test, TYPES.VarChar, longString, '7_2')

 exports.varCharMaxEmptyString = (test) ->
-  execSql(test, TYPES.VarChar, '', null, {length: 8001})
+  execSql(test, TYPES.VarChar, '', null, {length: 8000})
+
+exports.varCharMaxEmptyString = (test) ->
+  execSql(test, TYPES.VarChar, '', '7_2', {length: 8001})

 exports.nVarChar = (test) ->
   execSql(test, TYPES.NVarChar, 'qaz')

+###
+Per 2.2.5.4.3, lengths greater than 8000 only supported version 7.2 and
+beyond. Since NVarChar is unicode, that'd be 4000. More explict in:
+https://msdn.microsoft.com/en-us/library/ms186939.aspx
+###
 exports.nVarCharN = (test) ->
-  execSql(test, TYPES.NVarChar, 'qaz', null, {length: 4001})
+  execSql(test, TYPES.NVarChar, 'qaz', null, {length: 4000})
+
+exports.nVarCharN_7_2_AndLater = (test) ->
+  execSql(test, TYPES.NVarChar, 'qaz', '7_2', {length: 4001})

 exports.nVarCharEmptyString = (test) ->
   execSql(test, TYPES.NVarChar, '')
@@ -144,7 +159,10 @@
   execSql(test, TYPES.NVarChar, longString, '7_2')

 exports.nVarCharMaxEmptyString = (test) ->
-  execSql(test, TYPES.NVarChar, '', null, {length: 4001})
+  execSql(test, TYPES.NVarChar, '', null, {length: 4000})
+
+exports.nVarCharMaxEmptyString_7_2_AndLater = (test) ->
+  execSql(test, TYPES.NVarChar, '', '7_2', {length: 4001})

 exports.Char = (test) ->
   execSql(test, TYPES.Char, 'qaz')
@@ -191,10 +209,10 @@
   execSql(test, TYPES.DateTime, null)

 exports.dateTime2 = (test) ->
-  execSql(test, TYPES.DateTime2, new Date('December 4, 2011 10:04:23'))
+  execSql(test, TYPES.DateTime2, new Date('December 4, 2011 10:04:23'), '7_3_A')

 exports.dateTime2Null = (test) ->
-  execSql(test, TYPES.DateTime2, null)
+  execSql(test, TYPES.DateTime2, null, '7_3_A')

 exports.outputBitTrue = (test) ->
   execSqlOutput(test, TYPES.Bit, true)
@@ -395,6 +413,7 @@
   )

 execSql = (test, type, value, tdsVersion, options) ->
+
   config = getConfig()
   #config.options.packetSize = 32768


#166802182 return blank instead of nothing

--- app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
@@ -376,7 +376,7 @@
         else
           return selected.val()
       else
-        return
+        ""
     if $conditionElement.is(":checkbox")
       if $conditionElement.is(":checked")
         if $conditionElement.attr('data-label-value')


search against host description as well

search against host description as well

--- app/assets/javascripts/states/runbooks/views/run_command.js.coffee
+++ app/assets/javascripts/states/runbooks/views/run_command.js.coffee
@@ -70,7 +70,8 @@
       matcher: (term, text, opt) =>
         # match against host name, tags
         m = $.prototype.select2.defaults.matcher
-        tags = @hosts.get(opt.val()).tags()
+        host = @hosts.get(opt.val())
+        tags = host.tags()
         if term[0] == "#"
           term = term.substr(1)
-        m(term, text) || _.include(tags, term) || _.find(tags, (t) -> t.indexOf(term) >= 0)
+        m(term, text) || m(term, host.get("desc")) || _.include(tags, term) || _.find(tags, (t) -> t.indexOf(term) >= 0)


draw mirrored image

draw mirrored image

--- app/assets/javascripts/uploadcare/widget/tabs/camera-tab.js.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/camera-tab.js.coffee
@@ -77,7 +77,11 @@
       canvas = document.createElement('canvas')
       canvas.width = w;
       canvas.height = h;
-      canvas.getContext('2d').drawImage(video, 0, 0, w, h)
+      ctx = canvas.getContext('2d')
+      if @mirrored
+        ctx.translate(w, 0)
+        ctx.scale(-1, 1)
+      ctx.drawImage(video, 0, 0, w, h)

       utils.canvasToBlob canvas, 'image/jpeg', 0.9, (blob) =>
         blob.name = "camera.jpg"


hover the icon of the page

hover the icon of the page

--- app/assets/javascripts/locomotive/views/pages/list_view.js.coffee
+++ app/assets/javascripts/locomotive/views/pages/list_view.js.coffee
@@ -55,8 +55,8 @@

   make_hoverable: ->
     @$('a').hover ->
-      $(@).previous('i.icon').addClass('on')
-    , -> $(@).previous('i.icon').removeClass('on')
+      $(@).prev('i.icon').addClass('on')
+    , -> $(@).prev('i.icon').removeClass('on')

   call_sort: (folder) ->
     $.rails.ajax


using `chosen` on .comprobante_select

using `chosen` on .comprobante_select

--- app/assets/javascripts/mixins/comprobante_on_form_mixin.js.coffee
+++ app/assets/javascripts/mixins/comprobante_on_form_mixin.js.coffee
@@ -2,3 +2,11 @@
   comprobantes: (->
     Bandango.comprobantesHelper.comprobantes
   ).property()
+
+  didInsertElement: ->
+    @_super()
+    @$(".comprobante_select").chosen()
+
+  willDestroyElement: ->
+    @_super()
+    @$(".comprobante_select").chosen "destroy"


ModelBackedView already implements tagName

--- app/assets/javascripts/views/clientes/clientes_new_view.js.coffee
+++ app/assets/javascripts/views/clientes/clientes_new_view.js.coffee
@@ -1,7 +1,6 @@
 self = null

 Bandango.ClientesNewView = Bandango.ModelBackedView.extend
-  tagName: "form"
   tiposIds: ["RUC", "Cédula", "Otro"]

   success: ->


fixes a bug in fetching the next page of users, refs #7768

Change-Id: I7e8a100de7c8824d873fa4c64276c3199410bb1c
Reviewed-on: https://gerrit.instructure.com/12301
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/views/course_settings/UserCollectionView.coffee
+++ app/coffeescripts/views/course_settings/UserCollectionView.coffee
@@ -11,7 +11,7 @@

     # options.requestParams are merged with UserCollection#fetch request params
     initialize: (options) ->
-      @fetchOptions = data: $.extend ENV.USER_PARAMS, options.requestParams
+      @fetchOptions = data: $.extend {}, ENV.USER_PARAMS, options.requestParams
       @collection = new UserCollection()
       @collection.url = options.url
       @collection.on 'add', @renderUser


fix: rate limit check

fix: rate limit check

--- src/rest.coffee
+++ src/rest.coffee
@@ -34,14 +34,12 @@
     _.extend @options, _.omit options, 'baseUrl', 'debug', 'apikey'

   getRateLimit: (response) ->
-    headers = response?.headers
-
-    if headers['x-ratelimit-limit']
-      @rateLimit = headers['x-ratelimit-limit']
-    if headers['x-ratelimit-remaining']
-      @rateRemaining = headers['x-ratelimit-remaining']
-
-    return @rateRemaining
+    if headers = response?.headers
+      if headers['x-ratelimit-limit']
+        @rateLimit = headers['x-ratelimit-limit']
+      if headers['x-ratelimit-remaining']
+        @rateRemaining = headers['x-ratelimit-remaining']
+    @rateRemaining

   wrapResponse: (rest) =>
     deferred = Q.defer()


Revert "redraw on resize"

Revert "redraw on resize"

This reverts commit 23b238a0a7da390ab1faacd67c836f3a0521427b.

--- src/root.coffee
+++ src/root.coffee
@@ -11,12 +11,6 @@
 style = require './root.styl'

 style.use()
-
-###########
-# EVENTS  #
-###########
-
-window.addEventListener('resize', z.redraw)

 ###########
 # LOGGING #


master: update to the coffeescript to add a listener for a broadcast to update the DOM

--- src/angular-slider.coffee
+++ src/angular-slider.coffee
@@ -48,7 +48,7 @@

     template: '
<range floor="floor" class="slider" ceiling="ceiling" ng-model-low="range.low" ng-model-high="range.high"/>
'

-rangeDirective = ($timeout) ->
+rangeDirective = ($timeout, $window) ->
     restrict: 'EA'
     scope:
         floor:       '@'
@@ -230,12 +230,13 @@

             $timeout updateDOM
             scope.$watch w, updateDOM for w in watchables
-            $(window).resize(updateDOM);
+            $(window).resize updateDOM
+            scope.$on 'UPDATE_SLIDER_DOM', (event) -> updateDOM()

 module = (window, angular) ->
     angular
         .module(MODULE_NAME, [])
-        .directive(RANGE_TAG, ['$timeout', rangeDirective])
+        .directive(RANGE_TAG, ['$timeout', '$window', rangeDirective])
         .directive(SLIDER_TAG, [sliderDirective])

 module window, window.angular


Auto-merged master into new-build-header on deployment.

--- app/utils/keys-map.coffee
+++ app/utils/keys-map.coffee
@@ -19,6 +19,7 @@
   d:           'D'
   julia:       'Julia'
   csharp:      'C#'
+  mono:        'Mono'
   dart:        'Dart'
   elixir:      'Elixir'
 }


Add rootView to atom spec to eliminate warnings

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -1,12 +1,14 @@
 RootView = require 'root-view'
 {$$} = require 'space-pen'

-describe "the `atom` global", ->
+fdescribe "the `atom` global", ->
+  beforeEach ->
+    new RootView
+
   describe ".loadPackage(name)", ->
     [extension, stylesheetPath] = []

     beforeEach ->
-      rootView = new RootView
       extension = require "package-with-module"
       stylesheetPath = require.resolve("fixtures/packages/package-with-module/stylesheets/styles.css")


un-f and un-console

un-f and un-console

--- spec/app/file-spec.coffee
+++ spec/app/file-spec.coffee
@@ -14,13 +14,12 @@
     file.off()
     fs.remove(file.getPath()) if fs.exists(file.getPath())

-  fdescribe "when the contents of the file change", ->
+  describe "when the contents of the file change", ->
     it "triggers 'contents-change' event handlers", ->
       changeHandler = null
       runs ->
         changeHandler = jasmine.createSpy('changeHandler')
         file.on 'contents-change', changeHandler
-        console.log "ATOMIC WRITE"
         fs.write(file.getPath(), "this is new!")

       waitsFor "change event", ->
@@ -28,7 +27,6 @@

       runs ->
         changeHandler.reset()
-        console.log 'writing'
         fs.write(file.getPath(), "this is newer!")

       waitsFor "second change event", ->


conditional loading of tool settings

conditional loading of tool settings

--- app/views/settings.coffee
+++ app/views/settings.coffee
@@ -10,8 +10,9 @@
       when 'histogram' then ToolSettings = require 'views/graph_settings'
       when 'scatterplot' then ToolSettings = require 'views/graph_settings'

-    @toolSettings = new ToolSettings { model: @model, el: @el }
-
+    if ToolSettings?
+      @toolSettings = new ToolSettings { model: @model, el: @el }
+
     if @model?
       @model.on 'change:height', @setHeight
       @setHeight()


add MODEL_DIC type #14

add MODEL_DIC type #14

--- spec/lib/type-info.coffee
+++ spec/lib/type-info.coffee
@@ -36,3 +36,26 @@
             typeInfo = TypeInfo.createModelListType('hobby', 'happy-hobby-list')
             expect(typeInfo).to.have.property('listName', 'happy-hobby-list')

+
+    describe '@createModelDicType', ->
+
+        it 'returns TypeInfo representing MODEL_DIC', ->
+
+            typeInfo = TypeInfo.createModelDicType('hobby')
+
+            expect(typeInfo).to.have.property('name', 'MODEL_DIC')
+            expect(typeInfo).to.have.property('model', 'hobby')
+            expect(typeInfo).to.have.property('dicName', 'hobby-dic')
+
+
+        it 'returns TypeInfo with dicName', ->
+
+            typeInfo = TypeInfo.createModelDicType('hobby', name: 'another-hobby-dic')
+            expect(typeInfo).to.have.property('dicName', 'another-hobby-dic')
+
+
+        it 'parses 2nd argument as dicName when it is string ', ->
+
+            typeInfo = TypeInfo.createModelDicType('hobby', 'happy-hobby-dic')
+            expect(typeInfo).to.have.property('dicName', 'happy-hobby-dic')
+


Electron 0.37 reports ".<anonymous>" for anonymous methods rather than ""

--- spec/reporter-spec.coffee
+++ spec/reporter-spec.coffee
@@ -1,6 +1,12 @@
 Reporter = require '../lib/reporter'
 os = require 'os'
 osVersion = "#{os.platform()}-#{os.arch()}-#{os.release()}"
+
+# TODO: Remove me after Electron 0.37 is on stable
+if parseFloat(process.versions.electron) >= 0.37
+  method = ".<anonymous>"
+else
+  method = ""

 describe "Reporter", ->
   [requests, initialStackTraceLimit] = []
@@ -53,7 +59,7 @@
                 "message": "",
                 "stacktrace": [
                   {
-                    "method": "",
+                    "method": method,
                     "lineNumber": lineNumber,
                     "columnNumber": columnNumber
                   }
@@ -179,7 +185,7 @@
                 "message": "",
                 "stacktrace": [
                   {
-                    "method": "",
+                    "method": method,
                     "lineNumber": lineNumber,
                     "columnNumber": columnNumber
                   }


Only start an application when there is a commit

--- src/application.coffee
+++ src/application.coffee
@@ -89,7 +89,7 @@
 			json: true
 		).spread((request, body) ->
 			console.log("Remote apps")
-			remoteApps = ("registry.resin.io:5000/#{path.basename(app.git_repository, '.git')}/#{app.commit}" for app in body.d)
+			remoteApps = ("registry.resin.io/#{path.basename(app.git_repository, '.git')}/#{app.commit}" for app in body.d when app.commit)
 			console.log(remoteApps)

 			console.log("Local apps")


removed unnecessary changes

removed unnecessary changes

--- src/application.coffee
+++ src/application.coffee
@@ -100,21 +100,11 @@
 	Promise.all([
 		knex('config').select('value').where(key: 'apiKey')
 		knex('config').select('value').where(key: 'uuid')
-		knex('config').select('value').where(key: 'version')
 		knex('app').select()
 	])
 	.then ([[apiKey], [uuid], [version], apps]) ->
 		apiKey = apiKey.value
 		uuid = uuid.value
-		version = version.value
-		###
-		# should I post 'resin-supervisor' version every 15-minutes?
-		# with something like
-		resinAPI.post(
-		    device:
-		        supervisor-version: version
-		)
-		###
 		resinAPI.get(
 			resource: 'application'
 			options: