cache on for attributes optimized test

cache on for attributes optimized test

--- test/attributes.coffee
+++ test/attributes.coffee
@@ -16,7 +16,7 @@
   describe "a href: '/', title: 'Home'", ->
     it 'should render ', ->
       a = -> a href: '/', title: 'Home'
-      cc.render(a, optimize: true).should.equal ''
+      cc.render(a, optimize: true, cache: on).should.equal ''

 describe 'Attribute values optimized', ->
   describe "br vrai: yes, faux: no, undef: @foo, nil: null, str: 'str', num: 42, arr: [1, 2, 3], obj: {foo: 'bar'}, func: ->", ->


Correct comment.

Correct comment.

--- test/integration/price-import.spec.coffee
+++ test/integration/price-import.spec.coffee
@@ -81,7 +81,7 @@
     cleanup(@logger, @client)
     .then -> done()
     .catch (err) -> done(_.prettify err)
-  , 30000 # 10sec
+  , 30000 # 30sec

   it 'should update prices of a product', (done) ->
     prices = [


Update hello.coffee

Update hello.coffee

--- scripts/hello.coffee
+++ scripts/hello.coffee
@@ -46,6 +46,11 @@
     "Senti a sua falta"
     "também o/"
 ]
+amor= [
+    "Desculpe, mas o que é amar?"
+    "Não conheço essa definição, desculpe %"
+    "Você está bem, %?"
+]
 module.exports = (robot) ->
     robot.hear /(^olá lain|oie lain|oi lain)/i, (msg) ->
         hello = msg.random hellos
@@ -57,6 +62,10 @@

     robot.hear /(^saudades lain)/i, (msg) ->
         hello = msg.random saudades
+        msg.send hello.replace "%", msg.message.user.name
+
+    robot.hear /(^te amo lain|lain amo você|lain te amo)/i, (msg) ->
+        hello = msg.random amor
         msg.send hello.replace "%", msg.message.user.name

     robot.hear /(^bom dia(?! ))/i, (msg) ->


Disable sendEvent test in slimerjs due to laurentj/slimerjs#430

--- test/core.tests.coffee
+++ test/core.tests.coffee
@@ -174,7 +174,8 @@
       fs.unlinkSync(fileName)

   describe 'send event', ->
-    it 'call sendEvent function', ->
+    # https://github.com/laurentj/slimerjs/issues/430
+    it 'call sendEvent function', !slimerjs and ->
       { code } = yield run { test: 'send-event' }
       code.should.equal 0


remove debuggin code

remove debuggin code

--- test/create_zip.coffee
+++ test/create_zip.coffee
@@ -29,7 +29,6 @@
     t.end()

 test 'creates zip file', (t) ->
-  console.log zipFile, fs.existsSync(zipFile)
   t.false fs.existsSync(zipFile), 'zip file is not created'

   t.end()


Fix for bug where the done button for editing a pro or con wouldn't close the edit point dialog

--- @client/pro_con_widget.coffee
+++ @client/pro_con_widget.coffee
@@ -340,6 +340,7 @@
         pc = fetch edit_mode
         EditPoint
           key: if pc.adding_new_point then "new_point_#{valence}" else pc.editing_points[0]
+          point: if !pc.adding_new_point then pc.editing_points[0]
           proposal: @props.proposal
           fresh: pc.adding_new_point
           valence: valence
@@ -913,6 +914,7 @@
                !browser.is_mobile
               EditPoint
                 key: point.key
+                point: point.key
                 proposal: @props.proposal
                 fresh: false
                 valence: @props.valence


fix the wrong namespace

fix the wrong namespace

--- app/api/api.coffee
+++ app/api/api.coffee
@@ -4,9 +4,9 @@

   requestURI: (path, query={}) ->
     # NOTE: Setup your own API endpoint, as below
-    Barwallet.API_ENDPOINT = "http://bar_wallet.com/api/v1"
+    App.API_ENDPOINT = "http://bar_wallet.com/api/v1"

-    uri = "#{Barwallet.API_ENDPOINT}#{path}.json?"
+    uri = "#{App.API_ENDPOINT}#{path}.json?"
     for own key, value of query
       uri += "#{ key }=#{ escape(value) }&"


Edit clients.

Edit clients.

--- app/clients.coffee
+++ app/clients.coffee
@@ -8,12 +8,17 @@
 @open_client = (client) ->
   menu_slide ->
    ($ "form").append magic_button(),
-                     create_form ($ "clients client"), SCHEMAS.client
+                     create_form client, SCHEMAS.client

 add_button = () ->
   (($ "<button>").text "Kunden erstellen").click (x) ->
     ($ "clients").append make_client()
     make_clients_form()
+    x.preventDefault()
+
+edit_button = (client) ->
+  (($ "<button>").text "bearbeiten").click (x) ->
+    open_client client
     x.preventDefault()

 remove_button = (client) ->
@@ -26,6 +31,7 @@
   x = $ x
   (tr "listrecord").append ($ "<input>", type: "checkbox"),
                            (create_form x, SCHEMAS.client),
+                           (edit_button x),
                            remove_button x

 client_list = (x) ->


Adding more snippets

Adding more snippets

--- snippets/verify.cson
+++ snippets/verify.cson
@@ -223,7 +223,7 @@
         'body': 'verify the element found by "$1" is displayed within "$2" seconds'
     'verify102':
         'prefix': 'verify the element found by "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element found by "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element found by "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify103':
         'prefix': 'verify the element found by "identifier" is present'
         'body': 'verify the element found by "$1" is present'
@@ -250,7 +250,7 @@
         'body': 'verify the element found by "$1" is not displayed within "$2" seconds'
     'verify111':
         'prefix': 'verify the element found by "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element found by "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element found by "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify112':
         'prefix': 'verify the element found by "identifier" is not present'
         'body': 'verify the element found by "$1" is not present'
@@ -278,7 +278,7 @@
         'body': 'verify the element the ID of "$1" is displayed within "$2" seconds'
     'verify132':
         'prefix': 'verify the element the ID of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the ID of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the ID of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify133':
         'prefix': 'verify the element the ID of "identifier" is present'
         'body': 'verify the element the ID of "$1" is present'
@@ -305,7 +305,7 @@
         'body': 'verify the element the ID of "$1" is not displayed within "$2" seconds'
     'verify141':
         'prefix': 'verify the element the ID of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the ID of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the ID of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify142':
         'prefix': 'verify the element the ID of "identifier" is not present'
         'body': 'verify the element the ID of "$1" is not present'
@@ -333,7 +333,7 @@
         'body': 'verify the element the css selector of "$1" is displayed within "$2" seconds'
     'verify162':
         'prefix': 'verify the element the css selector of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the css selector of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the css selector of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify163':
         'prefix': 'verify the element the css selector of "identifier" is present'
         'body': 'verify the element the css selector of "$1" is present'
@@ -360,7 +360,7 @@
         'body': 'verify the element the css selector of "$1" is not displayed within "$2" seconds'
     'verify171':
         'prefix': 'verify the element the css selector of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the css selector of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the css selector of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify172':
         'prefix': 'verify the element the css selector of "identifier" is not present'
         'body': 'verify the element the css selector of "$1" is not present'
@@ -388,7 +388,7 @@
         'body': 'verify the element the class of "$1" is displayed within "$2" seconds'
     'verify192':
         'prefix': 'verify the element the class of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the class of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the class of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify193':
         'prefix': 'verify the element the class of "identifier" is present'
         'body': 'verify the element the class of "$1" is present'
@@ -415,7 +415,7 @@
         'body': 'verify the element the class of "$1" is not displayed within "$2" seconds'
     'verify201':
         'prefix': 'verify the element the class of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the class of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the class of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify202':
         'prefix': 'verify the element the class of "identifier" is not present'
         'body': 'verify the element the class of "$1" is not present'
@@ -443,7 +443,7 @@
         'body': 'verify the element the xpath of "$1" is displayed within "$2" seconds'
     'verify222':
         'prefix': 'verify the element the xpath of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the xpath of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the xpath of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify223':
         'prefix': 'verify the element the xpath of "identifier" is present'
         'body': 'verify the element the xpath of "$1" is present'
@@ -470,7 +470,7 @@
         'body': 'verify the element the xpath of "$1" is not displayed within "$2" seconds'
     'verify231':
         'prefix': 'verify the element the xpath of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the xpath of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the xpath of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify232':
         'prefix': 'verify the element the xpath of "identifier" is not present'
         'body': 'verify the element the xpath of "$1" is not present'
@@ -498,7 +498,7 @@
         'body': 'verify the element the name of "$1" is displayed within "$2" seconds'
     'verify252':
         'prefix': 'verify the element the name of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the name of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the name of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify253':
         'prefix': 'verify the element the name of "identifier" is present'
         'body': 'verify the element the name of "$1" is present'
@@ -525,7 +525,7 @@
         'body': 'verify the element the name of "$1" is not displayed within "$2" seconds'
     'verify261':
         'prefix': 'verify the element the name of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the name of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the name of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify262':
         'prefix': 'verify the element the name of "identifier" is not present'
         'body': 'verify the element the name of "$1" is not present'
@@ -553,7 +553,7 @@
         'body': 'verify the element found by alias "$1" is displayed within "$2" seconds'
     'verify302':
         'prefix': 'verify the element found by alias "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element found by alias "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element found by alias "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify303':
         'prefix': 'verify the element found by alias "identifier" is present'
         'body': 'verify the element found by alias "$1" is present'
@@ -580,7 +580,7 @@
         'body': 'verify the element found by alias "$1" is not displayed within "$2" seconds'
     'verify311':
         'prefix': 'verify the element found by alias "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element found by alias "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element found by alias "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify312':
         'prefix': 'verify the element found by alias "identifier" is not present'
         'body': 'verify the element found by alias "$1" is not present'
@@ -608,7 +608,7 @@
         'body': 'verify the element the ID alias of "$1" is displayed within "$2" seconds'
     'verify332':
         'prefix': 'verify the element the ID alias of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the ID alias of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the ID alias of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify333':
         'prefix': 'verify the element the ID alias of "identifier" is present'
         'body': 'verify the element the ID alias of "$1" is present'
@@ -635,7 +635,7 @@
         'body': 'verify the element the ID alias of "$1" is not displayed within "$2" seconds'
     'verify341':
         'prefix': 'verify the element the ID alias of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the ID alias of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the ID alias of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify342':
         'prefix': 'verify the element the ID alias of "identifier" is not present'
         'body': 'verify the element the ID alias of "$1" is not present'
@@ -663,7 +663,7 @@
         'body': 'verify the element the css selector alias of "$1" is displayed within "$2" seconds'
     'verify362':
         'prefix': 'verify the element the css selector alias of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the css selector alias of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the css selector alias of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify363':
         'prefix': 'verify the element the css selector alias of "identifier" is present'
         'body': 'verify the element the css selector alias of "$1" is present'
@@ -690,7 +690,7 @@
         'body': 'verify the element the css selector alias of "$1" is not displayed within "$2" seconds'
     'verify371':
         'prefix': 'verify the element the css selector alias of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the css selector alias of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the css selector alias of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify372':
         'prefix': 'verify the element the css selector alias of "identifier" is not present'
         'body': 'verify the element the css selector alias of "$1" is not present'
@@ -718,7 +718,7 @@
         'body': 'verify the element the class alias of "$1" is displayed within "$2" seconds'
     'verify392':
         'prefix': 'verify the element the class alias of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the class alias of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the class alias of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify393':
         'prefix': 'verify the element the class alias of "identifier" is present'
         'body': 'verify the element the class alias of "$1" is present'
