Use setTimeout for sleeping. Reordered tests to remove conflicts between capturing warnings.

--- tests.coffee
+++ tests.coffee
@@ -93,15 +93,6 @@
       other: @Reference 'self', ['content'], false

 Document.redefineAll()
-
-# sleep function from fibers docs
-sleep = (ms) ->
-  Fiber = Npm.require 'fibers'
-  fiber = Fiber.current
-  setTimeout ->
-    fiber.run()
-  , ms
-  Fiber.yield()

 testAsyncMulti 'meteor-peerdb - references', [
   (test, expect) ->
@@ -250,9 +241,7 @@
     # (triggered by person inserts, but pending) run afterwards, then they can patch things which
     # should in fact be done by source observers (on post), like setting usernames in post's
     # references to persons
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @person1 = Persons.findOne @person1Id
@@ -299,9 +288,7 @@
         @postId = postId

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -361,9 +348,7 @@
     # Sleep so that observers have time to update documents
     # so that persons updates are not merged togetger to better
     # test the code for multiple updates
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     Persons.update @person3Id,
@@ -390,9 +375,7 @@
     test.equal @person3.displayName, 'Person 3'

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -436,9 +419,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -473,9 +454,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -501,9 +480,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -525,38 +502,6 @@
   # Invalid document should not be added to the list
   test.equal Document.Meta.list, [UserLink, CircularSecond, Person, CircularFirst, Recursive, Post]

-testAsyncMulti 'meteor-peerdb - delayed defintion', [
-  (test, expect) ->
-    class BadPost extends Document
-      @Meta =>
-        collection: Posts
-        fields:
-          author: @Reference undefined, ['username']
-
-    Log._intercept 2 # Two to see if we catch more than expected
-
-    # Sleep so that error is shown
-    pollUntil expect, ->
-      false
-    , 1100, 100, true
-,
-  (test, expect) ->
-    intercepted = Log._intercepted()
-
-    test.equal intercepted.length, 1, intercepted
-
-    intercepted = EJSON.parse intercepted[0]
-
-    test.equal intercepted.message, "Not all delayed document definitions were successfully retried: BadPost"
-    test.equal intercepted.level, 'error'
-
-    test.equal Document.Meta.list, [UserLink, CircularSecond, Person, CircularFirst, Recursive, Post]
-    test.equal Document.Meta.delayed.length, 1
-
-    # Clear delayed so that we can retry tests without errors
-    Document.Meta.delayed = []
-]
-
 testAsyncMulti 'meteor-peerdb - circular changes', [
   (test, expect) ->
     Log._intercept 3 if Meteor.isServer # Three to see if we catch more than expected
@@ -580,9 +525,7 @@
         @circularSecondId = circularSecondId

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     if Meteor.isServer
@@ -623,9 +566,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -654,9 +595,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -686,9 +625,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -718,9 +655,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -746,9 +681,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -784,9 +717,7 @@
         @circularFirstId = circularFirstId

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     if Meteor.isServer
@@ -820,9 +751,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -848,9 +777,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update document
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -888,9 +815,7 @@
         @recursive2Id = recursive2Id

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -917,9 +842,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -948,9 +871,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -980,9 +901,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1012,9 +931,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1040,9 +957,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1070,9 +985,7 @@
         @recursiveId = recursiveId

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1093,9 +1006,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1117,9 +1028,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1137,9 +1046,7 @@
         test.isFalse error, error

     # Sleep so that observers have time to update documents
-    pollUntil expect, ->
-      false
-    , 500, 100, true
+    Meteor.setTimeout expect(), 500
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1214,3 +1121,33 @@

     # There should be no warning because user is optional
     test.equal intercepted.length, 0, intercepted
+
+testAsyncMulti 'meteor-peerdb - delayed defintion', [
+  (test, expect) ->
+    class BadPost extends Document
+      @Meta =>
+        collection: Posts
+        fields:
+          author: @Reference undefined, ['username']
+
+    Log._intercept 2 # Two to see if we catch more than expected
+
+    # Sleep so that error is shown
+    Meteor.setTimeout expect(), 1000
+,
+  (test, expect) ->
+    intercepted = Log._intercepted()
+
+    test.equal intercepted.length, 1, intercepted
+
+    intercepted = EJSON.parse intercepted[0]
+
+    test.equal intercepted.message, "Not all delayed document definitions were successfully retried: BadPost"
+    test.equal intercepted.level, 'error'
+
+    test.equal Document.Meta.list, [UserLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+    test.equal Document.Meta.delayed.length, 1
+
+    # Clear delayed so that we can retry tests without errors
+    Document.Meta.delayed = []
+]


Create or connect link

Create or connect link

--- apps/import/components/import_bookmarks/import_status_view.coffee
+++ apps/import/components/import_bookmarks/import_status_view.coffee
@@ -16,7 +16,7 @@

 module.exports = class ImportStatusView extends Backbone.View
   events:
-    'click .import__status__inner__connect' : 'initConnectBlocks'
+    'click .import__status__inner__connect' : 'connectLinks'

   initialize: ->
     @selectedChannel = new Channel()
@@ -31,7 +31,7 @@
     @listenTo @collection, 'model:selected', @render
     @listenTo @status, 'change', @render

-  initConnectBlocks: ->
+  connectLinks: ->
     @status.set connecting: true
     blocks = @collection.filter('selected')
     @maybeCreateChannel()
@@ -57,9 +57,7 @@
     selectedChannel = @selectedChannel
     block = new Block source: link.get('href')
     existingConnections = link.get('connections') or []
-    blocks.create block.toJSON(),
-      url: "#{sd.API_URL}/channels/#{selectedChannel.get('slug')}/blocks"
-      success: (block) ->
+    @createOrConnectBlock.then (block) ->
         connections = existingConnections.concat [
           {
             title: selectedChannel.get('title')
@@ -68,11 +66,31 @@
             user_slug: selectedChannel.get('user').slug
           }
         ]
-        link.set
+        link.set
+          block_id: block.id
           selected: false
           connections: connections
         link.collection.trigger 'model:selected'
         dfd.resolve()
+    dfd.promise
+
+  createOrConnectBlock: (link, selectedChannel)->
+    dfd = Q.defer()
+    if link.has('connections')
+      $.ajax
+        type: "POST"
+        url: "#{sd.API_URL}/channels/#{id}/connections"
+        data:
+          connectable_type: 'Block'
+          connectable_id: link.get('block_id')
+          channel_id: selectedChannel.id
+        success: dfd.resolve
+        error: dfd.error
+    else
+      blocks.create block.toJSON(),
+        url: "#{sd.API_URL}/channels/#{selectedChannel.get('slug')}/blocks"
+        success: dfd.resolve
+        error: dfd.reject
     dfd.promise

   showSuccess: =>


moderation: unnecessary console logs

moderation: unnecessary console logs

--- client/admin/lib/views/moderation/topiccommonview.coffee
+++ client/admin/lib/views/moderation/topiccommonview.coffee
@@ -59,7 +59,7 @@

     @addSubView @listController.getView()

-    #@listController.on 'LazyLoadThresholdReached', @bound 'searchChannels'
+
     @listController.on 'LazyLoadThresholdReached', =>

       if @skip is 0 and @searchSkip is 0
@@ -80,7 +80,7 @@
       skip                  : @skip
       showModerationNeeded  : true
       type                  : @getOptions().typeConstant or= "topic"
-    console.log 'fetchChannels2 skip'+ @skip
+
     kd.singletons.socialapi.channel.list options, @bound 'listFetchResults'

@@ -103,7 +103,7 @@
     # if we have items from listing. remove them all
     if @searchSkip is 0
       @listController.removeAllItems()
-    console.log 'listSearchResults searchSkip'+ @searchSkip
+
     @skip = 0  # revert skip of normal listing

     @searchSkip += channels?.length


Added text clarifying that you're not posting to Facebook/Twitter, but signing in with it

--- core/app/backbone/views/popovers/react_signin_popover.coffee
+++ core/app/backbone/views/popovers/react_signin_popover.coffee
@@ -29,6 +29,7 @@
       if window.localStorageIsEnabled
         ReactPopover className: 'white-popover', attachment: 'right',
           _span ["signin-popover"],
+            'Sign in with: ',
             _a ["button-twitter small-connect-button js-accounts-popup-link",
               href: "/auth/twitter"
               onMouseDown: @_onButtonClicked


Maybe fix touch?

Maybe fix touch?

--- src/controller.coffee
+++ src/controller.coffee
@@ -3106,8 +3106,8 @@
   hook 'populate', 0, ->
     @touchScrollAnchor = new draw.Point 0, 0
     @lassoSelectStartTimeout = null
-
-    @iceElement.addEventListener 'touchstart', (event) =>
+
+    @wrapperElement.addEventListener 'touchstart', (event) =>
       clearTimeout @lassoSelectStartTimeout

       trackPoint = @touchEventToPoint event, 0
@@ -3133,7 +3133,7 @@
       else
         @queueLassoMousedown trackPoint, event

-    @iceElement.addEventListener 'touchmove', (event) =>
+    @wrapperElement.addEventListener 'touchmove', (event) =>
       clearTimeout @lassoSelectStartTimeout

       trackPoint = @touchEventToPoint event, 0
@@ -3154,7 +3154,7 @@
       if @clickedBlock? or @draggingBlock? or @lassoSelectAnchor? or @textInputSelecting
         event.preventDefault()

-    @iceElement.addEventListener 'touchend', (event) =>
+    @wrapperElement.addEventListener 'touchend', (event) =>
       clearTimeout @lassoSelectStartTimeout

       trackPoint = @touchEventToPoint event, 0


Customized TinyMCE toolbar [#98305670]

Customized TinyMCE toolbar [#98305670]

--- app/assets/javascripts/components/itsi_authoring/rich_text_editor.js.coffee
+++ app/assets/javascripts/components/itsi_authoring/rich_text_editor.js.coffee
@@ -14,7 +14,12 @@
     componentDidMount: ->
       @node = $(React.findDOMNode(@refs.textarea))
       @node.tinymce
-        plugins: 'code paste textcolor'
+        menubar: false
+        statusbar: false
+        toolbar_items_size: 'small'
+        toolbar: "undo redo | bold italic strikethrough underline | aligncenter alignjustify alignleft alignright indent outdent | subscript superscript | numlist bullist | link unlink | hr image table pastetext | code"
+        plugins: 'paste link hr image table code'
+        paste_as_text: true
         setup: (editor) =>
           # both events are needed as the 'change' event is only sent when the input loses focus (which handles menu choices like formatting)
           editor.on 'keyup', (e) =>


add response

add response

--- scripts/befool.blog.coffee
+++ scripts/befool.blog.coffee
@@ -18,6 +18,7 @@
     robot.router.post '/bii/befool/publish', (req, res) ->
         robot.logger.info "Befool blog updated"
         robot.emit "befool_homepage_published", {}
+        res.send 'OK, I would check it out. ^ ^'

     # on published
     robot.on "befool_homepage_published", (datas) ->


fix feed generator

fix feed generator

--- scripts/chat-logger.coffee
+++ scripts/chat-logger.coffee
@@ -169,31 +169,38 @@

   list_feed_item = (room) ->
     src = chat_data()[room]
-    years = _.keys(src).sort().reverse()
-
     result = []

     try
-      for year in years
-        year.reduceRight (prev, month) ->
+      for year in _.keys(src).sort().reverse()
+        src[year].reduceRight (prev, month) ->
+          return unless month
           month.reduceRight (prev, day) ->
-            day.reduceRight (result, msg) ->
+            return unless day
+            day.reduceRight (prev, msg) ->
               if result.length == 0 or _.first(_.first result).date - msg.date >= FEED_DIVIDE_THRESHOLD
                 result.unshift [msg]
               else
                 _.last(result).unshift msg

               throw "break" if result.length > ITEM_COUNT
-            , result
+            , null
+          , null
+        , null
+    catch e
+      throw e if e != "break"

     result.slice(0, ITEM_COUNT).map (v) ->
       f = _.first(v)
       d = new Date f.date

       title: d.toUTCString()
+      author:
+        name: f.nick
       link: "#{base_url}/#{room}/#{d.getUTCFullYear()}/#{d.getUTCMonth() + 1}/#{d.getUTCDate()}\##{generate_time_string d}"
       description:
-        v.map (v) -> render_item v
+        v.map (v) ->
+          render_item v
         .reduce (ret, v) ->
           ret + v
         , ''
@@ -203,8 +210,11 @@
     feed = new Feed
       title: 'hubot log'
       description: "log of room: #{req.params.room}"
+      author:
+        name: robot.name
+        link: base_url
       link: "#{base_url}/#{req.params.room}/feed"
-    if chat_data()[req.params.room]
+    if chat_data()[req.params.room]?
       list_feed_item(req.params.room).forEach (v) -> feed.item v
     res.type 'application/atom+xml'
     res.send feed.render 'atom-1.0'


DeviceCommunication: Fix invalid end of transmission message

--- lib/devicecommunication.coffee
+++ lib/devicecommunication.coffee
@@ -73,7 +73,7 @@
         @sendCommands buffer, cb
     close: (cb) ->
         buffer = commandstream.Buffer commandstream.cmdFormat.commandSize
-        commandstream.writeString(buffer, 0, commandstream.cmdFormat.commands.End.id);
+        commandstream.writeCmd buffer, 0, commandstream.cmdFormat.commands.End.id
         @sendCommands buffer, cb

     # High-level API


Send lat/long forperson

Send lat/long forperson

--- src/main/assets/javascripts/simple.coffee
+++ src/main/assets/javascripts/simple.coffee
@@ -109,6 +109,14 @@

 if navigator && navigator.geolocation
   navigator.geolocation.getCurrentPosition(showMapAtGeoPosition, showMapAtDefault)
+
+  sendLocationDataToServer = (position) ->
+    sentDataSuccessCallback = (response) ->
+      console.log "Sent data - hoorah!"
+    $.get '/fgis/api/resource/6/location?x=' + position.coords.longitude + '&y=' + position.coords.latitude, {}, sentDataSuccessCallback, 'json'
+  sendLocationDataToServerCallback = ->
+    navigator.geolocation.getCurrentPosition(sendLocationDataToServer)
+  setInterval sendLocationDataToServerCallback, 1000
 else
   showMapAtDefault


bugfix da pagina detalhes

bugfix da pagina detalhes

--- src/main/coffee/DetalhesPublicacao.coffee
+++ src/main/coffee/DetalhesPublicacao.coffee
@@ -1,12 +1,10 @@

 class DetalhesPublicacao
   constructor : () ->
-    urlRegex = /(https?:\/\/[^\s<]+)/g;
-    html = $('.detallhes-publicacao-container').html().replace urlRegex, (url) ->
-      "#{url}"
+    if $('.detallhes-publicacao-container').length > 0
+      urlRegex = /(https?:\/\/[^\s<]+)/g;
+      html = $('.detallhes-publicacao-container').html()?.replace urlRegex, (url) ->
+        "#{url}"
+      $('.detallhes-publicacao-container').html(html)

-    $('.detallhes-publicacao-container').html(html)
-
-new DetalhesPublicacao()
-
-
+new DetalhesPublicacao()


Better exit handeling.

Better exit handeling.

--- server.coffee
+++ server.coffee
@@ -274,7 +274,8 @@
       socket.emit 'output', c "You are not logged in.", 'red'
       fn {error: true}

-process.on 'SIGINT', ->
+process.on 'SIGINT', -> process.exit()
+process.on 'SIGTERM', -> process.exit()
+process.on 'exit', ->
   util.puts ""
-  db.saveSync('db.json')
-  process.exit()+  db.saveSync('db.json')


show more messages in chat

show more messages in chat

--- server.coffee
+++ server.coffee
@@ -204,7 +204,7 @@
       res.send {message: 'OK'}, 200

 app.get '/messages/:channel', (req, res) ->
-  db.collection('messages').find({channel: req.params.channel}).sort(date: -1).limit(100).toArray (err, data) ->
+  db.collection('messages').find({channel: req.params.channel}).sort(date: -1).limit(150).toArray (err, data) ->
     throw err if err
     res.json(200, data.reverse())


better constructor and conditional broadcasters

--- source/lib/element.coffee
+++ source/lib/element.coffee
@@ -1,22 +1,21 @@
-Style = require './style'
 Event = require './event'

 module.exports = class Element extends require './evented'
   constructor: (args = {}) ->
     super
-    @_style = args.style or @_style
-    @_state = args.state or @_state
-    @addListener 'update', @updateListener, yes
-    @addListener 'render', @renderListener, yes
+    this[key] = args[key] if args[key] for key in ['origin', 'size', 'state', 'style']
+    (@styles ||= {}) and @styles[key] = value for key, value of args.styles
+    (@states ||= {}) and @states[key] = value for key, value of args.states

-  updateListener: ->
+    for name in ['mousemove', 'mousedown', 'mouseup', 'update', 'render']
+      do (name) => @addListener name, ((e) -> e.stop() unless @events?[name]), yes

-  renderListener: ->
-
-  update: ->
-    @broadcastEvent new Event type:'update', target:this
+  update: (args) ->
+    if @events?.update then @broadcastEvent new Event
+      type:'update', target:this, args:args
     return this

   render: (context, args) ->
-    @broadcastEvent new Event type:'render', target:this
+    if @events?.render then @broadcastEvent new Event
+      type:'render', target:this, context:context, args:args
     return this


remove #general channel name from the tests

--- test/api.spec.coffee
+++ test/api.spec.coffee
@@ -19,7 +19,6 @@
   it 'slack.send', ->
     slack.send('Hello!')
     expect(slack.request).toHaveBeenCalledWith
-      channel: '#general'
       username: 'Robot'
       text: 'Hello!'
       icon_emoji: ':bell:'
@@ -104,7 +103,6 @@
       icon_url: 'http://something.com/icon.png'

     expect(slack.request).toHaveBeenCalledWith
-      channel: '#general'
       username: 'Robot'
       text: 'Hello!'
       icon_url: 'http://something.com/icon.png'
@@ -128,7 +126,6 @@
     , undefined

     expect(slack.request).toHaveBeenCalledWith
-      channel: '#general'
       username: 'Robot'
       text: 'Hello!'
       icon_emoji: ':bell:'


fix to accounts for text and html token email

--- server/api/accounts.coffee
+++ server/api/accounts.coffee
@@ -98,6 +98,8 @@
       snd.vars = useremail: tok.email, loginurl: tok.url, logincode: tok.token
     else
       snd.subject = settings.subject
+      snd.text ?= settings.text
+      snd.html ?= settings.html
       re = new RegExp '\{\{LOGINCODE\}\}', 'g'
       ure = new RegExp '\{\{LOGINURL\}\}', 'g'
       tre = new RegExp '\{\{TIMEOUT\}\}', 'g'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/distance.coffee
+++ test/distance.coffee
@@ -22,6 +22,6 @@
     width: 7
     height: 20

-  equals, player.distance(enemy), Point.distance()
+  equals, player.distance(enemy), Point.distance(player.position(), enemeu)

 module()


update lastAutoImport in db

update lastAutoImport in db

--- server/lib/importer.coffee
+++ server/lib/importer.coffee
@@ -11,6 +11,12 @@
         model = require "../konnectors/#{konnector.slug}"
         konnector.import konnector, model.fields, (err) ->
             if err
-                log.info err
+                log.error err
     else
         log.debug "Connector #{konnector.slug} already importing"
+    console.log konnector
+    data =
+        lastAutoImport: new Date()
+    konnector.updateAttributes data, (err) ->
+        if err
+            log.error err


restructuralization

restructuralization

--- test/fixtures.coffee
+++ test/fixtures.coffee
@@ -568,133 +568,132 @@
 '''

 sampleJsonSchemaNonStrict2 = '''
-                            {
-                            "$schema":"http://json-schema.org/draft-03/schema",
-                            "id":"#",
-                            "required":true,
-                            "additionalProperties":true,
-                            "type":"object",
-                            "properties":{
-                            "simple_key_value_pairXXXXX":{
-                            "id":"simple_key_value_pair",
-                            "required":true
-                            },
-                            "complex_key_value_pair":{
-                            "id":"complex_key_value_pair",
-                            "required":true,
-                            "additionalProperties":true,
-                            "type":"object",
-                            "properties":{
-                            "complex_key_value_pair_key1":{
-                            "id":"complex_key_value_pair_key1",
-                            "required":true
-                            },
-                            "complex_key_value_pair_key2":{
-                            "id":"complex_key_value_pair_key2",
-                            "required":true
-                            },
-                            "complex_key_value_pair_key3":{
-                            "id":"complex_key_value_pair_key3",
-                            "required":true,
-                            "additionalProperties":true,
-                            "type":"object",
-                            "properties":{
-                            "complex_key_value_pair_key1_in_nested_hash":{
-                            "id":"complex_key_value_pair_key1_in_nested_hash",
-                            "required":true
-                            },
-                            "complex_key_value_pair_key2_in_nested_hash":{
-                            "id":"complex_key_value_pair_key2_in_nested_hash",
-                            "required":true
-                            }
-                            }
-                            }
-                            }
-                            },
-                            "array_of_hashes":{
-                            "id":"array_of_hashes",
-                            "required":true,
-                            "additionalItems":true,
-                            "type":"array",
-                            "items":[
-                            {
-                            "id":0,
-                            "required":true,
-                            "additionalProperties":true,
-                            "type":"object",
-                            "properties":{
-                            "array_of_hashes_item1_key1":{
-                            "id":"array_of_hashes_item1_key1",
-                            "required":true
-                            },
-                            "array_of_hashes_item1_key2":{
-                            "id":"array_of_hashes_item1_key2",
-                            "required":true
-                            }
-                            }
-                            },
-                            {
-                            "id":1,
-                            "required":true,
-                            "additionalProperties":true,
-                            "type":"object",
-                            "properties":{
-                            "array_of_hashes_item2_key1":{
-                            "id":"array_of_hashes_item2_key1",
-                            "required":true
-                            },
-                            "array_of_hashes_item2_key2":{
-                            "id":"array_of_hashes_item2_key2",
-                            "required":true
-                            }
-                            }
-                            }
-                            ]
-                            },
-                            "array_of_mixed_simple_types":{
-                            "id":"array_of_mixed_simple_types",
-                            "required":true,
-                            "additionalItems":true,
-                            "type":"array",
-                            "items":[
-                            {
-                            "id":0,
-                            "required":true
-                            },
-                            {
-                            "id":1,
-                            "required":true
-                            },
-                            {
-                            "id":2,
-                            "required":true
-                            },
-                            {
-                            "id":3,
-                            "required":true
-                            }
-                            ]
-                            },
-                            "array_of_same_simple_types":{
-                            "id":"array_of_same_simple_types",
-                            "required":true,
-                            "additionalItems":true,
-                            "type":"array",
-                            "items":[
-                            {
-                            "id":0,
-                            "required":true
-                            },
-                            {
-                            "id":1,
-                            "required":true
-                            }
-                            ]
-                            }
-                            }
-                            }
-                            '''
-
+{
+  "$schema":"http://json-schema.org/draft-03/schema",
+  "id":"#",
+  "required":true,
+  "additionalProperties":true,
+  "type":"object",
+  "properties":{
+    "simple_key_value_pairXXXXX":{
+      "id":"simple_key_value_pair",
+      "required":true
+    },
+    "complex_key_value_pair":{
+      "id":"complex_key_value_pair",
+      "required":true,
+      "additionalProperties":true,
+      "type":"object",
+      "properties":{
+        "complex_key_value_pair_key1":{
+          "id":"complex_key_value_pair_key1",
+          "required":true
+        },
+        "complex_key_value_pair_key2":{
+          "id":"complex_key_value_pair_key2",
+          "required":true
+        },
+        "complex_key_value_pair_key3":{
+          "id":"complex_key_value_pair_key3",
+          "required":true,
+          "additionalProperties":true,
+          "type":"object",
+          "properties":{
+            "complex_key_value_pair_key1_in_nested_hash":{
+              "id":"complex_key_value_pair_key1_in_nested_hash",
+              "required":true
+            },
+            "complex_key_value_pair_key2_in_nested_hash":{
+              "id":"complex_key_value_pair_key2_in_nested_hash",
+              "required":true
+            }
+          }
+        }
+      }
+    },
+    "array_of_hashes":{
+      "id":"array_of_hashes",
+      "required":true,
+      "additionalItems":true,
+      "type":"array",
+      "items":[
+      {
+        "id":0,
+        "required":true,
+        "additionalProperties":true,
+        "type":"object",
+        "properties":{
+          "array_of_hashes_item1_key1":{
+            "id":"array_of_hashes_item1_key1",
+            "required":true
+          },
+          "array_of_hashes_item1_key2":{
+            "id":"array_of_hashes_item1_key2",
+            "required":true
+          }
+        }
+      },
+      {
+        "id":1,
+        "required":true,
+        "additionalProperties":true,
+        "type":"object",
+        "properties":{
+          "array_of_hashes_item2_key1":{
+            "id":"array_of_hashes_item2_key1",
+            "required":true
+          },
+          "array_of_hashes_item2_key2":{
+            "id":"array_of_hashes_item2_key2",
+            "required":true
+          }
+        }
+      }
+      ]
+    },
+    "array_of_mixed_simple_types":{
+      "id":"array_of_mixed_simple_types",
+      "required":true,
+      "additionalItems":true,
+      "type":"array",
+      "items":[
+      {
+        "id":0,
+        "required":true
+      },
+      {
+        "id":1,
+        "required":true
+      },
+      {
+        "id":2,
+        "required":true
+      },
+      {
+        "id":3,
+        "required":true
+      }
+      ]
+    },
+    "array_of_same_simple_types":{
+      "id":"array_of_same_simple_types",
+      "required":true,
+      "additionalItems":true,
+      "type":"array",
+      "items":[
+      {
+        "id":0,
+        "required":true
+      },
+      {
+        "id":1,
+        "required":true
+      }
+      ]
+    }
+  }
+}
+'''

 sampleJsonSchemaNonStrictCustom = '''
 {
@@ -1052,6 +1051,171 @@
   }
 }

+sampleHttpMessageSchema =
+{
+  "type":"object",
+  "$schema": "http://json-schema.org/draft-03/schema",
+  "id": "http://jsonschema.net",
+  "required":true,
+  "properties":{
+    "httpRequest": {
+      "type":"object",
+      "id": "httpRequest",
+      "required":true,
+      "properties":{
+        "body": {
+          "type":"string",
+          "id": "httpRequest/body",
+          "required":true
+        },
+        "expected": {
+          "type":"object",
+          "id": "httpRequest/expected",
+          "required":true,
+          "properties":{
+            "bodySchema": {
+              "type":"object",
+              "id": "httpRequest/expected/bodySchema",
+              "required":true,
+              "properties":{
+                "type": {
+                  "type":"string",
+                  "id": "httpRequest/expected/bodySchema/type",
+                  "required":true
+                }
+              }
+            },
+            "body": {
+              "type":"string",
+              "id": "httpRequest/expected/body",
+              "required":true
+            },
+            "headersSchema": {
+              "type":"object",
+              "id": "httpRequest/expected/headersSchema",
+              "required":true,
+              "properties":{
+                "type": {
+                  "type":"string",
+                  "id": "httpRequest/expected/headersSchema/type",
+                  "required":true
+                }
+              }
+            },
+            "headers": {
+              "type":"string",
+              "id": "httpRequest/expected/headers",
+              "required":true
+            },
+            "method": {
+              "type":"string",
+              "id": "httpRequest/expected/method",
+              "required":true
+            },
+            "url": {
+              "type":"string",
+              "id": "httpRequest/expected/url",
+              "required":true
+            }
+          }
+        },
+        "headers": {
+          "type":"string",
+          "id": "httpRequest/headers",
+          "required":true
+        },
+        "method": {
+          "type":"string",
+          "id": "httpRequest/method",
+          "required":true
+        },
+        "url": {
+          "type":"string",
+          "id": "httpRequest/url",
+          "required":true
+        }
+      }
+    },
+    "httpResponse": {
+      "type":"object",
+      "id": "httpResponse",
+      "required":true,
+      "properties":{
+        "body": {
+          "type":"string",
+          "id": "httpResponse/body",
+          "required":true
+        },
+        "expected": {
+          "type":"object",
+          "id": "httpResponse/expected",
+          "required":true,
+          "properties":{
+            "bodySchema": {
+              "type":"object",
+              "id": "httpResponse/expected/bodySchema",
+              "required":true,
+              "properties":{
+                "type": {
+                  "type":"string",
+                  "id": "httpResponse/expected/bodySchema/type",
+                  "required":true
+                }
+              }
+            },
+            "body": {
+              "type":"string",
+              "id": "httpResponse/expected/body",
+              "required":true
+            },
+            "headersSchema": {
+              "type":"object",
+              "id": "httpResponse/expected/headersSchema",
+              "required":true,
+              "properties":{
+                "type": {
+                  "type":"string",
+                  "id": "httpResponse/expected/headersSchema/type",
+                  "required":true
+                }
+              }
+            },
+            "headers": {
+              "type":"string",
+              "id": "httpResponse/expected/headers",
+              "required":true
+            },
+            "statusCode": {
+              "type":"string",
+              "id": "httpResponse/expected/statusCode",
+              "required":true
+            },
+            "statusMessage": {
+              "type":"string",
+              "id": "httpResponse/expected/statusMessage",
+              "required":true
+            }
+          }
+        },
+        "headers": {
+          "type":"string",
+          "id": "httpResponse/headers",
+          "required":true
+        },
+        "statusCode": {
+          "type":"string",
+          "id": "httpResponse/statusCode",
+          "required":true
+        },
+        "statusMessage": {
+          "type":"string",
+          "id": "httpResponse/statusMessage",
+          "required":true
+        }
+      }
+    }
+  }
+}

 module.exports =
   sampleJson                       : sampleJson
@@ -1082,7 +1246,8 @@
   sampleHttpRequestSchema          : sampleHttpRequestSchema
   sampleHttpResponseSchema         : sampleHttpResponseSchema
   sampleHeadersSchema              : sampleHeadersSchema
-
-
-
-
+  sampleHttpMessageSchema          : sampleHttpMessageSchema
+
+
+
+


feat(sidebar): Add behavior to persist collapsed state of sidebar items

Summary:  - Updates account sidebar to persist collapsed state to the the config

Test Plan:  - manual

Reviewers: evan, bengotow

Reviewed By: bengotow

Differential Revision: https://phab.nylas.com/D2276

--- internal_packages/account-sidebar/lib/account-sidebar.cjsx
+++ internal_packages/account-sidebar/lib/account-sidebar.cjsx
@@ -33,7 +33,7 @@

   constructor: (@props) ->
     @state = @_getStateFromStores()
-    @state.collapsed = {}
+    @state.collapsed = NylasEnv.config.get('core.accountSidebarCollapsed') ? {}

   componentDidMount: =>
     @unsubscribers = []
@@ -111,6 +111,7 @@
   _onToggleCollapsed: (itemId) =>
     collapsed = _.clone(@state.collapsed)
     collapsed[itemId] = !collapsed[itemId]
+    NylasEnv.config.set('core.accountSidebarCollapsed', collapsed)
     @setState({collapsed})

   _getStateFromStores: =>


home/stack: add inactive state to secondary items

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/home/lib/stacks/components/stacktemplateitem/index.coffee
+++ client/home/lib/stacks/components/stacktemplateitem/index.coffee
@@ -2,6 +2,7 @@
 $ = require 'jquery'
 kd = require 'kd'
 React = require 'app/react'
+cx = require 'classnames'

 isAdmin = require 'app/util/isAdmin'
 whoami = require 'app/util/whoami'
@@ -57,11 +58,12 @@

   renderButton: ->

-    { onAddToSidebar, onRemoveFromSidebar,
+    { onAddToSidebar, onRemoveFromSidebar, canCreateStacks
       isVisibleOnSidebar, onCloneFromDashboard, template } = @props

     if onCloneFromDashboard and not template.isMine()
       <ItemLink
+        disabled={not canCreateStacks}
         onClick={onCloneFromDashboard}
         title='CLONE STACK' />

@@ -189,9 +191,15 @@
   return title

-ItemLink = ({ onClick, title }) ->
+ItemLink = ({ onClick, title, disabled }) ->
+  className = cx 'HomeAppView--button',
+    'primary': not disabled
+    'inactive': disabled
+
+  onClick = kd.noop  if disabled
+
   <a
     href="#"
-    className="HomeAppView--button primary"
+    className={className}
     onClick={onClick}
     children={title} />


get the updated value from the input view via a model

--- core/app/backbone/views/autocompleted_add_to_channel_view.coffee
+++ core/app/backbone/views/autocompleted_add_to_channel_view.coffee
@@ -4,6 +4,7 @@
   events:
     'click': 'focusInput'
     "keydown input.typeahead": "parseKeyDown"
+    "keyup input.typeahead": "autoCompleteCurrentValue"

   template:
     text: '<input type="text" class="typeahead">'
@@ -23,12 +24,15 @@
       e.preventDefault()
       e.stopPropagation()

+  autoCompleteCurrentValue: ->
+    searchValue = @$("input.typeahead").val()
+    @model.set(text: searchValue)
+

 class window.AutoCompletedAddToChannelView extends Backbone.Marionette.Layout
   tagName: "div"
   className: "add-to-channel"
   events:
-    "keyup input.typeahead": "autoCompleteCurrentValue"
     "click div.auto_complete": "addCurrentlySelectedChannel"

   regions:
@@ -46,7 +50,10 @@
     @_auto_completes_view = new AutoCompletesView
       mainView: this
       alreadyAdded: @collection
-    @_text_input_view = new TextInputView()
+
+    @model = new Backbone.Model text: ''
+    @model.on 'change', => @autoCompleteCurrentValue()
+    @_text_input_view = new TextInputView model: @model

     @_text_input_view.on 'return', => @addCurrentlySelectedChannel()
     @_text_input_view.on 'down', => @_auto_completes_view.moveSelectionDown()
@@ -93,5 +100,5 @@
     @$el.removeClass("disabled").find("input.typeahead").prop "disabled", false

   autoCompleteCurrentValue: ->
-    searchValue = @$("input.typeahead").val()
+    searchValue = @model.get('text')
     @_auto_completes_view.search_collection.searchFor searchValue


Add a description for each package

Add a description for each package

--- package.cson
+++ package.cson
@@ -1 +1,2 @@
-'main': 'lib/spell-check'
+'main': './lib/spell-check'
+'description': 'Highlight misspelled words in the current text or Markdown editor.\n\nDisplay corrections for the misspelling under the cursor with `meta-0`.'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- source/point.coffee
+++ source/point.coffee
@@ -4,18 +4,18 @@
   defaults to (0, 0).

       point = Point()
-
+
       p.x
       # => 0
-
+
       p.y
       # => 0
-
+
       point = Point(-2, 5)
-
+
       p.x
       # => -2
-
+
       p.y
       # => 5

@@ -70,12 +70,12 @@

         point = Point(1, 1)
         pointCopy = point.copy()
-
+
         point.equal(pointCopy)
         # => true
-
+
         point == pointCopy
-        # => false
+        # => false
     ###
     copy: ->
       Point(@x, @y)
@@ -86,18 +86,18 @@
     to add x and y values without a second point object.

         point = Point(2, 3).add(Point(3, 4))
-
+
         point.x
         # => 5
-
+
         point.y
         # => 7
-
+
         anotherPoint = Point(2, 3).add(3, 4)
-
+
         anotherPoint.x
         # => 5
-
+
         anotherPoint.y
         # => 7

@@ -115,27 +115,27 @@
     to add x and y values without a second point object.

         point = Point(2, 3)
-
+
         point.x
         # => 2
-
+
         point.y
         # => 3
-
+
         point.add$(Point(3, 4))
-
+
         point.x
         # => 5
-
+
         point.y
         # => 7
-
+
         anotherPoint = Point(2, 3)
         anotherPoint.add$(3, 4)
-
+
         anotherPoint.x
         # => 5
-
+
         anotherPoint.y
         # => 7

@@ -158,18 +158,18 @@
     Subtracts a point to this one and returns the new point.

         point = Point(1, 2).subtract(Point(2, 0))
-
+
         point.x
         # => -1
-
+
         point.y
         # => 2
-
+
         anotherPoint = Point(1, 2).subtract(2, 0)
-
+
         anotherPoint.x
         # => -1
-
+
         anotherPoint.y
         # => 2

@@ -185,27 +185,27 @@
     Subtracts a point to this one and returns the new point.

         point = Point(1, 2)
-
+
         point.x
         # => 1
-
+
         point.y
         # => 2
-
+
         point.subtract$(Point(2, 0))
-
+
         point.x
         # => -1
-
+
         point.y
         # => 2
-
+
         anotherPoint = Point(1, 2)
         anotherPoint.subtract$(2, 0)
-
+
         anotherPoint.x
         # => -1
-
+
         anotherPoint.y
         # => 2

@@ -228,10 +228,10 @@
     Scale this Point (Vector) by a constant amount.

         point = Point(5, 6).scale(2)
-
+
         point.x
         # => 10
-
+
         point.y
         # => 12

@@ -247,18 +247,18 @@
     Scale this Point (Vector) by a constant amount. Modifies the point in place.

         point = Point(5, 6)
-
+
         point.x
         # => 5
-
+
         point.y
         # => 6
-
+
         point.scale$(2)
-
+
         point.x
         # => 10
-
+
         point.y
         # => 12

@@ -278,20 +278,20 @@
     treats the point as though it is a vector from the origin to (x, y).

         point = Point(2, 3).norm()
-
+
         point.x
         # => 0.5547001962252291
-
-        point.y
+
+        point.y
         # => 0.8320502943378437
-
+
         anotherPoint = Point(2, 3).norm(2)
-
+
         anotherPoint.x
         # => 1.1094003924504583
-
-        anotherPoint.y
-        # => 1.6641005886756874
+
+        anotherPoint.y
+        # => 1.6641005886756874

     @name norm
     @methodOf Point#
@@ -305,20 +305,20 @@
     treats the point as though it is a vector from the origin to (x, y). Modifies the point in place.

         point = Point(2, 3).norm$()
-
+
         point.x
         # => 0.5547001962252291
-
-        point.y
+
+        point.y
         # => 0.8320502943378437
-
+
         anotherPoint = Point(2, 3).norm$(2)
-
+
         anotherPoint.x
         # => 1.1094003924504583
-
-        anotherPoint.y
-        # => 1.6641005886756874
+
+        anotherPoint.y
+        # => 1.6641005886756874

     @name norm$
     @methodOf Point#
@@ -334,10 +334,10 @@
     Floor the x and y values, returning a new point.

         point = Point(3.4, 5.8).floor()
-
+
         point.x
         # => 3
-
+
         point.y
         # => 5

@@ -353,10 +353,10 @@

         point = Point(3.4, 5.8)
         point.floor$()
-
+
         point.x
         # => 3
-
+
         point.y
         # => 5

@@ -376,10 +376,10 @@
         pointA = Point(2, 3)
         pointB = Point(2, 3)
         pointC = Point(4, 5)
-
+
         pointA.equal(pointB)
         # => true
-
+
         pointA.equal(pointC)
         # => false

@@ -395,7 +395,7 @@
     Computed the length of this point as though it were a vector from (0,0) to (x,y).

         point = Point(5, 7)
-
+
         point.length()
         # => 8.602325267042627

@@ -410,7 +410,7 @@
     Calculate the magnitude of this Point (Vector).

         point = Point(5, 7)
-
+
         point.magnitude()
         # => 8.602325267042627

@@ -425,7 +425,7 @@
     Returns the direction in radians of this point from the origin.

         point = Point(0, 1)
-
+
         point.direction()
         # => 1.5707963267948966 # Math.PI / 2

@@ -447,9 +447,9 @@
       @x * other.x + @y * other.y

     ###*
-    Calculate the cross product of this point and another point (Vector).
+    Calculate the cross product of this point and another point (Vector).
     Usually cross products are thought of as only applying to three dimensional vectors,
-    but z can be treated as zero. The result of this method is interpreted as the magnitude
+    but z can be treated as zero. The result of this method is interpreted as the magnitude
     of the vector result of the cross product between [x1, y1, 0] x [x2, y2, 0]
     perpendicular to the xy plane.

@@ -466,7 +466,7 @@

         pointA = Point(2, 3)
         pointB = Point(9, 2)
-
+
         pointA.distance(pointB)
         # => 7.0710678118654755 # Math.sqrt(50)

@@ -486,12 +486,26 @@
     toString: ->
       "Point(#{@x}, #{@y})"

+    # TODO: Extract as list of math methods to map onto point
+    abs: ->
+      Point
+        x: @x.abs()
+        y: @y.abs()
+
+    snap: (n) ->
+      Point
+        x: @x.snap(n)
+        y: @y.snap(n)
+
+    angle: ->
+      Math.atan2(@y, @x)
+
   ###*
   Compute the Euclidean distance between two points.

       pointA = Point(2, 3)
       pointB = Point(9, 2)
-
+
       Point.distance(pointA, pointB)
       # => 7.0710678118654755 # Math.sqrt(50)

@@ -507,7 +521,7 @@
   ###*
       pointA = Point(2, 3)
       pointB = Point(9, 2)
-
+
       Point.distanceSquared(pointA, pointB)
       # => 50

@@ -536,10 +550,10 @@
   Construct a point on the unit circle for the given angle.

       point = Point.fromAngle(Math.PI / 2)
-
+
       point.x
       # => 0
-
+
       point.y
       # => 1

@@ -558,7 +572,7 @@

       p1 = Point(0, 0)
       p2 = Point(7, 3)
-
+
       Point.direction(p1, p2)
       # => 0.40489178628508343


update isAuth checking

update isAuth checking

--- api/policies/isAuth.coffee
+++ api/policies/isAuth.coffee
@@ -1,5 +1,5 @@
 module.exports = (req, res, next) ->
-  if sails.config.isAuth req
+  if req.headers['x-forwarded-email']?
     next()
   else
     res.forbidden()


Rename file

Rename file

--- usercycle.coffee
+++ usercycle.coffee
@@ -1,87 +0,0 @@
-Meteor.startup ->
-
-  settings = Meteor.settings?.public?.usercycle
-
-  g = if Meteor.isServer then global else window
-
-  if settings.segment
-    g.analytics.load settings.segment.writeKey
-  else
-    throw new Error "Your Segment write key is not defined. Did you forget the --settings flag?"
-
-  # Debug logger
-  log = (msg) ->
-    if settings?.debug
-      console.log "Usercycle: " + msg
-
-
-  # Go ahead and identify if a user is already logged in or logs in
-  if Meteor.isClient
-    Tracker.autorun ->
-      userId = Meteor.userId()
-      if userId
-        log "Identifying #{userId}"
-        g.analytics.identify userId
-
-
-  #
-  # Override createUser
-  #
-  signupEvent = ->
-    settings?.signup?.name or "Signed Up"
-
-  # Wrap createUser so we can modify the callback
-  createUser = Accounts.createUser
-
-  if Meteor.isClient
-    # Send Signed Up event to segment if a user was created
-    trackSignup = (error) ->
-      unless error
-        user = Meteor.user()
-
-        log "Identifying #{user._id}"
-        g.analytics.identify user._id,
-          email: user.emails?[0]?.address
-          createdAt: new Date()
-
-        log "Tracking #{signupEvent()} for #{user._id}"
-        g.analytics.track signupEvent()
-
-    Accounts.createUser = ->
-      args = _.values arguments
-      callback = args[args.length-1]
-
-      if _.isFunction callback
-        args[args.length-1] = (error) ->
-          trackSignup error
-          callback error
-      else
-        args.push (error) ->
-          trackSignup error
-
-      createUser.apply @, args
-
-  else if Meteor.isServer
-    Accounts.createUser = ->
-      userId = createUser.apply @, _.values(arguments)
-      log "Tracking #{signupEvent()} for #{userId}"
-      g.analytics.track
-        userId: userId
-        event: signupEvent()
-        properties:
-          user:
-            userId: userId
-            email: options?.email
-            createdAt: new Date()
-      userId
-
-
-  # Itegration with IR
-  Router.onRun ->
-    name = @route.getName()
-    retentionRoutes = _.compact _.flatten [settings?.retention?.routes]
-    if Meteor.userId() and _.contains retentionRoutes, name
-      eventName = settings?.retention?.name or name
-      log "Tracking #{eventName} for #{Meteor.userId()}"
-      g.analytics.track eventName
-    @next()


Workspace: Added missing header to collaborative tabbed editor.

--- client/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -111,6 +111,7 @@

   pistachio: ->
     """
+      {{> @header}}
       {{> @tabHandleContainer}}
       {{> @tabView}}
     """


HUB-68 : Add unit test

HUB-68 : Add unit test

--- test/hSession.coffee
+++ test/hSession.coffee
@@ -105,3 +105,15 @@
       hMessage.payload.should.have.property('result').and.match(/Command not available/)
       done();

+  it "should always override publisher before sending", (done) ->
+    send = hActor.send
+    hActor.send = (hMessage) ->
+      hMessage.publisher.should.be.equal(hActor.actor)
+      hActor.send = send
+      done()
+
+    msg = hActor.buildMessage("urn:localhost:otherActor", "string", {})
+    msg.publisher = config.logins[0].urn
+    hActor.onMessage msg, (hMessage) ->
+
+


impr bdd of test

impr bdd of test

--- test/lib.test.coffee
+++ test/lib.test.coffee
@@ -7,24 +7,30 @@

   request = null

+  app = null
+  subapp = null
+
+  routesApp = ->
+    'get /': (req, res) -> res.send "Get Hello"
+    'post /': (req, res) -> res.send "Post Hello"
+    'scope /api':
+      'get /': (req, res) -> res.json info: 'ok'
+      'post /foo': (req, res) -> res.json info: 'bar'
+    'mount /subapp': subapp
+
+  routesSubApp = ->
+    'get /': (req, res) -> res.send "subapp"
+    'get /archive': (req, res) -> res.send "archive"
+
   before ->
     app = express()
-    blog = express()
+    subapp = express()

     routesmanager app
-    routesmanager blog
+    routesmanager subapp

-    app.loadRoutes
-      'get /': (req, res) -> res.send "Get Hello"
-      'post /': (req, res) -> res.send "Post Hello"
-      'scope /api':
-        'get /': (req, res) -> res.json info: 'ok'
-        'post /foo': (req, res) -> res.json info: 'bar'
-      'mount /blog': blog
-
-    blog.loadRoutes
-      'get /': (req, res) -> res.send "blog"
-      'get /archive': (req, res) -> res.send "archive"
+    app.loadRoutes routesApp()
+    subapp.loadRoutes routesSubApp()

     request = supertest app

@@ -42,30 +48,33 @@
       .expect "Post Hello"
       .end done

-  context "GET /api", (done) ->
-    it "should respond 200 and info: 'ok'", (done) ->
-      request.get '/api'
-      .expect 200
-      .expect info: 'ok'
-      .end done
-
-  context "POST /api/foo", (done) ->
-    it "should respond 200 and info: 'bar'", (done) ->
-      request.post '/api/foo'
-      .expect 200
-      .expect info: 'bar'
-      .end done
-
-  context "GET /blog", ->
-    it "should respond 200 and blog", (done) ->
-      request.get '/blog'
-      .expect 200
-      .expect "blog"
-      .end done
-
-  context "GET /blog/archive", ->
-    it "should respond 200 and archive", (done) ->
-      request.get '/blog/archive'
-      .expect 200
-      .expect "archive"
-      .end done+  context "SCOPE /api", ->
+    context "GET /", (done) ->
+      it "should respond 200 and info: 'ok'", (done) ->
+        request.get '/api'
+        .expect 200
+        .expect info: 'ok'
+        .end done
+
+    context "POST /foo", (done) ->
+      it "should respond 200 and info: 'bar'", (done) ->
+        request.post '/api/foo'
+        .expect 200
+        .expect info: 'bar'
+        .end done
+
+  context "MOUNT /subapp", ->
+    context "GET /", ->
+      it "should respond 200 and subapp", (done) ->
+        request.get '/subapp'
+        .expect 200
+        .expect "subapp"
+        .end done
+
+    context "GET /archive", ->
+      it "should respond 200 and archive", (done) ->
+        request.get '/subapp/archive'
+        .expect 200
+        .expect "archive"
+        .end done
+


Added forgotten classname

Added forgotten classname

--- core/app/backbone/views/tour/tour_users_list_view.coffee
+++ core/app/backbone/views/tour/tour_users_list_view.coffee
@@ -6,7 +6,8 @@
   _.extend @prototype, Backbone.Factlink.TooltipMixin

   template: 'tour/interesting_user'
-  className: 'tour-interesting-user'
+  className: 'tour-interesting-user action-button'
+
   initialize: ->
     @bindTo @model.followers, 'change', @updateButton, @


Fix a javascript error in search bar Property item_type may not always be defined.

--- src/opac/search_bar.coffee
+++ src/opac/search_bar.coffee
@@ -249,7 +249,7 @@
 			$(':input', @).each ->
 				switch @name
 					when 'search', 'offset', 'visibility_limit' then return
-					when 'item_type' then o.item_type = o.item_type.join ''
+					when 'item_type' then o.item_type ?= o.item_type.join ''
 				$(@).val o[@name] if o[@name]

 			$('.sort select', @).change() # FIXME: pokes into sort chooser`


:memo: Mark PackageManager constructor as private

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -23,6 +23,7 @@
 class PackageManager
   Emitter.includeInto(this)

+  # Private:
   constructor: ({configDirPath, devMode, @resourcePath}) ->
     @packageDirPaths = [path.join(configDirPath, "packages")]
     if devMode


Add test for renaming non-existing node

Add test for renaming non-existing node

--- spec/Network.coffee
+++ spec/Network.coffee
@@ -194,6 +194,11 @@
         g.renameNode 'Callback', 'Func'
       it 'shouldn\'t have the process in the old location', ->
         chai.expect(n.processes.Callback).to.be.undefined
+      it 'should fail to rename with the old name', (done) ->
+        n.renameNode 'Callback', 'Func', (err) ->
+          chai.expect(err).to.be.an 'error'
+          chai.expect(err.message).to.contain 'not found'
+          done()
       it 'should have informed the ports of their new node name', ->
         for name, port of n.processes.Func.component.inPorts.ports
           chai.expect(port.name).to.equal name


a cell can have a bottom left

a cell can have a bottom left

--- spec/lib/cell-spec.coffee
+++ spec/lib/cell-spec.coffee
@@ -33,3 +33,9 @@
     When -> @res = @cell.bottom()
     Then -> expect(@res.x).toBe 4
     And -> expect(@res.y).toBe 5
+
+  describe 'has a bottom left', ->
+
+    When -> @res = @cell.bottomLeft()
+    Then -> expect(@res.x).toBe 3
+    And -> expect(@res.y).toBe 5


Only get channels data when robot brain has loaded

--- src/hubot-mute.coffee
+++ src/hubot-mute.coffee
@@ -21,10 +21,14 @@
 mute_listener = null
 mute_all = false
 mute_explain = {}
+mute_first = true;

 module.exports = (robot) ->
-  robot.brain && mute_channels = robot.brain.get('mute_channels') or []
-  robot.brain && mute_all = robot.brain.get('mute_all') or false
+  robot.brain.on 'loaded', ->
+    if mute_first
+      mute_first = false
+      mute_channels = robot.brain.get('mute_channels') or []
+      mute_all = robot.brain.get('mute_all') or false

   robot.respond /mute list$/i, (msg) ->
     msg.finish()


Added specs for the collision problem

Added specs for the collision problem

--- spec/tin-foil_spec.coffee
+++ spec/tin-foil_spec.coffee
@@ -334,6 +334,56 @@
       @Extended.my_extension 'new-value'
       @Base.definition('extension').defaultValue.should.equal 'original-value'

+    it 'should instantiate new maps', ->
+      @Mappable = TinFoil.extend()
+      @Mappable.define 'map', as: TinFoilMap, with_alias: 'add_to_map'
+      @Base.define 'mappable', as: @Mappable
+
+      @Another = TinFoil.extend()
+      @Another.define 'mappable', as: @Mappable
+
+      @Base.add_to_map 'key 1', 'base value 1'
+      @Base.add_to_map 'key 2', 'base value 1'
+      @Another.add_to_map 'key 1', 'another value 2'
+      @Another.add_to_map 'key 2', 'another value 2'
+
+      @Base.definition('mappable').type
+        .definition('map').defaultValue.should.not.equal @Another.definition('mappable').type.definition('map').defaultValue
+
+      @Base.definition('mappable').type
+        .definition('map').defaultValue.get('key 1').should.equal 'base value 1'
+      @Base.definition('mappable').type
+        .definition('map').defaultValue.get('key 2').should.equal 'base value 2'
+      @Another.definition('mappable').type
+        .definition('map').defaultValue.get('key 1').should.equal 'another value 1'
+      @Another.definition('mappable').type
+        .definition('map').defaultValue.get('key 2').should.equal 'another value 2'
+
+    it 'should instantiate new collections', ->
+      @Collectable = TinFoil.extend()
+      @Collectable.define 'collection', as: TinFoilCollection, with_alias: 'add_to_collection'
+      @Base.define 'collectable', as: @Collectable
+
+      @Another = TinFoil.extend()
+      @Another.define 'collectable', as: @Collectable
+
+      @Base.add_to_collection 'base a'
+      @Base.add_to_collection 'base b'
+      @Another.add_to_collection 'another a'
+      @Another.add_to_collection 'another b'
+
+      @Base.definition('collectable').type
+        .definition('collection').defaultValue.should.not.equal @Another.definition('collectable').type.definition('collection').defaultValue
+
+      @Base.definition('collectable').type
+        .definition('collection').defaultValue.get(0).should.equal 'base a'
+      @Base.definition('collectable').type
+        .definition('collection').defaultValue.get(1).should.equal 'base b'
+      @Another.definition('collectable').type
+        .definition('collection').defaultValue.get(0).should.equal 'another a'
+      @Another.definition('collectable').type
+        .definition('collection').defaultValue.get(2).should.equal 'another b'
+
   describe 'compilation', ->
     Activity = require '../lib/activity'

@@ -388,6 +438,7 @@
         expect(@compiled.scene.correctResponsePattern).to.be.undefined

       it 'should compile the collection', ->
+        @compiled.scene.definition.choices.length.should.equal 2
         @compiled.scene.definition.choices[0].should.equal 'Jump out the window'
         @compiled.scene.definition.choices[1].should.equal 'Stay inside and play xBox'

@@ -396,6 +447,6 @@
         expect(@compiled.scene.emptyMap).to.be.undefined

       it 'should compile the map', ->
+        expect(@compiled.scene.definition.extensions['urn:tenant-id']).to.be.undefined
         @compiled.scene.definition.extensions['urn:organisation-id'].should.equal '4321-8765-dcba'
         @compiled.scene.definition.extensions['urn:nothing'].should.equal 'nowhere'
-        expect(@compiled.scene.definition.extensions['urn:tenant-id']).to.be.undefined


test S keybinding

test S keybinding

substitute line is a thing

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -139,6 +139,17 @@
         expect(editor.getText()).toBe '12345'
         expect(editor.getCursorScreenPosition()).toEqual [0, 0]
         expect(vimState.getRegister('"').text).toBe '0'
+
+    describe "the S keybinding", ->
+      it "deletes the entire line and enters insert mode", ->
+        editor.setText("12345\nabcde\nABCDE")
+        editor.setCursorScreenPosition([1,3])
+
+        keydown('S', element: editor[0])
+        expect(editor).toHaveClass 'insert-mode'
+        expect(editor.getText()).toBe '12345\n\nABCDE'
+        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+        expect(vimState.getRegister('"').text).toBe 'abcde'

     describe "the d keybinding", ->
       describe "when followed by a d", ->


expandUrls now also catches xxxx.yy type urls  without protocols and keeps the protocol invisible when a url without protocol gets parsed. ) koding.com is shown as koding.com and treated as //koding.com

--- core/utils.coffee
+++ core/utils.coffee
@@ -171,8 +171,15 @@

   expandUrls: (text) ->
     return null unless text
-    text.replace /[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&#\+\?\/.=]+/g, (url) ->
-      "#{url}"
+    text.replace /([A-Za-z]+:\/\/)?[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&#\+\?\/.=]+/g, (url) ->
+
+      visibleUrl = url
+
+      if not /[A-Za-z]+:\/\//.test url
+        # url has no protocol
+        url = '//'+url
+
+      "#{visibleUrl}"

   putShowMore: (text, l = 500)->
     shortenedText = __utils.shortenText text,


specify url replacement for mobile only on oauth/authorize urls to match tripit provider requirement.

--- src/presentationLayer/auth/index.coffee
+++ src/presentationLayer/auth/index.coffee
@@ -277,7 +277,7 @@
 							url += k + '=' + v
 					opts = JSON.parse(req.params.opts)
 					if opts.mobile is 'true' and provider_conf.mobile?.url?
-						url_split = url.split("/oauth/")
+						url_split = url.split("/oauth/authorize")
 						if url_split.length is 2
 							url = provider_conf.mobile.url + '/oauth/' + url_split[1]


Don't require auth for requests

Don't require auth for requests

--- app/routes/requests.coffee
+++ app/routes/requests.coffee
@@ -1,7 +1,6 @@
 `import TravisRoute from 'travis/routes/basic'`

 Route = TravisRoute.extend
-  needsAuth: true
   setupController: ->
     @_super.apply this, arguments


bug fix

bug fix

--- src/lib/entity.coffee
+++ src/lib/entity.coffee
@@ -141,12 +141,6 @@

 createEntity = (collection, id, timestamp, data, callback) ->

-    #create proper position field
-    pos =
-      lat: data.latE6/1e6
-      lng: data.lngE6/1e6
-    data.pos = pos
-
     data.time = timestamp

     Database.db.collection(collection).update
@@ -166,6 +160,10 @@

 createPortalEntity = (id, timestamp, data, callback) ->

+    data.pos =
+        lat: data.latE6 / 1e6
+        lng: data.lngE6 / 1e6
+
     createEntity 'Portals', id, timestamp, data, ->

         if data.team isnt 'NEUTRAL' and argv.detail isnt 'false'


1 more test for holdWhen

1 more test for holdWhen

--- spec/specs/holdwhen.coffee
+++ spec/specs/holdwhen.coffee
@@ -1,4 +1,4 @@
-# build-dependencies: startwith, filter, delay, interval, take, bus
+# build-dependencies: startwith, filter, delay, interval, take, bus, mapEnd, map, merge

 describe "EventStream.holdWhen", ->
   describe "Keeps events on hold when a property is true", ->
@@ -69,6 +69,14 @@
         Bacon.fromArray([new Bacon.Error(), "2"]).
           holdWhen(later(20, false).startWith(true))
       [error(), [20, "2"]])
+  describe.only "Sends the entire buffer even if valve ends", ->
+    expectStreamEvents(
+      ->
+        left = series(1, [1, 2, 3, 4, 5])
+        bufferedRight = series(1, [6, 7, 8, 9, 10]).
+            holdWhen(left.map(true).startWith(true).mapEnd(false))
+        left.merge(bufferedRight)
+      [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
   it "toString", ->
     expect(Bacon.once(1).holdWhen(Bacon.constant(true)).toString()).to.equal(
       "Bacon.once(1).holdWhen(Bacon.constant(true))")


fix alwaysOpenExisting specs

fix alwaysOpenExisting specs

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -12,15 +12,6 @@
       disposable.dispose()
       done()
     causeFileToOpen()
-
-waitsForWindowOpen = (causeWindowOpenCalled) ->
-  waitsFor (done) ->
-    workspaceOpen = atom.workspace.open
-    atom.workspace.open = ->
-      delete atom.workspace.open
-      workspaceOpen.apply(this, arguments)
-      setImmediate(done)
-    causeWindowOpenCalled()

 clickEvent = (properties) ->
   event = $.Event('click')
@@ -3143,7 +3134,6 @@

     describe "opening existing opened files in existing split panes", ->
       beforeEach ->
-        atom.project.setPaths([path1])

         jasmine.attachToDOM(workspaceElement)
         waitsForFileToOpen ->
@@ -3205,7 +3195,7 @@

             treeView.focus()

-            waitsForWindowOpen ->
+            waitsForFileToOpen ->
               sampleTxt.trigger clickEvent(originalEvent: {detail: 1})

             runs ->
@@ -3216,10 +3206,12 @@
             expect(treeView).toHaveFocus()

           it "doesn't open the file in the active pane", ->
-            expect(atom.views.getView(firstPane)).toHaveFocus()
+            expect(atom.views.getView(treeView)).toHaveFocus()
             expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')

       describe "when a file is double-clicked", ->
+        beforeEach ->
+          atom.config.set "tree-view.alwaysOpenExisting", true
         activePaneItem = null

         beforeEach ->
@@ -3228,10 +3220,12 @@

           treeView.focus()

-          waitsForWindowOpen ->
+          waitsForFileToOpen ->
             sampleTxt.trigger clickEvent(originalEvent: {detail: 1})
             sampleTxt.trigger clickEvent(originalEvent: {detail: 2})

+          waits 100
+
           runs ->
             activePaneItem = atom.workspace.getActivePaneItem()


:art: use spyOn(), andCallFake() to avoid actually spawning file manager

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -3026,8 +3026,9 @@
         atom.workspace.open(filePath)

       runs ->
+        {BufferedProcess} = require 'atom'
+        spyOn(BufferedProcess.prototype, 'spawn').andCallFake ->
         fileManagerProcess = treeView.showCurrentFileInFileManager()
-        {BufferedProcess} = require 'atom'
         expect(fileManagerProcess instanceof BufferedProcess).toBeTruthy()
         fileManagerProcess.kill()


More information in the augment object

More information in the augment object

--- library/gearing/augment.coffee
+++ library/gearing/augment.coffee
@@ -78,7 +78,9 @@
             namespace[method] = (resource, implementation) =>
                 augment = Augment.augmentForResource resource
                 augment.service::[method] = implementation
-                implementation.service = service; implementation
+                implementation.service = service
+                implementation.augment = augment
+                implementation

     # Obtain the augment object for the specified resource. If such
     # an object does not exist, it will be automatically created and


don't use the $-style ctor vars

don't use the $-style ctor vars

--- library/nucleus/restful.coffee
+++ library/nucleus/restful.coffee
@@ -151,12 +151,16 @@
     # that were used for configuring the class of this service.
     # It is async, so be sure to call the `decide` with boolean!
     matches: (request, response, decide) ->
-        conditions = try @constructor.condition() or []
+        assert _.isObject(request), "got invalid request"
+        assert _.isFunction(decide), "incorrect callback"
+        conditions = try @constructor.condition() or null
         conditions = Array() unless _.isArray conditions
         identify = try @constructor?.identify().underline
         return decide no if @constructor.DISABLE_SERVICE
         p = (i, cn) -> i.limitation request, response, cn
         fails = "Service #{identify} fails some conditions"
+        notify = "Running %s service conditional sequences"
+        logger.debug notify.toString(), identify.toString()
         return super request, response, (decision) =>
             return decide no unless decision is yes
             async.every conditions, p, (confirms) ->


MapCtrl: pass function to  to suppress eval

--- app/scripts/mapctrl.coffee
+++ app/scripts/mapctrl.coffee
@@ -23,13 +23,12 @@
         r = getRegItem(e.target.feature)

         if r
-          hoverText = r.region + ": " + r[$scope.choroChoice].toPrecision(3) + " %"
+          hoverText = r.region + ": " + r[$scope.choroChoice].toPrecision(3) + "%"
         else
           hoverText = ""

-        $scope.$apply(
-            $scope.hoverText = hoverText
-        )
+        $scope.$apply (scope) ->
+          scope.hoverText = hoverText

         layer = e.target
         layer.setStyle
@@ -42,9 +41,8 @@

       regionMouseOut = (e) ->
         regLayer.resetStyle(e.target)
-        $scope.$apply(
-            $scope.hoverText = ""
-        )
+        $scope.$apply (scope) ->
+          scope.hoverText = ""

       regionClick = (e) ->
         # dont do this here, the watch below will take care of that


re-eanble concurrent error failing, but with a random delay

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -113,7 +113,7 @@
             warning  : 30          # display warning and stop using connection if run takes this many seconds or more
             error    : 10*60       # kill any query that takes this long (and corresponding connection)
             concurrent_warn  : 500  # if number of concurrent outstanding db queries exceeds this number, put a concurrent_warn message in the log.
-            concurrent_error : 0  # if nonzero, and this many queries at once, any query instantly fails!
+            concurrent_error : 700  # if nonzero, and this many queries at once, any query fails after a slight delay.
             mod_warn : 2           # display MOD_WARN warning in log if any query modifies at least this many docs
             cache_expiry  : 15000  # expire cached queries after this many milliseconds (default: 15s)
             cache_size    : 250    # cache this many queries; use @...query.run({cache:true}, cb) to cache result for a few seconds
@@ -318,7 +318,7 @@
                         if that._concurrent_error and that._concurrent_queries > that._concurrent_error
                             winston.debug("rethink: *** concurrent_error *** CONCURRENT ERROR THRESHOLD #{that._concurrent_error} EXCEEDED -- FAILING QUERY")
                             that._concurrent_queries -= 1
-                            cb("concurrent_error")
+                            setTimeout((()=>cb("concurrent_error")), 5*Math.random())
                             return

                         # choose a random connection


fix why blob upload was really causing trouble: setInterval instead of setTimeout!

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -2742,11 +2742,11 @@
                                 setTimeout(cb, 1000*opts.throttle)
                             else
                                 cb()
-                async.mapLimit v, opts.map_limit, f, () =>
-                    dbg("finished this round")
+                async.mapLimit v, opts.map_limit, f, (err) =>
+                    dbg("finished this round -- #{err}")
                     if opts.repeat_until_done_s and v.length > 0
                         dbg("repeat_until_done triggering another round")
-                        setInterval((=> @copy_all_blobs_to_gcloud(opts)), opts.repeat_until_done_s*1000)
+                        setTimeout((=> @copy_all_blobs_to_gcloud(opts)), opts.repeat_until_done_s*1000)
                     else
                         dbg("done : #{misc.to_json(opts.errors)}")
                         opts.cb(if misc.len(opts.errors) > 0 then opts.errors)


another fix related to touching active projects

--- src/smc-util/schema.coffee
+++ src/smc-util/schema.coffee
@@ -447,6 +447,9 @@
                 id          : true
                 project_id  : true
                 path        : true
+            check_hook : (db, obj, account_id, project_id, cb) ->
+                # hook to note that project is being used
+                db.touch_project(project_id: obj.project_id)

 schema.hub_servers =
     primary_key : 'host'


Fix small issue with graded callback

Fix small issue with graded callback

--- lms/static/coffee/src/staff_grading/staff_grading.coffee
+++ lms/static/coffee/src/staff_grading/staff_grading.coffee
@@ -219,18 +219,23 @@

   setup_score_selection: =>
     # first, get rid of all the old inputs, if any.
-    @grade_selection_container.html('Choose score: ')
+    @grade_selection_container.html("""
+
Overall Score

+
Choose an overall score for this submission.

+    """)
     # Now create new labels and inputs for each possible score.
     for score in [0..@max_score]
       id = 'score-' + score
       label = """#{score}"""
       input = """
-              <input type="radio" name="grade-selection" id="#{id}" value="#{score}"/>
+              <input type="radio" class="grade-selection" name="grade-selection" id="#{id}" value="#{score}"/>
               """  # "  fix broken parsing in emacs
       @grade_selection_container.append(input + label)
+    $('.grade-selection').click => @graded_callback()

     @score_selection_container.html(@rubric)
     $('.score-selection').click => @graded_callback()
+

   graded_callback: () =>
     @grade = $("input[name='grade-selection']:checked").val()


add `Bu.clone()` util

add `Bu.clone()` util

--- src/Bu.coffee
+++ src/Bu.coffee
@@ -83,6 +83,16 @@
 			defaultOptions[i] = givenOptions[i]
 	return defaultOptions

+# clone an Object or Array
+Bu.clone = (target, deep = false) ->
+	# TODO deal with deep
+	if target instanceof Array
+		clone = []
+		clone[i] = target[i] for own i of target
+	else if target instanceof Object
+		clone = {}
+		clone[i] = target[i] for own i of target
+
 ###
 # polyfill
 ###


Fail authority test

Fail authority test

--- src/test/test_world.coffee
+++ src/test/test_world.coffee
@@ -88,8 +88,13 @@
     authority = world._spawn_world("authority")
     world.put("_AUTHORITY", authority)
     rebel = world._spawn_world("rebellious")
-    t.ok comply
-    t.ok rebel
+
+    world.put("foo", "bar")
+    t.equal comply.get('foo'), 'bar', 'comply foo'
+    t.equal rebel.get('foo'), 'bar', 'rebel foo'
+    # set authority
+    t.equal comply.get('foo'), 'bar', 'comply foo'
+    t.equal rebel.get('foo'), 'baz', 'rebel foo with authority'
     t.end()

   test 'world has dynamic properties', (t) ->


Fix tests

Fix tests

--- api/apps/graphql/test/resolvers.coffee
+++ api/apps/graphql/test/resolvers.coffee
@@ -25,11 +25,11 @@

     it 'returns throws error when trying to view a draft without channel_id', ->
       args = published: false
-      (-> resolvers.articles({}, args, {}, {})).should.throw('Must pass channel_id to view unpublished articles. Or pass published: true to only view published articles.')
+      resolvers.articles.bind({}, args, {}, {}).should.throw()

     it 'returns throws an error when trying to view an unauthorized draft', ->
       args = published: false, channel_id: '123'
-      (-> resolvers.articles({}, args, {}, {})).should.throw('Must be a member of this channel to view unpublished articles. Pass published: true to only view published articles.')
+      resolvers.articles.bind({}, args, {}, {}).should.throw()

     it 'can view drafts', ->
       args = published: false, channel_id: '456'


add a test for alternate template

add a test for alternate template

--- test/new_test.coffee
+++ test/new_test.coffee
@@ -5,6 +5,7 @@
 rimraf = require 'rimraf'
 mkdirp = require 'mkdirp'
 require('./helpers')(should)
+test_tpl_path = 'https://github.com/jenius/sprout-test-template.git'

 Roots = require '../lib'

@@ -36,6 +37,13 @@
      .on('deps:installing', increment)
      .on('deps:finished', increment)

-  it 'should create a project with another template if provided'
-    # this needs to add the template, create, then remove it
-    # depends on the roots template commands
+  it 'should create a project with another template if provided', (done) ->
+    p = path.join(test_path, 'testing')
+
+    Roots.template.add(name: 'foobar', url: test_tpl_path)
+      .catch(done)
+      .then ->
+        Roots.new(path: p, options: { foo: 'bar' }, template: 'foobar').on 'done', ->
+          fs.existsSync(path.join(p, 'index.html')).should.be.ok
+          rimraf.sync(p)
+          Roots.template.remove('foobar').then(-> done())


Less aggressive image preloading.

Less aggressive image preloading.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -77,13 +77,11 @@
   isRead: (entryId) ->
     feedbin.Counts.get().isRead(entryId)

-  precacheImages: (data) ->
-    if feedbin.data.precache_images == true
-      entries = []
-      $.each data, (entryId, entry) ->
-        if !feedbin.isRead(entryId * 1)
-          entries.push(entry.content)
-      $(entries.join())
+  preloadImages: (id) ->
+    id = parseInt(id)
+    if feedbin.entries[id] && !_.contains(feedbin.preloadedImageIds, id)
+      $(feedbin.entries[id].content)
+      feedbin.preloadedImageIds.push(id)

   localizeTime: (container) ->
     $("time.timeago").timeago()
@@ -126,7 +124,8 @@
     if entry_ids.length > 0
       $.getJSON feedbin.data.preload_entries_path, {ids: entry_ids.join(',')}, (data) ->
         $.extend feedbin.entries, data
-        feedbin.precacheImages(data)
+        ids = _.keys(data)
+        feedbin.preloadImages(ids[0])

   readability: () ->
     feedId = feedbin.selectedEntry.feed_id
@@ -499,6 +498,8 @@

   dragOwner: null

+  preloadedImageIds: []
+
 $.extend feedbin,
   init:

@@ -699,6 +700,14 @@
         event.preventDefault()
         return

+    preloadImages: ->
+      $(document).on 'click', '[data-behavior~=show_entry_content]', ->
+        selected = $(@).parents('li')
+        next = selected.next('li')
+        if next.length > 0
+          id = next.data('entry-id')
+          feedbin.preloadImages(id)
+        return

     feedSelected: ->
       $(document).on 'click', '[data-behavior~=back_to_feeds]', ->


fix: removed unneeded file upload package

--- src/menu/views/ExportMenu.coffee
+++ src/menu/views/ExportMenu.coffee
@@ -3,7 +3,6 @@
 FastaExporter = require("biojs-io-fasta").writer
 _ = require "underscore"
 blobURL = require "blueimp_canvastoblob"
-Upload = require "../../utils/upload"
 corsURL = require("../../utils/proxy").corsURL
 xhr = require "xhr"


remove console log call

remove console log call

--- app/assets/javascripts/repos.js.coffee
+++ app/assets/javascripts/repos.js.coffee
@@ -7,7 +7,6 @@
       return false
     link = $(event.target)
     $.get link.attr('href'), (data)->
-      console.log(data)
       link.parent().append HandlebarsTemplates['repos/index'](data)
       link.addClass('populated')
       link.href = null


refactored copy and paste functionality

refactored copy and paste functionality

--- app/assets/javascripts/snippets.coffee
+++ app/assets/javascripts/snippets.coffee
@@ -6,7 +6,7 @@
     $element.prepend('<button class="btn btn-default" data-clipboard-snippet><i class="fa fa-clipboard"></i></button>')

     clipboardSnippets = new Clipboard('[data-clipboard-snippet]', target: (trigger) ->
-      code = $(trigger).siblings( "code" ).get(0)
+      $(trigger).siblings( "code" ).get(0)
     )

     clipboardSnippets.on 'success', (e) ->
@@ -30,7 +30,4 @@
   return

 $ ->
-# -----------
-# Automation
-# -----------
   $('.snippet').initSnippetCopyToClipboard()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/oscillator.coffee
+++ src/oscillator.coffee
@@ -5,7 +5,7 @@
 @constructor
 @param {Number} amplitude How much to scale the oscillator function value
 @param {Number} period How fast the osciallator function repeats
-@param {Number} offset How much to offset the created osciallator function
+@param {Number} offset How much to offset the created osciallator function. Useful for translat
 ###
 Oscillator = (options={}) ->
   {amplitude, period, offset} = options


Makin hte post request to the segmenting server

--- src/api.coffee
+++ src/api.coffee
@@ -14,14 +14,16 @@

 sendFileToSegmenter = (path,callback) ->
+  callback_url = "http://192.168.201.196:3000/internal/add_segment"
   post_options =
-    host: 'localhost'
-    port: '3000'
-    path: '/testpost'
+    host: 'vbox'
+    port: '15437'
+    path: "/segment_ts/?callback_url=#{encodeURIComponent(callback_url)}"
     method: 'POST'

-  console.log "sending file at path #{path}"
+  console.log "sending file at path #{path} to:"
+  console.log post_options
   post_request = http.request post_options, (res) ->
     if callback? then callback()

@@ -80,7 +82,7 @@

     # Find out where it should go
     destdir = "#{__dirname}/../public/segmented/"
-    filename = "#{stream_id}-#{Math.floor(Math.random()*1000000)}"
+    filename = "#{stream_id}-#{Math.floor(Math.random()*1000000)}.ts"
     dest = destdir + filename

     # Write out the file


create a pretty address as 'query' for the address model

--- src/app.coffee
+++ src/app.coffee
@@ -23,6 +23,7 @@

   $.ajaxPrefilter (options, originalOptions, jqXHR) ->
     options.url = "http://researchdb.warwick.ac.uk/liverpool/api" + options.url
+    if options.url.split(":")[0] != "http"
     return

   Address = Backbone.RelationalModel.extend()
@@ -76,7 +77,14 @@
           # use some render helpers to add to the model some text to format fuzzy date types
       @.birthdate = @fuzzyDateMaker @birthtype, @birthdate1, @birthdate2
       @.deathdate = @fuzzyDateMaker @deathtype, @deathdate1, @deathdate2
-
+      _.each response.addresses, (address) ->
+        query = []
+        query.push address.address.split(",")[1].trim() + " " + address.address.split(",")[0].trim() #street
+        #address.district != "" && query.push address.district currently causes failed lookup
+        query.push "Liverpool"
+        address.region != "" && query.push address.region
+        address.country != "" && query.push address.country
+        address.query = query.join ", "
       response
     fuzzyDateMaker: (type, date1, date2) ->


Remove extra console.log in app

Remove extra console.log in app

--- src/app.coffee
+++ src/app.coffee
@@ -1,7 +1,5 @@
 nconf = require 'nconf'
 nconf.formats.yaml = require 'nconf-yaml'
-
-console.log process.cwd()

 nconf.file 'user', {
   file: "#{process.cwd()}/breakpad-server.yaml", format: nconf.formats.yaml


Test for returning multiple queries together.

See #7.

--- tests.coffee
+++ tests.coffee
@@ -59,6 +59,25 @@
     ,
       Users.find userId,
         fields:
+          addresses: 1
+
+  Meteor.publish 'users-posts-and-addresses-together', (userId) ->
+    @related (user) ->
+      [
+        Posts.find(
+          _id:
+            $in: user?.posts or []
+        )
+      ,
+        Addresses.find(
+          _id:
+            $in: user?.addresses or []
+        )
+      ]
+    ,
+      Users.find userId,
+        fields:
+          posts: 1
           addresses: 1

   Meteor.publish 'users-posts-count', (userId, countId) ->
@@ -376,12 +395,7 @@
       ]
   ]

-  testClientMultiple: [
-    ->
-      @userId = Random.id()
-
-      @assertSubscribeSuccessful 'users-posts-and-addresses', @userId, @expect()
-  ,
+  @multiple: [
     ->
       @assertEqual Posts.find().fetch(), []
       @assertEqual Addresses.find().fetch(), []
@@ -465,5 +479,19 @@
       @assertItemsEqual _.pluck(Addresses.find().fetch(), '_id'), []
   ]

+  testClientMultiple: [
+    ->
+      @userId = Random.id()
+
+      @assertSubscribeSuccessful 'users-posts-and-addresses', @userId, @expect()
+  ].concat @multiple
+
+  testClientMultipleTogether: [
+    ->
+      @userId = Random.id()
+
+      @assertSubscribeSuccessful 'users-posts-and-addresses-together', @userId, @expect()
+  ].concat @multiple
+
 # Register the test case.
 ClassyTestCase.addTest new RelatedTestCase()


Added addEventHandler and raise event snippets

--- snippets/language-sqf.cson
+++ snippets/language-sqf.cson
@@ -163,6 +163,47 @@
     'description': 'Function Header per ACE3 guidelines.'
     'descriptionMoreURL': 'http://ace3mod.com/wiki/development/coding-guidelines.html#3-1-function-definitions'

+  # ACE3 Event system
+  'ACE addEventHandler':
+    'prefix': 'ace_addEventHandler'
+    'body': """
+      ["${1:Event name}", ${2:code}] call EFUNC(common,addEventhandler);
+    """
+    'description': 'Add ACE3 Eventhandler'
+    'descriptionMoreURL': 'http://ace3mod.com/wiki/development/ace3-events-system.html'
+
+  'ACE Local Event':
+    'prefix': 'localEvent'
+    'body': """
+      ["${1:Event name}", [${2:Parameters}]] call EFUNC(common,localEvent);
+    """
+    'description': 'Raise an local ACE3 Eventhandler'
+    'descriptionMoreURL': 'http://ace3mod.com/wiki/development/ace3-events-system.html'
+
+  'ACE Server Event':
+    'prefix': 'serverEvent'
+    'body': """
+      ["${1:Event name}", [${2:Parameters}]] call EFUNC(common,serverEvent);
+    """
+    'description': 'Raise a Server ACE3 Eventhandler'
+    'descriptionMoreURL': 'http://ace3mod.com/wiki/development/ace3-events-system.html'
+
+  'ACE Global Event':
+    'prefix': 'globalEvent'
+    'body': """
+      ["${1:Event name}", [${2:Parameters}]] call EFUNC(common,globalEvent);
+    """
+    'description': 'Raise a Global ACE3 Eventhandler'
+    'descriptionMoreURL': 'http://ace3mod.com/wiki/development/ace3-events-system.html'
+
+  'ACE Target Event':
+    'prefix': 'targetEvent'
+    'body': """
+      ["${1:Event name}", ${2:Target(s)}, [${3:Parameters}]] call EFUNC(common,targetEvent);
+    """
+    'description': 'Raise a Global ACE3 Eventhandler'
+    'descriptionMoreURL': 'http://ace3mod.com/wiki/development/ace3-events-system.html'
+

 # Config
 '.source.cpp, .source.hpp':


Remove unnecessary sentence in the UsStatus directive

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -556,7 +556,6 @@
     link = ($scope, $el, $attrs) ->
         $ctrl = $el.controller()
         us = $scope.$eval($attrs.tgUsStatus)
-        $el.find(".us-status").text(us.status)

         taiga.bindOnce $scope, "project", (project) ->
             $el.append(selectionTemplate({ 'statuses':  project.us_statuses }))


save table description event, send it along only after table has been materialized

--- src/tex-writer-mkts-table.coffee
+++ src/tex-writer-mkts-table.coffee
@@ -80,17 +80,17 @@
 #-----------------------------------------------------------------------------------------------------------
 @$collect_field_contents = ( S ) ->
   ### TAINT should allow to name tables in description and content tags ###
-  prv_description               = null
+  prv_description_event         = null
   within_table_content          = false
   within_field                  = false
   fieldhints_and_content_events = []
   current_field                 = null
   #.........................................................................................................
   return $ ( event, send ) =>
+    ### When a table description comes along, we save it for later and do not send anything: ###
     if select event, '.', 'MKTS/TABLE/description'
-      [ type, name, description, meta, ]  = event
-      prv_description                     = description
-      return send stamp event
+      prv_description_event = event
+      return null
     #.......................................................................................................
     if select event, '(', 'mkts-table-content'
       within_table_content = true
@@ -98,7 +98,7 @@
     #.......................................................................................................
     if select event, ')', 'mkts-table-content'
       within_table_content = false
-      send sub_event for sub_event from MKTS_TABLE._walk_events prv_description, fieldhints_and_content_events
+      description           = prv_description_event[ 2 ]
       return send stamp event
     #.......................................................................................................
     if within_table_content


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -45,7 +45,8 @@
   ###
   bind: (events, callback) ->
     events.split(" ").each (namespacedEvent) ->
-      [event, namespaces] = namespacedEvent.split(".")
+      [event, namespaces...] = namespacedEvent.split(".")
+
       eventCallbacks[event] = eventCallbacks[event] || []

       eventCallbacks[event].push(callback)


Make metadata option work

Make metadata option work

--- src/biscotto.coffee
+++ src/biscotto.coffee
@@ -163,6 +163,7 @@
             describe  : 'Show internal methods'
           )
           .options('metadata',
+            boolean   : true
             describe  : 'The path to the top-level npm module directory'
           )
           .options('stability',
@@ -245,7 +246,7 @@
                     console.log "Cannot parse file #{ filename }: #{ error.message }"

           if options.metadata
-            generateMetadata(package_json_path, parser, options)
+            @generateMetadata(package_json_path, parser, options)

           generator = new Generator(parser, options)
           generator.generate(file_generator_cb)


update GUI version

update GUI version

--- app/js/controllers/unlockwallet.coffee
+++ app/js/controllers/unlockwallet.coffee
@@ -23,7 +23,7 @@
       if (Info.info.client_version)
         cancel()
         $scope.client_version = Info.info.client_version
-        if $scope.client_version != "0.4.13"
+        if $scope.client_version != "0.4.14"
             $scope.update_available = true


webapp/course: missing ErrorDisplay import in handouts_panel and hence this crashes on error

--- src/smc-webapp/course/handouts_panel.cjsx
+++ src/smc-webapp/course/handouts_panel.cjsx
@@ -11,7 +11,7 @@
 course_funcs = require('./course_funcs')
 styles = require('./styles')
 {BigTime, FoldersToolbar} = require('./common')
-{Icon, Tip, SearchInput, MarkdownInput} = require('../r_misc')
+{ErrorDisplay, Icon, Tip, SearchInput, MarkdownInput} = require('../r_misc')

 # Could be merged with steps system of assignments.
 # Probably not a good idea mixing the two.
@@ -574,4 +574,4 @@
                     </Col>
                 </Row>
             </Col>
-        </Row>+        </Row>


test(base-service): add tests for deleting by key and for existing resource id checking when deletin

--- src/spec/client/services/base.spec.coffee
+++ src/spec/client/services/base.spec.coffee
@@ -761,6 +761,25 @@
             expect(=> @service.delete()).toThrow new Error "Version is required for deleting a resource (endpoint: #{@service._currentEndpoint})"
             expect(@restMock.DELETE).not.toHaveBeenCalled()

+          if not _.contains(o.blacklist, 'byKey')
+
+            it 'should send request for current endpoint with Key', ->
+              spyOn(@restMock, 'DELETE')
+              @service.byKey(KEY).delete(1)
+              expect(@restMock.DELETE).toHaveBeenCalledWith "#{o.path}/key=#{KEY}?version=1", jasmine.any(Function)
+
+            it 'should throw error if id and key is missing', ->
+              spyOn(@restMock, 'DELETE')
+              expect(=> @service.delete(1)).toThrow new Error "Missing resource id. You can set it by chaining '.byId(ID)' or '.byKey(KEY)'"
+              expect(@restMock.DELETE).not.toHaveBeenCalled()
+
+          else
+
+            it 'should throw error if id is missing', ->
+              spyOn(@restMock, 'DELETE')
+              expect(=> @service.delete(1)).toThrow new Error "Missing resource id. You can set it by chaining '.byId(ID)'"
+              expect(@restMock.DELETE).not.toHaveBeenCalled()
+
           it 'should return promise on delete', ->
             promise = @service.byId('123-abc').delete(1)
             expect(promise.isPending()).toBe true


allow for lower-casing column names

allow for lower-casing column names

--- src/token/colmetadata-token-parser.coffee
+++ src/token/colmetadata-token-parser.coffee
@@ -21,6 +21,10 @@

     colName = buffer.readBVarchar()

+    if options.lowerCaseColumns
+      colName = colName.replace /^[A-Z]/, (s) -> s.toLowerCase()
+
+
     column =
       userType: metadata.userType
       flags: metadata.flags


Pass rowIndex to mapMoney.

Pass rowIndex to mapMoney.

--- src/coffee/mapping.coffee
+++ src/coffee/mapping.coffee
@@ -147,7 +147,7 @@
       when CONS.ATTRIBUTE_TYPE_SET then @mapSetAttribute rawVariant[@header.toIndex attribute.name]
       when CONS.ATTRIBUTE_TYPE_LTEXT then @mapLocalizedAttrib rawVariant, attribute.name, languageHeader2Index
       when CONS.ATTRIBUTE_TYPE_NUMBER then @mapNumber rawVariant[@header.toIndex attribute.name], attribute.name, rowIndex
-      when CONS.ATTRIBUTE_TYPE_MONEY then @mapMoney rawVariant[@header.toIndex attribute.name], attribute.name
+      when CONS.ATTRIBUTE_TYPE_MONEY then @mapMoney rawVariant[@header.toIndex attribute.name], attribute.name, rowIndex
       else rawVariant[@header.toIndex attribute.name] # works for text, enum and lenum

   # TODO: support set of ltext attributes!


Allow to reference by Id during import. Refs #20.

--- src/coffee/matcher.coffee
+++ src/coffee/matcher.coffee
@@ -57,14 +57,18 @@
   getIdFromCache: (parent) ->
     if @parentBy is CONS.HEADER_SLUG
       @slug2IdMap[parent.id]
+    if @parentBy is CONS.HEADER_EXTERNAL_ID
+      @externalId2IdMap[parent.id]
     else
-      @externalId2IdMap[parent.id]
+      parent.id

   fetchRef: (parent) ->
     if @parentBy is CONS.HEADER_SLUG
       @apiClient.getBySlugs [parent.id], @language
+    if @parentBy is CONS.HEADER_EXTERNAL_ID
+      @apiClient.getByExternalIds [parent.id]
     else
-      @apiClient.getByExternalIds [parent.id]
+      @apiClient.byId parent.Id

   match: (category) ->
     new Promise (resolve, reject) =>


Make the initial bind of IteratorBinding nextTick'd.

--- src/view/bindings/iterator_binding.coffee
+++ src/view/bindings/iterator_binding.coffee
@@ -21,7 +21,9 @@
     parentNode = @prototypeNode.parentNode
     parentNode.insertBefore(@backingView.get('node'), @prototypeNode)
     parentNode.removeChild(@prototypeNode)
-    @bind()
+    setTimeout =>
+      @bind()
+    , 0

   dataChange: (collection) ->
     if collection?


Enable emmet in JSX

Enable emmet in JSX

--- .atom/keymap.cson
+++ .atom/keymap.cson
@@ -25,6 +25,24 @@
 # https://github.com/bevry/cson#what-is-cson
 'atom-text-editor[data-grammar~="lisp"]':
     'tab': 'lisp-paredit:indent'
+
+# Stop emmet from hijacking tab from snippets and autocomplete
+'atom-text-editor.autocomplete-active:not([mini])':
+  'tab': 'autocomplete-plus:confirm'
+
+# Stop emmet from hijacking tab from snippet tab stops
+'atom-text-editor[data-grammar="text html basic"]:not([mini]),
+ atom-text-editor[data-grammar~="jade"]:not([mini]),
+ atom-text-editor[data-grammar~="css"]:not([mini]),
+ atom-text-editor[data-grammar~="sass"]:not([mini])':
+  'tab': 'snippets:next-tab-stop'
+
+# Emmet for JSX
+'atom-text-editor[data-grammar~="jsx"]:not([mini])':
+  'tab': 'emmet:expand-abbreviation-with-tab'
+
+'atom-text-editor[data-grammar~="js"]:not([mini])':
+  'tab': 'emmet:expand-abbreviation-with-tab'

 # Allows using enter to select an autocomplete suggestion.
 '.platform-darwin atom-text-editor[data-grammar~="clojure"].autocomplete-active':


Pull out Die constructor for comparisons.

--- src/cli.coffee
+++ src/cli.coffee
@@ -11,7 +11,7 @@
     mod = false

   # Try to require Die app
-  Die = require './die'
+  {Die} = require('./die')
   if mod
     app = require mod
     # If we actually have a Die app instance, use it


Pre beta release

Pre beta release

--- src/cli.coffee
+++ src/cli.coffee
@@ -2,7 +2,7 @@
 pkg = require '../package.json'
 { log } = require './common'

-[ 'auth', 'package' ]
+[ 'create' ]
   .map (file) -> './command/' + file
   .forEach require


Fixed the goals view, which wasn't being properly shown.

--- app/views/play/level/goals_view.coffee
+++ app/views/play/level/goals_view.coffee
@@ -46,11 +46,11 @@
       li.prepend($('<i></i>').addClass(stateIconMap[state.status]))
       list.append(li)
       goals.push goal
-    @$el.toggle goals.length > 0
+    @$el.removeClass('secret') if goals.length > 0

   render: ->
     super()
-    @$el.addClass('hide').addClass('expanded')
+    @$el.addClass('secret').addClass('expanded')

   onSetLetterbox: (e) ->
     @$el.toggle not e.on