@@ -745,7 +745,7 @@
         'body': 'verify the element the class alias of "$1" is not displayed within "$2" seconds'
     'verify401':
         'prefix': 'verify the element the class alias of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the class alias of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the class alias of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify402':
         'prefix': 'verify the element the class alias of "identifier" is not present'
         'body': 'verify the element the class alias of "$1" is not present'
@@ -773,7 +773,7 @@
         'body': 'verify the element the xpath alias of "$1" is displayed within "$2" seconds'
     'verify422':
         'prefix': 'verify the element the xpath alias of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the xpath alias of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the xpath alias of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify423':
         'prefix': 'verify the element the xpath alias of "identifier" is present'
         'body': 'verify the element the xpath alias of "$1" is present'
@@ -800,7 +800,7 @@
         'body': 'verify the element the xpath alias of "$1" is not displayed within "$2" seconds'
     'verify431':
         'prefix': 'verify the element the xpath alias of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the xpath alias of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the xpath alias of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify432':
         'prefix': 'verify the element the xpath alias of "identifier" is not present'
         'body': 'verify the element the xpath alias of "$1" is not present'
@@ -828,7 +828,7 @@
         'body': 'verify the element the name alias of "$1" is displayed within "$2" seconds'
     'verify452':
         'prefix': 'verify the element the name alias of "identifier" is displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the name alias of "$1" is displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the name alias of "$1" is displayed within "$2" seconds ignoring timeouts'
     'verify453':
         'prefix': 'verify the element the name alias of "identifier" is present'
         'body': 'verify the element the name alias of "$1" is present'
@@ -855,7 +855,7 @@
         'body': 'verify the element the name alias of "$1" is not displayed within "$2" seconds'
     'verify461':
         'prefix': 'verify the element the name alias of "identifier" is not displayed within "time" seconds ignoring timeouts'
-        'body': 'verify the element the name alias of "$1" is not displayed within "$2" seconds ignoring timeouts"
+        'body': 'verify the element the name alias of "$1" is not displayed within "$2" seconds ignoring timeouts'
     'verify462':
         'prefix': 'verify the element the name alias of "identifier" is not present'
         'body': 'verify the element the name alias of "$1" is not present'


[content] use smallest column in about tab Adds the info pane to the smallest column instead of the last column.

--- src/coffee/content/profile.coffee
+++ src/coffee/content/profile.coffee
@@ -4,6 +4,23 @@
 # @license MIT

 ((module, $) ->
+
+    # find the smallest of a list of elements
+    # smallest = lowest height
+    $.fn.smallest = ->
+        return  @ if @length = 0
+
+        smallest = null
+        smallestHeight = Infinity
+
+        @each ->
+            $this = $ @
+            if $this.height() < smallestHeight
+                smallestHeight = $this.height()
+                smallest = $this
+
+        smallest
+
     helper =
         createWrapper: ->
             $ '''
@@ -245,7 +262,7 @@
                 module.log.log 'Creating profile wrapper'
                 $elem = helper.createWrapper()
                 $root.find 'div.Ypa.jw.am'
-                    .last()
+                    .smallest()
                     .prepend $elem
             else
                 module.log.log 'Re-using existing profile wrapper'


:hear_no_evil:

--- scripts/roles.coffee
+++ scripts/roles.coffee
@@ -1,85 +0,0 @@
-# Description:
-#   Assign roles to people you're chatting with
-#
-# Commands:
-#   hubot <user> is a badass guitarist - assign a role to a user
-#   hubot <user> is not a badass guitarist - remove a role from a user
-#   hubot who is <user> - see what roles a user has
-#
-# Examples:
-#   hubot holman is an ego surfer
-#   hubot holman is not an ego surfer
-
-module.exports = (robot) ->
-
-  getAmbiguousUserText = (users) ->
-    "Be more specific, I know #{users.length} people named like that: #{(user.name for user in users).join(", ")}"
-
-  robot.respond /who is @?([\w .\-]+)\?*$/i, (msg) ->
-    joiner = ', '
-    name = msg.match[1].trim()
-
-    if name is "you"
-      msg.send "Who ain't I?"
-    else if name is robot.name
-      msg.send "The best."
-    else
-      users = robot.brain.usersForFuzzyName(name)
-      if users.length is 1
-        user = users[0]
-        user.roles = user.roles or [ ]
-        if user.roles.length > 0
-          if user.roles.join('').search(',') > -1
-            joiner = '; '
-          msg.send "#{name} is #{user.roles.join(joiner)}."
-        else
-          msg.send "#{name} is nothing to me."
-      else if users.length > 1
-        msg.send getAmbiguousUserText users
-      else
-        msg.send "#{name}? Never heard of 'em"
-
-  robot.respond /@?([\w .\-_]+) is (["'\w: \-_]+)[.!]*$/i, (msg) ->
-    name    = msg.match[1].trim()
-    newRole = msg.match[2].trim()
-
-    unless name in ['', 'who', 'what', 'where', 'when', 'why']
-      unless newRole.match(/^not\s+/i)
-        users = robot.brain.usersForFuzzyName(name)
-        if users.length is 1
-          user = users[0]
-          user.roles = user.roles or [ ]
-
-          if newRole in user.roles
-            msg.send "I know"
-          else
-            user.roles.push(newRole)
-            if name.toLowerCase() is robot.name.toLowerCase()
-              msg.send "Ok, I am #{newRole}."
-            else
-              msg.send "Ok, #{name} is #{newRole}."
-        else if users.length > 1
-          msg.send getAmbiguousUserText users
-        else
-          msg.send "I don't know anything about #{name}."
-
-  robot.respond /@?([\w .\-_]+) is not (["'\w: \-_]+)[.!]*$/i, (msg) ->
-    name    = msg.match[1].trim()
-    newRole = msg.match[2].trim()
-
-    unless name in ['', 'who', 'what', 'where', 'when', 'why']
-      users = robot.brain.usersForFuzzyName(name)
-      if users.length is 1
-        user = users[0]
-        user.roles = user.roles or [ ]
-
-        if newRole not in user.roles
-          msg.send "I know."
-        else
-          user.roles = (role for role in user.roles when role isnt newRole)
-          msg.send "Ok, #{name} is no longer #{newRole}."
-      else if users.length > 1
-        msg.send getAmbiguousUserText users
-      else
-        msg.send "I don't know anything about #{name}."
-


modifications days 2013-03-11 p2

modifications days 2013-03-11 p2

--- vendor/assets/javascripts/lol_framework/Datatable.coffee
+++ vendor/assets/javascripts/lol_framework/Datatable.coffee
@@ -58,65 +58,68 @@
   </table>
   *-* Setting manually configuration *-*
   var datatable = new Lol.Datatable({
-    debug         : true,
-    selectable    : true,
-    target        : '#lol-datatable',
-    delayDblclick : 3000,
+    debug:         false,
+    selectable:    true,
+    target:        null,
+    delayDblclick: 0,
     classes: {
-      activeRow : 'active_row'
+      activeRow: 'active_row'
     },
     contextMenu: {
-      delete: function(row, object) {
-        // See default configuration
+      "delete": function(row, object) {
+        //see documentation
       },
-      edit: function(row, object) {
-        object.debug(row.data(object.dataset.editLink));
-        window.location = row.data(object.dataset.editLink);
-        return false; },
-      view: function(row, object) {
-        window.location = row.data(object.dataset.showLink);
-        return false; },
-      iconView : 'icon-eye-open icon-white',
-      useIcons : true,
+      edit:     function(row, object) {
+        //see documentation
+      },
+      view:     function(row, object) {
+        //see documentation
+      },
+      iconView:   'icon-eye-open',
+      iconEdit:   ' icon-edit',
+      iconDelete: 'icon-remove',
+      useIcons:   true,
+      model: null
     },
     configuration: {
-      aaSortingFixed : null,
-      aoColumnDefs   : null,
-      aoColumns      : null,
+      aaSortingFixed: null,
+      aoColumnDefs: null,
+      aoColumns: null,
       asStripeClasses: null,
-      bAutoWidth     : true,
-      bDeferRender   : false,
-      bDestroy       : false,
-      bFilter        : true,
-      bInfo          : true,
-      bJQueryUI      : false,
-      bLengthChange  : false,
-      bPaginate      : true,
-      bProcessing    : false,
-      bRetrieve      : false,
-      bScrollAutoCss : true,
+      bAutoWidth: true,
+      bDeferRender: false,
+      bDestroy: false,
+      bFilter: false,
+      bInfo: false,
+      bJQueryUI: false,
+      bLengthChange: false,
+      bPaginate: false,
+      bProcessing: false,
+      bRetrieve: false,
+      bScrollAutoCss: true,
       bScrollCollapse: false,
       bScrollInfinite: false,
-      bServerSide    : false,
-      bSort          : true,
-      bSortCellsTop  : false,
-      bSortClasses   : true,
-      bStateSave     : false,
+      bServerSide: false,
+      bSort: true,
+      bSortCellsTop: false,
+      bSortClasses: true,
+      bStateSave: false,
       iCookieDuration: 7200,
-      iDeferLoading  : null,
-      iDisplayLength : 20,
-      iDisplayStart  : 0,
-      iScrollLoadGap : 100,
-      iTabIndex      : 0,
-      sAjaxDataProp  : "aaData",
-      sAjaxSource    : null,
-      sCookiePrefix  : "SpryMedia_DataTables_",
-      sDom           : "lfrtip",
+      iDeferLoading: null,
+      iDisplayLength: 20,
+      iDisplayStart: 0,
+      iScrollLoadGap: 100,
+      iTabIndex: 0,
+      sAjaxDataProp: "aaData",
+      sAjaxSource: null,
+      sCookiePrefix: "SpryMedia_DataTables_",
+      sDom: "lfrtip",
       sPaginationType: "two_button",
-      sScrollX       : "",
-      sScrollXInner  : "",
-      sScrollY       : "",
-      sServerMethod  : "GET" }
+      sScrollX: "",
+      sScrollXInner: "",
+      sScrollY: "",
+      sServerMethod: "GET"
+		}
   });
 ###
 class Lol.Datatable extends Lol.Core
@@ -133,6 +136,7 @@
     @dataset = Lol.datatable.private.dataset
     @setTable()
     @setConfigTable()
+    @setModel()
     @setTranslation()
     @setAjax()
     @generate()
@@ -216,6 +220,8 @@
       _this.setActiveRow(row)
       _this.createMenuContext(row, event)
       false
+  setModel: ->
+    @settings.contextMenu.model = @table.data(Lol.datatable["private"].dataset.modelName)
   setTable: ->
     @table = @settings.target
   setTranslation: ->
@@ -273,9 +279,8 @@
       delete: (row, object)->
         window.lol_temp_fn_model_destroy = [row,object]
         attrs =
-          "data-datatable-model-name": row.data(Lol.datatable.private.dataset.modelName)
+          "data-datatable-model-name": object.settings.contextMenu.model
           "data-datatable-model-id"  : row.data(Lol.datatable.private.dataset.modelId)
-        console.log attrs
         new Lol.Modal
           buttons  : 'OK_CANCEL'
           content  : Lol.t('datatable_confirm_delete')


Social: add session token into request data

--- workers/social/lib/social/models/socialapi/helper.coffee
+++ workers/social/lib/social/models/socialapi/helper.coffee
@@ -59,6 +59,7 @@
       options.accountId       = socialApiId
       options.accountNickname = nickname
       options.showExempt    or= delegate.isExempt
+      options.sessionToken   = client.sessionToken

       bareRequest funcName, options, callback


Pull routes out of app.coffee into routes.coffee.

--- app/app.coffee
+++ app/app.coffee
@@ -1,17 +1,5 @@
 app = angular.module 'qiprofile', [
-  'ngRoute', 'ui.bootstrap', 'angularCharts',
-  'qiprofile.services', 'qiprofile.controllers', 'qiprofile.filters', 'qiprofile.directives'
+  'ngRoute', 'ui.bootstrap', 'nvd3ChartDirectives',
+  'qiprofile.services', 'qiprofile.controllers', 'qiprofile.filters',
+  'qiprofile.directives', 'qiprofile.routes'
 ]
-
-app.config ['$routeProvider', '$locationProvider',
-  ($routeProvider, $locationProvider) ->
-    $routeProvider
-      .when '/qiprofile',
-        templateUrl: '/partials/subject_list.html'
-        controller:  'SubjectListCtrl'
-      .when '/qiprofile/:collection/subject/:subject',
-        templateUrl: '/partials/subject_detail.html'
-        controller:  'SubjectDetailCtrl'
-
-    $locationProvider.html5Mode true
-]


Fix destoryed turret still absorbing bullets

--- src/lazerscripts/stage1/turret.coffee
+++ src/lazerscripts/stage1/turret.coffee
@@ -33,6 +33,7 @@
   onKilled: ->
     @leaveAnimation @sequence(
       @deathDecoy()
+      @sendToBackground(1.0)
       @bigExplosion()
       @wait(400)
       @bigExplosion()


Re-enable full test suite

Re-enable full test suite

--- spec/ass-spec.coffee
+++ spec/ass-spec.coffee
@@ -7,10 +7,10 @@
 # NOTE
 # Manually specify {fixtures} if you only want to run specific tests.
 # A {fixture} is a relative path + filename (without extension).
-fixtures = [
-  # "flavors/github/tables"
-  "issues"
-]
+# fixtures = [
+#   # "flavors/github/tables"
+#   "issues"
+# ]

 # Automatically generate the {fixtures} array from the file system.
 # Include all .ass files found inside /spec/fixtures, by their relative path


storage: slightly improve close_all_projects

--- salvus/storage.coffee
+++ salvus/storage.coffee
@@ -535,7 +535,9 @@
         unset_loc  : true          # set location to undefined in the database
         wait_for_replicate : true  # make sure that we successfully replicate each project everywhere, or returns an error.
         limit      : 10
+        ttl        : undefined   # if given is a time in seconds; any project with last_edited within ttl (or timeout_disabled set) is not killed
         cb         : required
+
     dbg = (m) -> winston.debug("close_all_projects(host:#{opts.host},unset_loc:#{opts.unset_loc}): #{m}")
     errors = {}
     projects = undefined
@@ -544,17 +546,25 @@
             dbg("querying database...")
             database.select
                 table   : 'projects'
-                columns : ['project_id', 'location']
+                columns : ['project_id', 'location', 'last_edited', 'timeout_disabled']
                 json    : ['location']
                 limit   : 1000000   # TODO: stupidly slow
                 cb      : (err, result) ->
                     if result?
-                        projects = (x[0] for x in result when x[1]?.host == opts.host)
+                        if opts.ttl? and opts.ttl
+                            cutoff = misc.mswalltime() - opts.ttl*1000  # cassandra timestamps come back in ms since UTC epoch
+                            projects = (x[0] for x in result when x[1]?.host == opts.host and (not x[3] and x[2] < cutoff))
+                        else
+                            projects = (x[0] for x in result when x[1]?.host == opts.host)
+
                     dbg("got #{projects.length} projects")
                     cb(err)
         (cb) ->
             dbg("closing projects...")
+            cnt = 0
             f = (project_id, cb) ->
+                cnt += 1
+                dbg("**************\nclosing project #{cnt}/#{projects.length}\n**********")
                 close_project
                     project_id : project_id
                     host       : opts.host


Make sure setCookies is set on init

Make sure setCookies is set on init

--- src/client/api.coffee
+++ src/client/api.coffee
@@ -14,6 +14,8 @@

     data = @currentConfig.getRemote('data')
     authScope = if data.headers?['Hull-Auth-Scope'] then data.headers['Hull-Auth-Scope'].split(":")[0] else ''
+
+    @currentUser.setCookies(data.headers, currentConfig.get('appId')) if (data.headers?)

     @_message = (params, userSuccessCallback, userErrorCallback)=>


fixing a bug

fixing a bug

--- kurea.base/config.coffee
+++ kurea.base/config.coffee
@@ -20,7 +20,7 @@
     @route "idle.player", path: "/idle/:name", data: ->
       player = IdlePlayers.findOne {name:@params.name}
       return if not player
-      player.personalityString = if player.personalityStrings.length > 0 then player.personalityStrings.join '' else "No personalities selected"
+      player.personalityString = if player.personalityStrings?.length > 0 then player.personalityStrings.join '' else "No personalities selected"
       player.equipment = _.sortBy player.equipment, (item) -> item.type
       Session.set "player", player
       player


failing specs for branching within branched choices

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -2,6 +2,8 @@
 Root = require '../lib/Root'

 describe 'Thenable', ->
+
+

   #8888888ba                          88
@@ -342,6 +344,7 @@
           chai.expect(res[0]).to.be.true
           chai.expect(c.availableItems().length).to.equal 2
           done()
+

@@ -1331,13 +1334,6 @@
                 c.branch 'seven', (b, data) ->
                   data + '-seven'

-          #
-          # @bergie, uncommenting this & commennting out 'subtree-branching' makes this spec pass???
-          #
-          #.maybe failedComponent
-          #.else (c,d) ->
-          #  d
-
            .all [component]
            .then (n,results) ->
              results[0]
@@ -1352,6 +1348,252 @@
           done()

+    describe.only 'merge nested branches w/ contest', ->
+
+      merger = (splitter) ->
+        return (choice) ->
+          plucked = null
+          choice.tree()
+          .deliver()
+          .contest [splitter]
+          , (c, results) ->
+            plucked = results.map (r) ->
+              r.value
+            results[0]
+          .else (c,d) ->
+            console.log "Splitter failed", d
+            d
+          .then ->
+            plucked
+
+      test = (splitter, expected, done) ->
+        Root()
+        .deliver({})
+        .then merger(splitter)
+        .else (choice, results) ->
+          console.log "FALIED!", results
+          results
+        .finally (choice, results) ->
+          chai.expect(results).to.eql expected
+          done()
+
+      it 'v0', (done) ->
+        splitter = (choice,data) ->
+          choice.tree()
+          .deliver()
+          .then (choice,data) ->
+            choice.branch "1", ->
+              1
+            choice.branch "2", ->
+              2
+            choice.branch "3", ->
+              3
+        test splitter, [1,2,3], done
+
+      it 'v1', (done) ->
+        splitter = (choice) ->
+          choice.tree()
+          .deliver('-')
+          .then (choice,data) ->
+            choice.branch "1", (b,d) ->
+              d += '1-'
+              d
+            choice.branch "2", (b,d) ->
+              d += '2-'
+              d
+          .then (choice,data) ->
+            choice.branch "11", (b,d) ->
+              d += '11-'
+              d
+            choice.branch "22", (b,d) ->
+              d += '22-'
+              d
+          .then (choice,data) ->
+            data
+        test splitter, ['-1-11-','-2-11-','-1-22-','-2-22-'], done
+
+      it 'v2', (done) ->
+        splitter = (choice,data) ->
+          choice.tree()
+          .deliver({})
+          .then (choice,data) ->
+            choice.branch "1", (b) ->
+              b.tree()
+              .deliver({})
+              .then (choice,data) ->
+                1
+            choice.branch "2",  (b) ->
+              b.tree()
+              .deliver({})
+              .then (choice,data) ->
+                2
+        test splitter, [1,2], done
+
+      it 'v3', (done) ->
+        splitter = (choice,data) ->
+          choice.tree()
+          .deliver({})
+          .then (choice,data) ->
+            choice.branch "0", (b) ->
+              b.tree()
+              .deliver({})
+              .then (choice,data) ->
+                choice.branch "1", (b) ->
+                  1
+                choice.branch "2",  (b) ->
+                  2
+
+        test splitter, [1,2], done
+
+      it 'v4', (done) ->
+        splitter = (choice,data) ->
+          choice.tree()
+          .deliver({})
+          .then (choice,data) ->
+            choice.branch "0"
+            .then (choice,data) ->
+              choice.branch "1", (b) ->
+                1
+              choice.branch "2",  (b) ->
+                2
+
+        test splitter, [1,2], done
+
+      it 'v5', (done) ->
+
+        splitter = (choice,data) ->
+          choice.tree()
+          .deliver('')
+          .then (choice,data) ->
+            choice.branch "1", (b,data) ->
+              data += 'a-'
+              b.tree()
+              .deliver(data)
+              .then (choice,data) ->
+                choice.branch "sub1-1", (b,data) ->
+                  data += '11-'
+                  data
+                choice.branch "sub1-2", (b,data) ->
+                  data += '12-'
+                  data
+            choice.branch "2",  (b,data) ->
+              data += '-b-'
+              b.tree()
+              .deliver(data)
+              .then (choice,data) ->
+                choice.branch "sub2-1", (b,data) ->
+                  data += '21'
+                  data
+                choice.branch "sub2-2", (b,data) ->
+                  data += '22'
+                  data
+          .then (choice,data) ->
+            data
+
+
+        test splitter, ['a-11-b-21','a-12-b-21','a-11-v-22','a-12-b-22'], done
+
+      it 'v6', (done) ->
+        splitter = (choice) ->
+          choice.tree()
+          .deliver()
+          .then (choice) ->
+            choice.tree()
+            .deliver()
+            .then (choice) ->
+              choice.branch "11", (b) ->
+                '11'
+              choice.branch "12", (b) ->
+                '12'
+          .then (choice,data) ->
+            choice.tree()
+            .deliver(data)
+            .then (choice,data) ->
+              choice.branch "21", (b,data) ->
+                data += '-21'
+                data
+              choice.branch "22", (b,data) ->
+                data += '-22'
+                data
+          .then (choice,data) ->
+            data
+
+        test splitter, ['11-21','12-21','11-22','12-22'], done
+
+
+      it 'v7 - branch.branch', (done) ->
+        splitter = (choice) ->
+          choice.tree()
+          .deliver()
+          .then (choice,data) ->
+            choice.branch "1", (b) ->
+              b.branch "sub1-1", (b) ->
+                11
+              b.branch "sub1-2", (b) ->
+                12
+            choice.branch "2",  (b) ->
+              b.branch "sub2-1", (b) ->
+                21
+              b.branch "sub2-2", (b) ->
+                22
+
+        test splitter, [11,12,21,22], done
+
+
+      it 'v8', (done) ->
+
+        splitter = (choice,data) ->
+          choice.tree()
+          .deliver('-')
+          .then (choice,data) ->
+            # branch()
+            # .then
+            #   branch()
+            #   .then
+            #   branch()
+            #   .then
+            choice.branch "1", (b,data) ->
+              data
+            .then (choice,data) ->
+              choice.branch "a", (b,data) ->
+                data
+              .then (choice,data) ->
+                data += '1a-'
+                data
+              choice.branch "b", (b,data) ->
+                data
+              .then (choice,data) ->
+                data += '1b-'
+                data
+            # branch()
+            #   tree()
+            #   .then()
+            #     branch()
+            #       tree()
+            #       .then()
+            #     branch()
+            #       tree()
+            #       .then()
+            choice.branch "2",  (b,data) ->
+              b.tree()
+              .deliver(data)
+              .then (choice,data) ->
+                choice.branch "a", (b,data) ->
+                  b.tree()
+                  .deliver(data)
+                  .then (choice,data) ->
+                    data += '2a-'
+                    data
+                choice.branch "b", (b,data) ->
+                  b.tree()
+                  .deliver(data)
+                  .then (choice,data) ->
+                    data += '2b-'
+                    data
+          .then (choice,data) ->
+            data
+
+        test splitter, ['-1a-2a-','-1b-2a-','-1a-2b-','-1b-2b-'], done

    #d88888ba                                   88


Removing uneeded code.

Removing uneeded code.

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -70,9 +70,6 @@
         @config = config
         @i = 0

-        # State about previous state in the tree.
-        @inString = false
-
     # Return a list of errors encountered in the given source.
     lint : () ->
         errors = []
@@ -88,7 +85,6 @@
         [type, value, line] = token
         switch type
             when "INDENT" then @lintIndentation(token)
-            when "STRING" then @lintString(token)
             else null

     # Return an error if the given indentation token is not correct.
@@ -106,9 +102,6 @@
             error = {reason: MESSAGES.INDENTATION_ERROR + info, line: line}
         else
             null
-
-    lintString : (token) ->
-        null

     # Return the next token in the stream.
     peek : (n=1) ->


Insert in collection write items in file

collection file was created but calling insert() was not actually writting items in it.

--- src/collection.coffee
+++ src/collection.coffee
@@ -38,6 +38,7 @@
 				elem['$created'] = date
 				elem['$updated'] = date
 				@items.push elem
+			@save()
 		else
 			date = (new Date).toJSON()
 			@header.lcid++
@@ -45,6 +46,7 @@
 			element['$created'] = date
 			element['$updated'] = date
 			@items.push element
+		@save()

 	# Retrieving data functions
 	get: (cid) ->  _.findWhere(@items, {'cid': cid})
@@ -106,4 +108,4 @@
 				return true
 		return false

-module.exports = Collection+module.exports = Collection


'Update' method for contenteditable

'Update' method for contenteditable

--- jquery.mentions.coffee
+++ jquery.mentions.coffee
@@ -415,6 +415,11 @@
                 sel.removeAllRanges()
                 sel.addRange range

+    update: ->
+        @_initValue()
+        @_initEvents()
+        @input.focus()
+
     append: (pieces...) ->
         value = @input.html()
         for piece in pieces


Tests: Fix indent and bump timeout a bit

--- spec/examples.coffee
+++ spec/examples.coffee
@@ -12,13 +12,13 @@
   stderr = null
   describe 'with a valid input board', ->
     it 'executes without error', (done) ->
-      @timeout 2000
+      @timeout 4000
       # From http://www2.warwick.ac.uk/fac/sci/moac/people/students/peter_cock/python/sudoku
       board = '............942.8.16.....29........89.6.....14..25......4.......2...8.9..5....7..'
       jsJobRun example('sudoku.js'), { 'board': board }, (err, o, e) ->
         [stdout, stderr] = [o, e]
         chai.expect(err).to.not.exist
         done()
-     it 'has a solution', () ->
-        # correctness is ensured in the tests in finitedomain
-        chai.expect(stdout).to.contain '"I9": 2'
+    it 'has a solution', () ->
+      # correctness is ensured in the tests in finitedomain
+      chai.expect(stdout).to.contain '"I9": 2'


use a mockRepo object instead of a real GitRepository object

--- spec/git-spec.coffee
+++ spec/git-spec.coffee
@@ -5,6 +5,11 @@

 pathToRepoFile = Path.get "~/.atom/packages/git-plus/lib/git.coffee"
 pathToSubmoduleFile = Path.get "~/.atom/packages/git-plus/spec/foo/foo.txt"
+
+mockRepo = {
+  getWorkingDirectory: () -> 'dir'
+  refreshStatus: () -> undefined
+}

 describe "Git-Plus git module", ->
   describe "git.getRepo", ->
@@ -40,31 +45,27 @@
     it "calls git.cmd with ['add', '--all', {fileName}]", ->
       spyOn(git, 'cmd').andCallFake () -> Promise.resolve true
       waitsForPromise ->
-        repo = git.getSubmodule(pathToSubmoduleFile)
-        git.add(repo, file: pathToSubmoduleFile).then (success) ->
-          expect(git.cmd).toHaveBeenCalledWith(['add', '--all', pathToSubmoduleFile], cwd: repo.getWorkingDirectory())
+        git.add(mockRepo, file: pathToSubmoduleFile).then (success) ->
+          expect(git.cmd).toHaveBeenCalledWith(['add', '--all', pathToSubmoduleFile], cwd: mockRepo.getWorkingDirectory())

     it "calls git.cmd with ['add', '--all', '.'] when no file is specified", ->
       spyOn(git, 'cmd').andCallFake () -> Promise.resolve true
       waitsForPromise ->
-        repo = git.getSubmodule(pathToSubmoduleFile)
-        git.add(repo).then (success) ->
-          expect(git.cmd).toHaveBeenCalledWith(['add', '--all', '.'], cwd: repo.getWorkingDirectory())
+        git.add(mockRepo).then (success) ->
+          expect(git.cmd).toHaveBeenCalledWith(['add', '--all', '.'], cwd: mockRepo.getWorkingDirectory())

     it "calls git.cmd with ['add', '--update'...] when update option is true", ->
       spyOn(git, 'cmd').andCallFake () -> Promise.resolve true
       waitsForPromise ->
-        repo = git.getSubmodule(pathToSubmoduleFile)
-        git.add(repo, update: true).then (success) ->
-          expect(git.cmd).toHaveBeenCalledWith(['add', '--update', '.'], cwd: repo.getWorkingDirectory())
+        git.add(mockRepo, update: true).then (success) ->
+          expect(git.cmd).toHaveBeenCalledWith(['add', '--update', '.'], cwd: mockRepo.getWorkingDirectory())

   describe "git.reset", ->
     it "resets and unstages all files", ->
       spyOn(git, 'cmd').andCallFake () -> Promise.resolve true
       waitsForPromise ->
-        repo = git.getSubmodule(pathToSubmoduleFile)
-        git.reset(repo).then () ->
-          expect(git.cmd).toHaveBeenCalledWith ['reset', 'HEAD'], cwd: repo.getWorkingDirectory()
+        git.reset(mockRepo).then () ->
+          expect(git.cmd).toHaveBeenCalledWith ['reset', 'HEAD'], cwd: mockRepo.getWorkingDirectory()

   describe "git.stagedFiles", ->
     it "returns an empty array when there are no staged files", ->
@@ -150,10 +151,6 @@
         git.status(git.getSubmodule(pathToSubmoduleFile)).then () -> expect(true).toBeTruthy()

     describe "git.refresh", ->
-      mockRepo = {
-        getWorkingDirectory: () -> 'dir'
-        refreshStatus: () -> undefined
-      }
       it "calls git.cmd with 'add' and '--refresh' arguments for each repo in project", ->
         spyOn(git, 'cmd').andCallFake () ->
           args = git.cmd.mostRecentCall.args[0]


Better test names

Better test names

--- spec/git-spec.coffee
+++ spec/git-spec.coffee
@@ -36,7 +36,7 @@
         git.cmd().then () -> expect(true).toBeTruthy()

   describe "git.add", ->
-    it "stages a file", ->
+    it "calls git.cmd with ['add', '--all', {fileName}]", ->
       spyOn(git, 'cmd').andCallFake () ->
         args = git.cmd.mostRecentCall.args[0]
         if args[0] is 'add' and args[1] is '--all' and args[2] is pathToSubmoduleFile
@@ -45,7 +45,7 @@
         repo = git.getSubmodule(pathToSubmoduleFile)
         git.add(repo, file: pathToSubmoduleFile).then (success) -> expect(success).toBe true

-    it "stages all files if no file is specified", ->
+    it "calls git.cmd with ['add', '--all', '.'] when no file is specified", ->
       spyOn(git, 'cmd').andCallFake () ->
         args = git.cmd.mostRecentCall.args[0]
         if args[0] is 'add' and args[1] is '--all' and args[2] is '.'
@@ -54,7 +54,7 @@
         repo = git.getSubmodule(pathToSubmoduleFile)
         git.add(repo).then (success) -> expect(success).toBe true

-    it "stages modified files when the update option is true", ->
+    it "calls git.cmd with ['add', '--update'...] when update option is true", ->
       spyOn(git, 'cmd').andCallFake () ->
         args = git.cmd.mostRecentCall.args[0]
         if args[0] is 'add' and args[1] is '--update'
@@ -62,7 +62,7 @@
       waitsForPromise ->
         repo = git.getSubmodule(pathToSubmoduleFile)
         git.add(repo, update: true).then (success) -> expect(success).toBe true
-
+
   describe "git.reset", ->
     it "resets and unstages all files", ->
       spyOn(git, 'cmd').andCallThrough()


tests: Use dedicated component name in setsource test

--- spec/protocol.coffee
+++ spec/protocol.coffee
@@ -286,6 +286,7 @@
   describe 'adding a component', ->
     componentName = 'SetSource'
     componentCode = fs.readFileSync(__dirname+'/fixtures/ProduceFoo.coffee', 'utf-8')
+    componentCode = componentCode.replace(/ProduceFoo/g, componentName)

     it 'should become available', (done) ->
       receivedAck = false
@@ -298,8 +299,8 @@
           chai.expect(protocol).to.equal 'component'
           chai.expect(payload).to.include.keys ['name', 'code', 'language', 'library']
           chai.expect(payload.language).to.equal 'coffeescript'
-          chai.expect(payload.code).to.include "component: 'ProduceFoo'"
-          chai.expect(payload.code).to.include "module.exports = ProduceFoo"
+          chai.expect(payload.code).to.include "component: '#{componentName}'"
+          chai.expect(payload.code).to.include "module.exports = #{componentName}"
           receivedAck = true
           console.log 'got ACK'
         else if command == 'component'
@@ -334,8 +335,8 @@
         chai.expect(payload.library).to.equal options.config.namespace
         chai.expect(payload.name).to.equal 'SetSource'
         chai.expect(payload.language).to.equal 'coffeescript'
-        chai.expect(payload.code).to.include "component: 'ProduceFoo'"
-        chai.expect(payload.code).to.include "module.exports = ProduceFoo"
+        chai.expect(payload.code).to.include "component: '#{componentName}'"
+        chai.expect(payload.code).to.include "module.exports = #{componentName}"
         done()

       source =
@@ -390,7 +391,7 @@
         chai.expect(graph).to.include.keys ['connections', 'processes']
         chai.expect(graph.processes).to.include.keys ['mycoffeescriptproducer']
         chai.expect(graph.processes.mycoffeescriptproducer).to.eql
-          component: 'ProduceFoo' # NOTE: Different from the name the component was registered as
+          component: componentName
           metadata:
             label: 'mycoffeeproducer'
             x: 2


Use a hash of arrays to keep track of queried IDs

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -17,8 +17,7 @@
 HDL_REGEX = /^https?:\/\/hdl\.handle\.net\/2027\/(.+)\/?/
 HT_CATALOG_REGEX = /^https?:\/\/catalog\.hathitrust\.org\/Record\/(\d{9})/

-QUERIED_OCLC_IDS = []
-QUERIED_LCCN_IDS = []
+QUERIED_IDS = {}

 html_id = (input) ->
   input.replace(/[\/:$.,'-]/g,'_')
@@ -193,9 +192,9 @@
   console.log ia_id

 lccn_query = (lccn_id, score = 0) ->
-  if lccn_id not in QUERIED_LCCN_IDS
+  if lccn_id not in QUERIED_IDS['lccn']
     console.log "lccn_query: #{lccn_id}"
-    QUERIED_LCCN_IDS.push lccn_id
+    QUERIED_IDS['lccn'].push lccn_id
     $.ajax "#{GOOGLE_BOOKS_URI}?q=lccn:#{lccn_id}&key=#{GOOGLE_BOOKS_API_KEY}",
       type: 'GET'
       cache: true
@@ -215,9 +214,9 @@
             gb_query(item.id)

 oclc_query = (oclc_id, score = 0) ->
-  if oclc_id not in QUERIED_OCLC_IDS
+  if oclc_id not in QUERIED_IDS['oclc']
     console.log "oclc_query: #{oclc_id}"
-    QUERIED_OCLC_IDS.push oclc_id
+    QUERIED_IDS['oclc'].push oclc_id
     $.ajax "#{GOOGLE_BOOKS_URI}?q=oclc:#{oclc_id}&key=#{GOOGLE_BOOKS_API_KEY}",
       type: 'GET'
       cache: true
@@ -285,9 +284,15 @@
   gb_biblio_query(gb_id, score)
   console.log gb_id

+reset_queried_ids = ->
+  QUERIED_IDS = {
+    'isbn': [],
+    'oclc': [],
+    'lccn': []
+  }
+
 process_identifier = (identifier_string) ->
-  QUERIED_OCLC_IDS = []
-  QUERIED_LCCN_IDS = []
+  reset_queried_ids()
   $('#results').empty()
   $('#results').append($('<table/>',{id: 'table', class: 'display', cellspacing: 0, width: '100%'}))
   $('#table').DataTable({


[Config] Remove spaces and hyphens from "fromNumber"

Twilio wants the test number to be free of spaces and hyphens, so removing them from the example.

--- server/secret.coffee
+++ server/secret.coffee
@@ -6,7 +6,7 @@
     testing:
       AccountSID: 'AC_YOUR_TWILIO_TEST_CREDENTIALS_ACCOUNT_SID'
       AuthToken: 'YOUR_TWILIO_TEST_CREDENTIALS_AUTH_TOKEN'
-      fromNumber: '+1 500-555-0006'
+      fromNumber: '+15005550006'

   rethinkdb:
     discovery: false


Use meteor.setInterval

Use meteor.setInterval

--- server/server.coffee
+++ server/server.coffee
@@ -174,12 +174,10 @@
               Characters.update({_id: character._id}, {$set: update})

 checkActiveCharacters = ->
-  Fiber(->
-    lastAcceptableTime = (new Date().getTime()) - 10*1000
-    Characters.update({active: true, lastActiveTime: {$lt: lastAcceptableTime}}, {$set: {active: false}}, {multi: true})
-  ).run()
+  lastAcceptableTime = (new Date().getTime()) - 10*1000
+  Characters.update({active: true, lastActiveTime: {$lt: lastAcceptableTime}}, {$set: {active: false}}, {multi: true})

-setInterval checkActiveCharacters, 5000
+Meteor.setInterval checkActiveCharacters, 5000

 String::hashCode = ->
   hash = 0


save to Memory current attacking priorities


Fixing issue when dataset id was not allowed in HTML/SVG. Fix for updates when dataset was already removed.

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -180,7 +180,7 @@
                     @options.zoom
                         .scale(@options.lastZoom.scale)
                         .translate(@options.lastZoom.translate)
-
+
                     @tooltipBrushMin.transition()
                         .duration(100)
                         .style("opacity", .9)
@@ -281,7 +281,7 @@

     #         r = @svg.selectAll('.timetick')
     #             .data([@timetickDate])
-
+
     #         r.enter().append('rect')
     #             .attr('class', 'timetick')
     #             .attr('x', (a) =>  @scales.x(a) )
@@ -363,7 +363,7 @@

         r = datasetElement.selectAll('rect')
             .data(records)
-
+
         r.enter().append('rect')
             .attr('x', (record) => @scales.x(new Date(record[0])) )
             .attr('y', - (@options.ticksize + 3) * dataset.index + -(@options.ticksize-2) )
@@ -428,7 +428,7 @@
             .attr('fill', 'none')
             .attr('transform', "translate(0,"+ (-@options.height+29)+")")

-
+
         step = (@scales.y.domain()[1] - @scales.y.domain()[0])/4
         @axis.y.tickValues(
             d3.range(@scales.y.domain()[0],@scales.y.domain()[1]+step, step)
@@ -459,8 +459,9 @@
     # this function acually draws a dataset

     redrawDataset: (datasetId) ->
-        datasetElement = @svg.select("g.datasets #dataset-#{datasetId}")
         dataset = @datasets[datasetId]
+        if not dataset
+            return

         records = dataset.getRecords()
         paths = dataset.getPaths()
@@ -468,7 +469,7 @@
         color = dataset.color

         if paths and paths.length
-            @drawPaths(datasetElement, dataset, paths)
+            @drawPaths(dataset.element, dataset, paths)
         else if records and records.length
             points = dataset.getRecords().filter((record) =>
                 (@scales.x(new Date(record[1])) - @scales.x(new Date(record[0]))) < 5
@@ -476,8 +477,8 @@
             ranges = dataset.getRecords().filter((record) =>
                 (@scales.x(new Date(record[1])) - @scales.x(new Date(record[0]))) >= 5
             )
-            @drawRanges(datasetElement, dataset, ranges)
-            @drawPoints(datasetElement, dataset, points)
+            @drawRanges(dataset.element, dataset, ranges)
+            @drawPoints(dataset.element, dataset, points)

     # this function triggers the reloading of a dataset (sync)

@@ -595,20 +596,23 @@
         lineplot = false

         id = definition.id
+        @ordinal = @ordinal + 1

         if !definition.lineplot
             index = @options.datasetIndex++
             @svg.select('g.datasets')
                 .insert('g',':first-child')
                     .attr('class', 'dataset')
-                    .attr('id', "dataset-#{id}")
+                    .attr('id', "dataset-#{@ordinal}")
         else
             index = @options.linegraphIndex++
             lineplot = true
             @svg.select('g.datasets')
                 .append('g')
                     .attr('class', 'dataset')
-                    .attr('id', "dataset-#{id}")
+                    .attr('id', "dataset-#{@ordinal}")
+
+        element = @svg.select("g.datasets #dataset-#{@ordinal}")

         @datasets[id] = new Dataset({
             id: id,
@@ -617,10 +621,11 @@
             source: definition.source,
             records: definition.records,
             lineplot: lineplot,
-            debounceTime: @options.debounce
+            debounceTime: @options.debounce,
+            ordinal: @ordinal,
+            element: element
         })
-
-
+
         @reloadDataset(id)

     # remove a dataset. redraws.
@@ -629,14 +634,15 @@

         i = @datasets[id].index
         lp = @datasets[id].lineplot
+        ordinal = @datasets[id].ordinal
         delete @datasets[id]

         if lp
             @options.linegraphIndex--
         else
             @options.datasetIndex--
-
-        d3.select(@element).select("g.dataset#dataset-#{id}").remove()
+
+        d3.select(@element).select("g.dataset#dataset-#{ordinal}").remove()

         for dataset of @datasets
             if lp == @datasets[dataset].lineplot
@@ -703,7 +709,7 @@
 # Dataset utility class for internal use only
 class Dataset
     constructor: (options) ->
-        { @id,  @color, @source, @sourceParams, @index, @records, @paths, @lineplot } = options
+        { @id,  @color, @source, @sourceParams, @index, @records, @paths, @lineplot, @ordinal, @element } = options
         @syncDebounced = debounce(@sync, options.debounceTime)

     getSource: ->
@@ -739,5 +745,3 @@

 module.exports = TimeSlider
-
-


search for item using bucketHash

search for item using bucketHash

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -80,13 +80,14 @@
   else
     return null

+# returns bucketHash associated with each weapon slot
 checkWeaponSlot = (slot) ->
   if slot is 'primary'
-    return 1
+    return '1498876634'
   else if slot in ['special', 'secondary']
-    return 2
+    return '2465295065'
   else if slot is 'heay'
-    return 3
+    return '953998645'
   else
     return null

@@ -170,9 +171,18 @@

   makeRequest bot, endpoint, (response) ->
     data = response.data
-
-
-    itemInstanceId = data.items[weaponSlot].itemId
+    items = data.items
+
+    matchesBucketHash = (object) ->
+      "#{object.bucketHash}" is weaponSlot
+
+    item = items.filter(matchesBucketHash)
+    if item.length is 0
+      robot.send {room: bot.message.user.name}, "Something went wrong, couldn't find the requested item for this character."
+      deferred.reject()
+      return
+
+    itemInstanceId = item[0].itemId
     deferred.resolve(itemInstanceId)

   deferred.promise


Fix getting sale item categories

Fix getting sale item categories

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -59,9 +59,6 @@
       remainingIteration = vendors.length
       for vendorHash in vendors
         getVendorDetails(res, vendorHash).then (vendorDetails) ->
-
-          console.log 'vendorDetails ='
-          console.log vendorDetails

           for category in vendorDetails.saleItemCategories
             if category.categoryIndex in Constants.BOUNTIES_CATEGORY_INDICES
@@ -366,7 +363,7 @@

     vendorDetails =
       vendorName: response.definitions.vendorDetails[vendorHash].summary.vendorName,
-      salesItemCategories: response.data.saleItemCategories
+      saleItemCategories: response.data.saleItemCategories

     deferred.resolve(vendorDetails)


Improve the look of single-line commented things

--- src/controller.coffee
+++ src/controller.coffee
@@ -458,13 +458,21 @@
       rectangle.x -= GRAY_BLOCK_MARGIN; rectangle.y -= GRAY_BLOCK_MARGIN
       rectangle.width += 2 * GRAY_BLOCK_MARGIN; rectangle.height += 2 * GRAY_BLOCK_MARGIN

-      if (blockView.totalBounds.width - blockView.bounds[blockView.bounds.length - 1].width) < @mainCtx.measureText('/*').width
-        rectangle.height += @fontSize
+      bottomTextPosition = blockView.totalBounds.bottom() - blockView.distanceToBase[blockView.lineLength - 1].below - @fontSize
+
+      if (blockView.totalBounds.width - blockView.bounds[blockView.bounds.length - 1].width) < endWidth
+        if blockView.lineLength > 1
+          rectangle.height += @fontSize
+          bottomTextPosition = rectangle.bottom() - @fontSize - 5
+        else
+          rectangle.width += endWidth

       unless rectangle.equals(record.grayBox)
         record.grayBox = rectangle

         oldBounds = record.grayBoxPath?.bounds?() ? new @view.draw.NoRectangle()
+
+        startHeight = blockView.bounds[0].height + 10

         # Make the path surrounding the gray box (with rounded corners)
         record.grayBoxPath = path = new @view.draw.Path()
@@ -472,14 +480,22 @@
         path.push new @view.draw.Point rectangle.right(), rectangle.y + 5
         path.push new @view.draw.Point rectangle.right(), rectangle.bottom() - 5
         path.push new @view.draw.Point rectangle.right() - 5, rectangle.bottom()
-        path.push new @view.draw.Point rectangle.x + 5, rectangle.bottom()
-        path.push new @view.draw.Point rectangle.x, rectangle.bottom() - 5
-        path.push new @view.draw.Point rectangle.x, rectangle.y + GRAY_BLOCK_HANDLE_HEIGHT
-        path.push new @view.draw.Point rectangle.x - startWidth + 5, rectangle.y + GRAY_BLOCK_HANDLE_HEIGHT
-        path.push new @view.draw.Point rectangle.x - startWidth, rectangle.y + GRAY_BLOCK_HANDLE_HEIGHT - 5
+
+        if blockView.lineLength > 1
+          path.push new @view.draw.Point rectangle.x + 5, rectangle.bottom()
+          path.push new @view.draw.Point rectangle.x, rectangle.bottom() - 5
+        else
+          path.push new @view.draw.Point rectangle.x, rectangle.bottom()
+
+        # Handle
+        path.push new @view.draw.Point rectangle.x, rectangle.y + startHeight
+        path.push new @view.draw.Point rectangle.x - startWidth + 5, rectangle.y + startHeight
+        path.push new @view.draw.Point rectangle.x - startWidth, rectangle.y + startHeight - 5
         path.push new @view.draw.Point rectangle.x - startWidth, rectangle.y + 5
         path.push new @view.draw.Point rectangle.x - startWidth + 5, rectangle.y
+
         path.push new @view.draw.Point rectangle.x, rectangle.y
+

         path.bevel = true
         path.style = {
@@ -496,8 +512,9 @@
       @mainCtx.globalAlpha *= 0.8
       record.grayBoxPath.draw @mainCtx
       @mainCtx.fillStyle = '#000'
-      @mainCtx.fillText @mode.startComment, blockView.totalBounds.x - startWidth, blockView.totalBounds.y
-      @mainCtx.fillText @mode.endComment, blockView.totalBounds.right() - endWidth, record.grayBox.bottom() - @fontSize - GRAY_BLOCK_MARGIN
+      @mainCtx.fillText(@mode.startComment, blockView.totalBounds.x - startWidth,
+        blockView.totalBounds.y + blockView.distanceToBase[0].above - @fontSize)
+      @mainCtx.fillText(@mode.endComment, record.grayBox.right() - endWidth - 5, bottomTextPosition)
       @mainCtx.globalAlpha /= 0.8

       blockView.draw @mainCtx, rect, {


Hide the paletteGroup name if nullish or empty.

--- src/controller.coffee
+++ src/controller.coffee
@@ -1462,7 +1462,7 @@
         paletteHeaderRow.className = 'droplet-palette-header-row'
         @paletteHeader.appendChild paletteHeaderRow
         # hide the header if there is only one group, and it has no name.
-        if @paletteGroups.length is 1 and paletteGroup.name is ''
+        if @paletteGroups.length is 1 and !paletteGroup.name
           paletteHeaderRow.style.height = 0

       # Create the element itself


format code

format code

--- src/db.appsscript.coffee
+++ src/db.appsscript.coffee
@@ -42,11 +42,12 @@
 		else
 			now = @now()

-			entity.active = true
-			entity.until = -1
-			entity.signup = now
-			entity.lastUsed = now
-			entity.version = 0
+			entity.active	= true
+			entity.until	= -1
+			entity.signup	= now
+			entity.lastUsed	= now
+			entity.version	= 0
+
 			entity = @DB.save entity
 		entity


Clarify active faults legend. Fixes #2

Clarify active faults legend. Fixes #2

--- src/app.coffee
+++ src/app.coffee
@@ -66,7 +66,7 @@
       useD3: true
       description: '
Layer Description

This layer summarizes available data on active Arizona faults. If faults have been active during the past 2.5 million years (Quaternary period), then we consider that there is some chance they could generate large earthquakes. Generally, the more active the fault zone is, the more likely it is to generate earthquakes, and earthquakes are more likely to occur in regions with many Quaternary faults.

These data were originally compiled in 1998 as part of an effort coordinated by the USGS to compile data and map information on Quaternary faults throughout the world. The database has recently been revised with much more accurate fault mapping, incorporation of new data on fault activity, and inclusion of additional fault zones, primarily in northern Arizona. This database depends heavily on several previous compilations of data on Quaternary faults in Arizona. The first and most comprehensive of these is the state-wide compilation of neotectonic faults by Menges and Pearthree (1983), supplemented by a state-wide compilation of young faults by Euge and others (1992). More detailed regional geologic maps provided most of the data in northern Arizona and southeastern Arizona. The database includes information from all detailed investigations of fault zones in Arizona.

The data structure is set up to provide systematic information on each fault zone. Fault names are based primarily on published maps or reports. In cases where different names have been used for the fault, the alternative names are listed within the database. All of the faults are listed by name and number in the table on the following page. This table indicates where the data summary for each fault can be found, as well as the age of youngest activity and fault slip rate category. The individual fault data sheets include information on map and data sources, fault location, geologic setting of the fault, the geomorphic expression of the fault, recency of fault movement, fault slip rate(s), and fault zone length and orientation. Faults are grouped into slip rate categories of <0.02 mm/yr, < 0.2 mm/yr, and <1 mm/yr. Most faults in Arizona fall into the lowest slip-rate category, with a few faults in the higher categories. Reported lengths are for the whole fault zone, not cumulative length of each individual fault in the zone, and orientations are averages for the fault zone.

Citations:

Euge, K.M., Schell, B.A. and Lam, I.P., 1992, Development of seismic acceleration maps for Arizona: Arizona Department of Transportation Report No. AZ92-344, 327 p., 5 sheets, scale 1:1,000,000.

Menges, C.M. and Pearthree, P.A., 1983, Map of neotectonic (latest Pliocene - Quaternary) deformation in Arizona: Arizona Bureau of Geology and Mineral Technology Open-File Report 83-22, 48 p., map scale 1:500,000, 4 map sheets.
'
       legend: new app.models.Legend [
-          caption: "Holocene ( <10 ka )"
+          caption: "Within 10,000 years"
           attribute: "symbol"
           value: "2.13.2"
           imageTemplateId: "faultImage"
@@ -74,7 +74,7 @@
           imageInfo:
             color: "#FFA500"
         ,
-          caption: "Late Quaternary ( <750 ka )"
+          caption: "Within 750,000 years"
           attribute: "symbol"
           value: "2.13.3"
           imageTemplateId: "faultImage"
@@ -82,7 +82,7 @@
           imageInfo:
             color: "#008000"
         ,
-          caption: "Quaternary (Undifferentiated)"
+          caption: "Within 2.5 million years"
           attribute: "symbol"
           value: "2.13.4"
           imageTemplateId: "faultImage"
@@ -91,7 +91,7 @@
             color: "#800080"
       ],
         legendHeading: "Active Faults Latest Motion"
-        heading: "Latest Motion"
+        heading: "Most Recent Motion"
       layerOptions:
         styler: "symbol"
   ,


made dgshow suggestions more readable

made dgshow suggestions more readable

--- scripts/dgshow.coffee
+++ scripts/dgshow.coffee
@@ -77,7 +77,7 @@
         msg.send "There was an error showing the list."
         return
       msg.send "Suggestions in " + data.name + ":"
-      msg.send "- (" + card.name + ")["+card.desc+"]" for card in data.cards
+      msg.send "- " + card.name + " "+card.desc for card in data.cards

 createCard = (msg, cardName, cardUrl) ->
   Trello = require("node-trello")


add security

add security

--- scripts/fierce.coffee
+++ scripts/fierce.coffee
@@ -92,3 +92,6 @@

   robot.hear /(pause|paws)/i, (msg) ->
     msg.send "http://www.wppl.org/kidsteens/paws.jpg"
+
+  robot.hear /(security|secure)/i, (msg) ->
+    msg.send "<!--- TODO: add security --->"


Trying to fix broken bot.

Trying to fix broken bot.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -125,3 +125,17 @@
             # Outputs an inspection of recall which comes out as:
             # { masonest: 3670, awebot: 3860, knexem: 3455 }
             msg.send "Inspection: #{Util.inspect(recall)}"
+
+    robot.respond /top (\d*)$/i, (msg) ->
+        if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])
+            pointcount = msg.match[1]
+            recall = robot.brain.get 'winners'
+
+            scores = [username + " has " + points[username]] for username of recall
+
+            topscore = scores.sort((a, b) ->
+                b.points[username] - a.points[username]
+            ).slice(0, pointcount)
+
+            # msg.send "The top #{pointcount} users with the most points are: #{topscore}"
+            msg.send "Scores: #{topscore}"


Made making a move tutorial step nicer, and added make a move step

--- client/src/Tutorial.coffee
+++ client/src/Tutorial.coffee
@@ -285,24 +285,21 @@
       {
         modal: true
         header: "It's your turn"
-        content: "We must now make a move. Try clicking on one of the dice to move it to another area of the mat."
-      }
-      {
-        modal: false
+        content: "We must now make a move. This means moving a grey dice to another area, namely required, optional and forbidden..."
       }
       {
         modal: true
         header: "Forbidden, required and optional"
-        content: "As you can see, in a move, you can move a die to one of three areas of the board:
-

-
Forbidden: The forbidden section includes all dice that can not be used when forming a solution to the goal.

-
Optional: You can use any number of optional dice to form the goal.

-
Required: You must use ALL of these dice when forming the goal.

-        The idea is that when someone makes a 'challenge', you must use a combination of NONE of the forbidden dice, ANY of the optional dice and ALL of the required dice to form your solution to the goal.
-

+        content: "When you click on a dice to make a move, you can move it to one of three areas of the board:
+

Forbidden: The forbidden section includes all dice that can not be used when forming a solution to the goal.

+

Optional: You can use any number of optional dice to form the goal.

+

Required: You must use ALL of these dice when forming the goal.

+

The idea is that when someone makes a 'challenge', you must use a combination of NONE of the forbidden dice, ANY of the optional dice and ALL of the required dice to form your solution to the goal.

         "
       }
       {
+        tipselector: 'li.dice[data-index="1"]'
+        tipmessage: 'As an example, we want to make this dice required.
Click on it and move it to required.'
         modal: false
       }


Added a TODO about replacing UTILITIES when the --google option is used.

--- closure/test/person.coffee
+++ closure/test/person.coffee
@@ -1,6 +1,8 @@
 # TODO: Make it possible to specify type information.
 # TODO: Make sure that it is possible to specify quoted vs. unquoted
 #       properties so the code can be compiled in Advanced mode.
+# TODO: Make sure that everything in nodes.UTILITIES uses a Closure Library
+#       equivalent when --google is used.

 class example.people.Person extends example.people.AbstractPerson


karma debug config watches .temp

karma debug config watches .temp

--- karmadebug.conf.coffee
+++ karmadebug.conf.coffee
@@ -6,6 +6,7 @@

 module.exports = (config) ->
 	config.set
+		autoWatchBatchDelay: 1000
 		basePath: 'dist'
 		browsers: [
 			'Chrome'


Update endpoint unit spec to cover new error serialization logic

--- src/endpoint.spec.coffee
+++ src/endpoint.spec.coffee
@@ -31,7 +31,7 @@
       expect(ioStub.sockets.on.calledWith 'connection', sinon.match.func).to.be.ok

-  describe 'receiving a eventric:joinRoom socket event', ->
+  describe 'receiving an eventric:joinRoom socket event', ->
     socketStub = null

     beforeEach ->
@@ -97,8 +97,7 @@
             done()

-
-  describe 'receiving a eventric:leaveRoom socket event', ->
+  describe 'receiving an eventric:leaveRoom socket event', ->

     socketStub = null

@@ -117,7 +116,7 @@
       expect(socketStub.leave.calledWith 'RoomName').to.be.ok

-  describe 'receiving a eventric:rpcRequest event', ->
+  describe 'receiving an eventric:rpcRequest event', ->
     socketStub = null
     rpcRequestFake = null
     rpcHandlerStub = null
@@ -201,11 +200,10 @@
       describe 'which rejects', ->

         responseFake = null
-        errorFake = null

         beforeEach (done) ->
           responseFake = {}
-          errorFake = {}
+          errorFake = new Error 'The error message'
           rpcRequestMiddlewareStub.returns new Promise (resolve, reject) -> reject errorFake
           rpcHandlerStub.yields null, responseFake
           endpoint.initialize
@@ -220,8 +218,15 @@

         it 'should emit a eventric:rpcResponse event with an error', ->
-          expect(socketStub.emit.calledWith 'eventric:rpcResponse', rpcId: rpcRequestFake.rpcId, error: errorFake, data: null).to.be.ok
-
+          expect(
+            socketStub.emit.calledWith(
+              'eventric:rpcResponse', {
+                rpcId: rpcRequestFake.rpcId,
+                error: {message: 'The error message', name: 'Error'},
+                data: null
+              }
+            )
+          ).to.be.ok

   describe '#publish', ->


atom 0.190.0 keymap fix

atom 0.190.0 keymap fix

--- keymaps/show-todo.cson
+++ keymaps/show-todo.cson
@@ -7,5 +7,5 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'.atom-workspace':
-  'ctrl-shift-t': 'todo-show:find-in-project'
+'atom-workspace':
+  'cmd-shift-t': 'todo-show:find-in-project'


Add test coverage for a previously untested code path.

--- tests/dice_test.coffee
+++ tests/dice_test.coffee
@@ -97,6 +97,23 @@
     it "should just skip rerolling", ->
       expect(room.messages[1][1]).to.match /I rolled 1, 2, 3, and 4/
       expect(random_stub.callCount).to.eql 4
+
+  context 'and rerolls anything over 4', ->
+    beforeEach ->
+      random_stub = stub(Math, "random")
+      random_stub.onCall(0).returns(0.7) # 5
+      random_stub.onCall(1).returns(0.9) # 6
+      random_stub.onCall(2).returns(0)   # 1
+      random_stub.onCall(3).returns(0.3) # 2
+      random_stub.onCall(4).returns(0.4) # 3
+      random_stub.onCall(5).returns(0.5) # 4
+      room.user.say 'alice', '@hubot roll 4d6r>5'
+
+    afterEach ->
+      random_stub.restore()
+
+    it "should only have low rolls", ->
+      expect(room.messages[1][1]).to.match /I rolled [1-4], [1-4], [1-4], and [1-4]/

   context '2 dice and drops the lowest', ->
     beforeEach ->


Update Handler admin get query params

Update Handler admin get query params

Unclear how this was supposed to work before these changes.

--- server/commons/Handler.coffee
+++ server/commons/Handler.coffee
@@ -179,21 +179,18 @@
     # if it's not a text search but the user is an admin, let him try stuff anyway
     else if req.user?.isAdmin()
       # admins can send any sort of query down the wire
+      # Example URL: http://localhost:3000/db/user?filter[anonymous]=true
       filter = {}
-      filter[key] = (val for own key, val of req.query.filter when key not in specialParameters) if 'filter' of req.query
-
+      filter[key] = JSON.parse(val) for own key, val of req.query.filter when key not in specialParameters if 'filter' of req.query
       query = @modelClass.find(filter)

       # Conditions are chained query functions, for example: query.find().limit(20).sort('-dateCreated')
-      conditions = JSON.parse(req.query.conditions || '[]')
+      # Example URL: http://localhost:3000/db/user?conditions[limit]=20&conditions[sort]=-dateCreated
       hasLimit = false
       try
-        for condition in conditions
-          name = condition[0]
-          f = query[name]
-          args = condition[1..]
-          query = query[name](args...)
-          hasLimit ||= f is 'limit'
+        for own key, val of req.query.conditions
+          query = query[key](val)
+          hasLimit ||= key is 'limit'
       catch e
         return @sendError(res, 422, 'Badly formed conditions.')
       query.limit(2000) unless hasLimit


How did them spaces get there?

How did them spaces get there?

--- src/dispatcher.coffee
+++ src/dispatcher.coffee
@@ -98,7 +98,7 @@
 			stores[id]._handleAction.call stores[id], currentAction, currentPayload, @waitFor
 			isHandled[id] = yes

-        ###
+		###
         # Registers a store with the dispatcher so that it's notified when actions
         # are dispatched.
         #
@@ -109,7 +109,7 @@
 			store._id = storeID++;

-        ###
+		###
         # Unregisters a store from the dispatcher so that it's no longer
         # notified when actions are dispatched.
         #
@@ -120,7 +120,7 @@
 				"dispatcher.unregister(...): Store is not registered with the dispatcher."
 			delete stores[store._id]

-        ###
+		###
         # Method for waiting for other stores to complete their handling
         # of actions. This method is passed along to the Stores when an action
         # is dispatched.
@@ -154,7 +154,7 @@
 				# Make the dependency handle the action.
 				notifyStore.call @, id

-        ###
+		###
         # Method for dispatching in action. This method is used by the Action class
         # when calling Action.dispatch().
         #


Remove @previouslyActiveElement from file finder. Always focus the root view on detach.

--- src/extensions/file-finder.coffee
+++ src/extensions/file-finder.coffee
@@ -15,7 +15,6 @@

   paths: null
   maxResults: null
-  previouslyActiveElement: null

   initialize: (@rootView) ->
     requireStylesheet 'file-finder.css'
@@ -39,13 +38,12 @@
       @attach() if @rootView.project.getPath()?

   attach: ->
-    @previouslyActiveElement = document.activeElement
     @rootView.project.getFilePaths().done (@paths) => @populatePathList()
     @rootView.append(this)
     @miniEditor.focus()

   detach: ->
-    (@previouslyActiveElement or @rootView).focus()
+    @rootView.focus()
     super
     @miniEditor.setText('')


log shutdown messages as warnings

log shutdown messages as warnings

--- services/real-time/app.coffee
+++ services/real-time/app.coffee
@@ -111,17 +111,17 @@
 shutdownCleanly = (signal) ->
 	connectedClients = io.sockets.clients()?.length
 	if connectedClients == 0
-		logger.log("no clients connected, exiting")
+		logger.warn("no clients connected, exiting")
 		process.exit()
 	else
-		logger.log {connectedClients}, "clients still connected, not shutting down yet"
+		logger.warn {connectedClients}, "clients still connected, not shutting down yet"
 		setTimeout () ->
 			shutdownCleanly(signal)
-		, 10000
+		, 30 * 1000

 drainAndShutdown = (signal) ->
 	if Settings.shutDownInProgress
-		logger.log signal: signal, "shutdown already in progress, ignoring signal"
+		logger.warn signal: signal, "shutdown already in progress, ignoring signal"
 		return
 	else
 		Settings.shutDownInProgress = true


Add Atom snippets for it blocks and describe blocks in JS

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -66,3 +66,23 @@
   "Create Jasmine spy":
     prefix: "spy"
     body: 'jasmine.createSpy("${1:description}")$2'
+
+".source.js": {
+  "It block": {
+    prefix: "i"
+    body: """
+      it('$1', () => {
+        $2
+      })
+    """
+  }
+
+  "Describe block": {
+    prefix: "de"
+    body: """
+      describe('${1:description}', () => {
+        ${2:body}
+      })
+    """
+  }
+}


Make SQL snippets more functional

Make SQL snippets more functional

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -525,15 +525,19 @@

   'number':
     'prefix': 'number'
-    'body': 'NUMBER'
+    'body': 'NUMBER(${1:total}, ${2:decimal})$3'
+
+  'integer':
+    'prefix': 'integer'
+    'body': 'INTEGER'

   'char':
     'prefix': 'char'
-    'body': 'CHAR'
+    'body': 'CHAR(${1:maxlen})$2'

   'varchar2':
     'prefix': 'varchar2'
-    'body': 'VARCHAR2'
+    'body': 'VARCHAR2(${1:maxlen})$2'

   'date':
     'prefix': 'date'
@@ -561,27 +565,23 @@

   'primary key':
     'prefix': 'primarykey'
-    'body': 'PRIMARY KEY'
+    'body': 'PRIMARY KEY(${1:columns})$2'

   'foreign key':
     'prefix': 'foreignkey'
-    'body': 'FOREIGN KEY'
-
-  'references':
-    'prefix': 'references'
-    'body': 'REFERENCES ${1:tname}(${2:column})'
+    'body': 'FOREIGN KEY(${1:column}) REFERENCES ${2:tname}(${3:column})$4'

   'not null':
     'prefix': 'notnull'
-    'body': 'NOT NULL'
+    'body': 'NOT NULL(${1:column})$2'

   'unique':
     'prefix': 'unique'
-    'body': 'UNIQUE'
+    'body': 'UNIQUE(${1:column})$2'

   'check':
     'prefix': 'check'
-    'body': 'CHECK(${1:condition})'
+    'body': 'CHECK(${1:condition})$2'

   'select':
     'prefix': 'select'
@@ -599,6 +599,10 @@
     'prefix': 'or'
     'body': 'OR '

+  'between':
+    'prefix': 'between'
+    'body': 'BETWEEN '
+
   'like':
     'prefix': 'like'
     'body': 'LIKE '


Add JSON.stringify and JSON.parse snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -278,6 +278,14 @@
     'prefix': 'modexp'
     'body': 'module.exports = ${1:object};'

+  'JSON.stringify':
+    'prefix': 'jsonstringify'
+    'body': 'JSON.stringify($1)$2'
+
+  'JSON.parse':
+    'prefix': 'jsonparse'
+    'body': 'JSON.parse($1)$2'
+
   # ES6

   'class':


Correct stray space in mountToApp snippet

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -425,7 +425,7 @@

   'mountToApp':
     'prefix': 'mta'
-    'body': '{\n\tmountToApp: function ($1) {\n\n\t\t $2\n\n\t}\n}'
+    'body': '{\n\tmountToApp: function ($1) {\n\n\t\t$2\n\n\t}\n}'

   'wrap prototype method':
     'prefix': 'wrap'


Remove superfluous tab stop from {{else}} snippet

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -1228,7 +1228,7 @@

   'else':
     'prefix': 'else'
-    'body': '{{else}}$2'
+    'body': '{{else}}'

   'endif':
     'prefix': 'endif'


:lipstick:

:lipstick:

--- src/app/range.coffee
+++ src/app/range.coffee
@@ -1,6 +1,5 @@
 Point = require 'point'
 _ = require 'underscore'
-

 module.exports =
 class Range


Allow deletion of suppliers through GUI

Allow deletion of suppliers through GUI

--- src/handlers/DeleteHandler.coffee
+++ src/handlers/DeleteHandler.coffee
@@ -20,6 +20,7 @@
 		'others'
 		'ingredients'
 		'beerStyles'
+		'suppliers'
 	]

 	specialParams: [


Use forward slash on all platforms

Use forward slash on all platforms

Paths are normalized by git-utils to use / on all platforms

--- src/git.coffee
+++ src/git.coffee
@@ -1,4 +1,4 @@
-{join, sep} = require 'path'
+{join} = require 'path'

 _ = require 'underscore-plus'
 {Emitter, Subscriber} = require 'emissary'
@@ -246,7 +246,7 @@
   # Returns a {Number} representing the status. This value can be passed to
   # {::isStatusModified} or {::isStatusNew} to get more information.
   getDirectoryStatus: (directoryPath)  ->
-    directoryPath = "#{@relativize(directoryPath)}#{sep}"
+    directoryPath = "#{@relativize(directoryPath)}/"
     directoryStatus = 0
     for path, status of @statuses
       directoryStatus |= status if path.indexOf(directoryPath) is 0


:memo: Use TomDoc style comments in Git class

--- src/git.coffee
+++ src/git.coffee
@@ -27,17 +27,19 @@
   Emitter.includeInto(this)
   Subscriber.includeInto(this)

-  # Private: Creates a new `Git` instance.
-  #
-  # * path: The path to the git repository to open
-  # * options:
-  #    + refreshOnWindowFocus:
-  #      A Boolean that identifies if the windows should refresh
+  # Public: Creates a new Git instance.
+  #
+  # path - The path to the Git repository to open.
+  # options - An object with the following keys (default: {}):
+  #   :refreshOnWindowFocus - `true` to refresh the index and statuses when the
+  #                           window is focused.
+  #
+  # Returns a Git instance or null if the repository could not be opened.
   @open: (path, options) ->
     return null unless path
     try
       new Git(path, options)
-    catch e
+    catch
       null

   @exists: (path) ->
@@ -47,20 +49,6 @@
     else
       false

-  path: null
-  statuses: null
-  upstream: null
-  branch: null
-  statusTask: null
-
-  # Private: Creates a new `Git` object.
-  #
-  # * path: The {String} representing the path to your git working directory
-  # * options:
-  #    + refreshOnWindowFocus: If `true`, {#refreshIndex} and {#refreshStatus}
-  #      are called on focus
-  #    + project: A project that supplies buffers that will be monitored for
-  #      save and reload events to trigger status refreshes.
   constructor: (path, options={}) ->
     @repo = GitUtils.open(path)
     unless @repo?
@@ -80,7 +68,7 @@
     if @project?
       @subscribe @project.eachBuffer (buffer) => @subscribeToBuffer(buffer)

-  # Private: Subscribes to buffer events.
+  # Subscribes to buffer events.
   subscribeToBuffer: (buffer) ->
     @subscribe buffer, 'saved reloaded path-changed', =>
       if path = buffer.getPath()
@@ -100,29 +88,29 @@

     @unsubscribe()

-  # Private: Returns the corresponding {Repository}
+  # Returns the corresponding {Repository}
   getRepo: ->
     unless @repo?
       throw new Error("Repository has been destroyed")
     @repo

-  # Public: Reread the index to update any values that have changed since the
+  # Reread the index to update any values that have changed since the
   # last time the index was read.
   refreshIndex: -> @getRepo().refreshIndex()

-  # Public: Returns the path of the repository.
+  # Public: Returns the {String} path of the repository.
   getPath: ->
     @path ?= fs.absolute(@getRepo().getPath())

-  # Public: Returns the working directory of the repository.
+  # Public: Returns the {String} working directory path of the repository.
   getWorkingDirectory: -> @getRepo().getWorkingDirectory()

-  # Public: Returns the status of a single path in the repository.
-  #
-  # * path:
-  #   A String defining a relative path
-  #
-  # Returns a {Number}, FIXME representing what?
+  # Public: Get the status of a single path in the repository.
+  #
+  # path - A {String} repository-relative path.
+  #
+  # Returns a {Number} representing the status. This value can be passed to
+  # {.isStatusModified} or {.isStatusNew} to get more information.
   getPathStatus: (path) ->
     currentPathStatus = @statuses[path] ? 0
     pathStatus = @getRepo().getStatus(@relativize(path)) ? 0
@@ -134,7 +122,9 @@
       @emit 'status-changed', path, pathStatus
     pathStatus

-  # Public: Returns true if the given path is ignored.
+  # Public: Is the given path ignored?
+  #
+  # Returns a {Boolean}.
   isPathIgnored: (path) -> @getRepo().isIgnored(@relativize(path))

   # Public: Returns true if the given status indicates modification.
@@ -163,22 +153,22 @@
   # `refs/remotes`.  It also shortens the SHA-1 of a detached `HEAD` to 7
   # characters.
   #
-  # Returns a String.
+  # Returns a {String}.
   getShortHead: -> @getRepo().getShortHead()

   # Public: Restore the contents of a path in the working directory and index
   # to the version at `HEAD`.
   #
   # This is essentially the same as running:
+  #
   # ```
   # git reset HEAD -- <path>
   # git checkout HEAD -- <path>
   # ```
   #
-  # * path:
-  #   The String path to checkout
-  #
-  # Returns a Boolean that's true if the method was successful.
+  # path - The {String} path to checkout.
+  #
+  # Returns a {Boolean} that's true if the method was successful.
   checkoutHead: (path) ->
     headCheckedOut = @getRepo().checkoutHead(@relativize(path))
     @getPathStatus(path) if headCheckedOut
@@ -186,10 +176,9 @@

   # Public: Checks out a branch in your repository.
   #
-  # * reference:
-  #   The String reference to checkout
-  # * create:
-  #   A Boolean value which, if true creates the new reference if it doesn't exist.
+  # reference - The String reference to checkout
+  # create -  A Boolean value which, if true creates the new reference if it
+  #           doesn't exist.
   #
   # Returns a Boolean that's true if the method was successful.
   checkoutReference: (reference, create) ->
@@ -200,27 +189,26 @@
   # This compares the working directory contents of the path to the `HEAD`
   # version.
   #
-  # * path:
-  #   The String path to check
-  #
-  # Returns an object with two keys, `added` and `deleted`. These will always
-  # be greater than 0.
+  # path - The {String} path to check.
+  #
+  # Returns an {Object} with the following keys:
+  #   :added - The {Number} of added lines.
+  #   :deleted - The {Number} of deleted lines.
   getDiffStats: (path) -> @getRepo().getDiffStats(@relativize(path))

-  # Public: Identifies if a path is a submodule.
-  #
-  # * path:
-  #   The String path to check
-  #
-  # Returns a Boolean.
+  # Public: Is the given path a submodule in the repository?
+  #
+  # path - The {String} path to check.
+  #
+  # Returns a {Boolean}.
   isSubmodule: (path) -> @getRepo().isSubmodule(@relativize(path))

-  # Public: Retrieves the status of a directory.
-  #
-  # * path:
-  #   The String path to check
-  #
-  # Returns a Number representing the status.
+  # Public: Get the status of a directory in the repository's working directory.
+  #
+  # path - The {String} path to check.
+  #
+  # Returns a {Number} representing the status. This value can be passed to
+  # {.isStatusModified} or {.isStatusNew} to get more information.
   getDirectoryStatus: (directoryPath)  ->
     {sep} = require 'path'
     directoryPath = "#{directoryPath}#{sep}"
@@ -232,16 +220,14 @@
   # Public: Retrieves the line diffs comparing the `HEAD` version of the given
   # path and the given text.
   #
-  # This is similar to the commit numbers reported by `git status` when a
-  # remote tracking branch exists.
-  #
-  # * path:
-  #   The String path (relative to the repository)
-  # * text:
-  #   The String to compare against the `HEAD` contents
-  #
-  # Returns an object with two keys, `ahead` and `behind`. These will always be
-  # greater than zero.
+  # path - The {String} path relative to the repository.
+  # text - The {String} to compare against the `HEAD` contents
+  #
+  # Returns an {Array} of hunk {Object}s with the following keys:
+  #   :oldStart - The line {Number} of the old hunk.
+  #   :newStart - The line {Number} of the new hunk.
+  #   :oldLines - The {Number} of lines in the old hunk.
+  #   :newLines - The {Number} of lines in the new hunk
   getLineDiffs: (path, text) ->
     # Ignore eol of line differences on windows so that files checked in as
     # LF don't report every line modified when the text contains CRLF endings.
@@ -257,7 +243,7 @@
   # Public: Returns the upstream branch for the current HEAD, or null if there
   # is no upstream branch for the current HEAD.
   #
-  # Returns a String branch name such as `refs/remotes/origin/master`
+  # Returns a {String} branch name such as `refs/remotes/origin/master`.
   getUpstreamBranch: -> @getRepo().getUpstreamBranch()

   # Public: Returns the current SHA for the given reference.
@@ -265,19 +251,21 @@

   # Public: Gets all the local and remote references.
   #
-  # Returns an object with three keys: `heads`, `remotes`, and `tags`. Each key
-  # can be an array of strings containing the reference names.
+  # Returns an {Object} with the following keys:
+  #  :heads - An {Array} of head reference names.
+  #  :remotes - An {Array} of remote reference names.
+  #  :tags - An {Array} of tag reference names.
   getReferences: -> @getRepo().getReferences()

   # Public: Returns the number of commits behind the current branch is from the
-  # default remote branch.
+  # its upstream remote branch.
   getAheadBehindCount: (reference) -> @getRepo().getAheadBehindCount(reference)

   # Public: Returns true if the given branch exists.
   hasBranch: (branch) -> @getReferenceTarget("refs/heads/#{branch}")?

-  # Private: Refreshes the current git status in an outside process and
-  # asynchronously updates the relevant properties.
+  # Refreshes the current git status in an outside process and asynchronously
+  # updates the relevant properties.
   refreshStatus: ->
     @statusTask = Task.once require.resolve('./repository-status-handler'), @getPath(), ({statuses, upstream, branch}) =>
       statusesUnchanged = _.isEqual(statuses, @statuses) and _.isEqual(upstream, @upstream) and _.isEqual(branch, @branch)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_over.coffee
+++ src/game_over.coffee
@@ -7,6 +7,8 @@
 ###

 ###*
+Trans
+
 @name update
 @methodOf GameOver#
 @event


doc: add install command usage

doc: add install command usage

--- src/installer.coffee
+++ src/installer.coffee
@@ -4,6 +4,7 @@
 async = require 'async'
 _ = require 'underscore'
 mkdir = require('mkdirp').sync
+optimist = require 'optimist'
 temp = require 'temp'
 cp = require('wrench').copyDirSyncRecursive
 rm = require('rimraf').sync
@@ -22,6 +23,22 @@
     @atomNodeDirectory = path.join(@atomDirectory, '.node-gyp')
     @atomNpmPath = require.resolve('npm/bin/npm-cli')
     @atomNodeGypPath = require.resolve('node-gyp/bin/node-gyp')
+
+  parseOptions: (argv) ->
+    options = optimist(argv)
+    options.usage """
+
+      Usage: apm install [<package_name>]
+
+      Install the given Atom package to ~/.atom/packages/<package_name>.
+
+      If no package name is given then all the dependencies in the package.json
+      file are installed into the node_modules folder for the current working
+      directory.
+    """
+    options.alias('h', 'help').describe('help', 'Print this usage message')
+
+  showHelp: (argv) -> @parseOptions(argv).showHelp()

   installNode: (callback) =>
     process.stdout.write "Installing node@#{config.getNodeVersion()} "


Deploy: remove unnecessary config lines

Deploy: remove unnecessary config lines

--- config/main.feature.coffee
+++ config/main.feature.coffee
@@ -260,8 +260,6 @@
         proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_next_upstream   error timeout invalid_header http_500;
         proxy_connect_timeout 1;
-        add_header            X-Upstream       $upstream_addr;
-        add_header            X-Backend-Server $HOSTNAME;
       }

       location /xhr {