Messenger class updated

Messenger class updated

--- src/models/messenger.coffee
+++ src/models/messenger.coffee
@@ -3,8 +3,51 @@
 Connecter = require('./connecter')
 {Clients} = require('clients')

-class Messenger
+class Messenger extends Backbone.Model
+    defaults:
+        id: null
+        url: null
+
     constructor: ->
+        @connecter = new Connecter()
+        @clients   = new Clients()
+        @clients.messenger = @
+
+        @listenTo @connecter, 'ready',        @onReady
+        @listenTo @connecter, 'ready:client', @addClient
+        @listenTo @connecter, 'message',      @onMessage
+
+    start: (id, url)->
+        @set
+            id:  if id then id
+            url: if url then url
+
+        @connecter.start url, id
+
+    connect: (client_id, callback)->
+        @connecter.hello client_id, callback
+
+    send: (client_id, data, callback)->
+        @connecter.send client_id, data, callback
+
+    onReady: (id)->
+        @set 'id', id
+
+    addClient: (client_id)->
+        @clients.add id: client_id, {merge: false}
+
+    onMessage: (client_id, data)->
+        try
+            message = JSON.parse data
+
+        catch
+            return
+
+        if client = @clients.get client_id
+            switch message.type
+                when 'message'   then client.addMessage message
+                when 'status'    then client.setMessageStatus message
+                when 'heartbeat' then client.heartbeat message

 module.exports = Messenger


Throw an error on malformed slackapp lines.

--- src/parsers/slackapp.coffee
+++ src/parsers/slackapp.coffee
@@ -36,5 +36,8 @@
       ts = parseTs m[1]
       continue

+    unless nick? and ts?
+      throw new Error("Expected nick and timestamp line first.")
+
     result.push "#{nick} [#{ts}] #{line}"
   result


Consider folds when testing the rightmost position

--- spec/display-layer-spec.coffee
+++ spec/display-layer-spec.coffee
@@ -745,8 +745,10 @@
   longestScreenRows = new Set
   for screenLine, row in screenLines
     bufferRow = displayLayer.translateScreenPosition({row: row, column: 0}).row
-
-    if displayLayer.buffer.lineForRow(bufferRow).length is 0
+    bufferLine = displayLayer.buffer.lineForRow(bufferRow)
+    hasNoFoldMarker = screenLine.indexOf("⋯") is -1
+
+    if hasNoFoldMarker and bufferLine.length is 0
       screenLineLength = 0
     else
       screenLineLength = screenLine.length


disable input

disable input

--- resources/assets/coffee/react/beatmaps/search-panel.coffee
+++ resources/assets/coffee/react/beatmaps/search-panel.coffee
@@ -60,6 +60,7 @@
       div className: 'fancy-search fancy-search--beatmapsets js-user-link',
         input
           className: 'fancy-search__input'
+          disabled: true
           type: 'textbox'
           placeholder: osu.trans('beatmaps.listing.search.login_required')
         div className: 'fancy-search__icon',


skip the whole element if null; layout looks the same

--- resources/assets/lib/beatmap-discussions/discussion.coffee
+++ resources/assets/lib/beatmap-discussions/discussion.coffee
@@ -289,8 +289,9 @@
                 className: 'far fa-check-circle'
                 title: trans 'beatmaps.discussions.resolved'

-        div className: "#{tbn}__text",
-          formatTimestamp @props.discussion.timestamp if @props.discussion.timestamp?
+        if @props.discussion.timestamp?
+          div className: "#{tbn}__text",
+            formatTimestamp @props.discussion.timestamp

   toggleCollapse: =>


Moved lines.

Moved lines.

--- app/assets/javascripts/counts_behavior.js.coffee
+++ app/assets/javascripts/counts_behavior.js.coffee
@@ -101,11 +101,11 @@

     if !@isRead(entry.id)
       $.post $(container).data('mark-as-read-path')
+      feedbin.Counts.get().removeEntry(entry.id, entry.feed_id, 'unread')
+      @mark('read')
       feedbin.recentlyReadTimer = setTimeout ( ->
         $.post $(container).data('recently-read-path')
       ), 10000
-      feedbin.Counts.get().removeEntry(entry.id, entry.feed_id, 'unread')
-      @mark('read')

   isRead: (entryId) ->
     feedbin.Counts.get().isRead(entryId)


Allow to cancel jobs after they're started

--- assets/scripts/app/models/job.coffee
+++ assets/scripts/app/models/job.coffee
@@ -73,7 +73,7 @@
   ).property('config', 'build.rawConfigKeys.length')

   canCancel: (->
-    @get('state') == 'created'
+    !@get('isFinished')
   ).property('state')

   cancel: (->


Add specs for searching packages

Add specs for searching packages

--- spec/install-panel-spec.coffee
+++ spec/install-panel-spec.coffee
@@ -56,6 +56,31 @@
       expect(@panel.searchType).toBe 'themes'
       expect(@panel.search.callCount).toBe 3

+  describe "searching packages", ->
+    it "highlights exact name matches", ->
+      spyOn(@packageManager, 'search').andCallFake () ->
+        new Promise (resolve, reject) -> resolve([ {name:'not-first'}, {name:'first'} ])
+      spyOn(@panel, 'getPackageCardView').andCallThrough()
+
+      waitsForPromise =>
+        @panel.search('first')
+
+      runs ->
+        expect(@panel.getPackageCardView.argsForCall[0][0].name).toEqual 'first'
+        expect(@panel.getPackageCardView.argsForCall[1][0].name).toEqual 'not-first'
+
+    it "prioritizes partial name matches", ->
+      spyOn(@packageManager, 'search').andCallFake () ->
+        new Promise (resolve, reject) -> resolve([ {name:'something else'}, {name:'partial name match'} ])
+      spyOn(@panel, 'getPackageCardView').andCallThrough()
+
+      waitsForPromise =>
+        @panel.search('match')
+
+      runs ->
+        expect(@panel.getPackageCardView.argsForCall[0][0].name).toEqual 'partial name match'
+        expect(@panel.getPackageCardView.argsForCall[1][0].name).toEqual 'something else'
+
   describe "searching git packages", ->
     beforeEach ->
       spyOn(@panel, 'showGitInstallPackageCard').andCallThrough()


Undo unrelated change

Undo unrelated change

--- resources/assets/coffee/react/beatmaps/search-panel.coffee
+++ resources/assets/coffee/react/beatmaps/search-panel.coffee
@@ -56,7 +56,7 @@
       div
         className: 'osu-page-header__background'
         style:
-          backgroundImage: @props.background ? "url(#{@props.background})" : ""
+          backgroundImage: "url(#{@props.background})"
       div className: 'fancy-search fancy-search--beatmapsets js-user-link',
         input
           className: 'fancy-search__input'
@@ -75,7 +75,7 @@
       div
         className: 'beatmapsets-search__background'
         style:
-          backgroundImage: @props.background ? "url(#{@props.background})" : ""
+          backgroundImage: "url(#{@props.background})"
       div className: 'fancy-search fancy-search--beatmapsets',
         input
           className: 'fancy-search__input js-beatmapsets-search-input'


do end matching strictness increased (fix #38)

--- lib/alchemide/doendmatcher.coffee
+++ lib/alchemide/doendmatcher.coffee
@@ -1,7 +1,7 @@
 DO = "do"
 END = "end"
 FN = "fn"
-DOEND = /(do|end|fn)/g
+DOEND = /(\Wdo\W|\Wend\W|\Wfn\W)/g
 {Range, Point} = require("atom")
 decorations = []


Create IsUp collection

Create IsUp collection

--- src/IsUp.coffee
+++ src/IsUp.coffee
@@ -1 +1,6 @@
-// Write your package code here!
+log = new ObjectLogger("practical.isup.IsUp")
+@IsUp = new Mongo.Collection("isup")
+
+# Populate
+if not IsUp.findOne({})
+  IsUp.insert({status: "yes"})


FIX: pause() method. Working properlly

FIX: pause() method. Working properlly

--- src/Moon.coffee
+++ src/Moon.coffee
@@ -19,6 +19,7 @@
         return this

     Moon.fn = Moon.prototype =
+
         # config object
         config:
             duration: 1000
@@ -29,9 +30,10 @@

         # already returned prefixes
         _prefixes: {}
+        _cssPrefixes: {}

         # default vendor prefixes
-        _vendorPrefixes = ["webkit-", "moz-", "ms-", "O-"]
+        _vendorPrefixes: ["webkit-", "moz-", "ms-", "O-"]

         # returns the collection of HTMLCollection or NodeList, that can be animated by Moon later
         _getMoonCollection: (target) ->
@@ -44,10 +46,12 @@
                 if tgt instanceof NodeList || tgt instanceof HTMLCollection
                     for el in tgt
                         collection.push(el)
+
                 else if typeof tgt == "string"
                     selectedElements = document.querySelectorAll(tgt)
                     for el in selectedElements
                         collection.push(el)
+
                 else if !(tgt instanceof Array)
                     collection.push(tgt)

@@ -75,7 +79,7 @@

             # if not, try to find the prefix
             for pre in Moon.fn._vendorPrefixes
-                property = Moon.fn._camelize(pre + propCap)
+                property = Moon.fn._camelize(pre + prop)
                 if document.documentElement.style[property]?
                     Moon.fn._prefixes[prop] = property
                     return property
@@ -83,25 +87,45 @@
         # capitalizes the first letter
         # this implementation is done before deep optimization research (#2)
         _camelize: (str) ->
-            return str.replace(Moon.fn._rdashAlpha, Moon.fn._camelizeReplaceCallback)
-
-        # RegExp to match the dash and the char after it
-        _rdashAlpha: "/-([\D])/g"
+            return str.replace(/-([\D])/g, Moon.fn._camelizeReplaceCallback)

         # _camelize replace callback function to return
         # just the capitalized char
         _camelizeReplaceCallback: ($1) ->
             return $1.charAt(1).toUpperCase()

+        # returns the prefix of a css style in "javascript" style. Used mainly for css3
+        _getCssPrefix: (prop) ->
+
+            # check if Moon already tried to get this prefix
+            if Moon.fn._cssPrefixes[prop]?
+                return Moon.fn._cssPrefixes[prop]
+
+            # camelCase properties to compare with style
+            propCam = Moon.fn._camelize(prop)
+
+            # is the property avaiable without prefix?
+            if document.documentElement.style[propCam]?
+                Moon.fn._cssPrefixes[prop] = prop
+                return prop
+
+            # if not, try to find the prefix
+            for pre in Moon.fn._vendorPrefixes
+                property = "-" + pre + prop
+                propertyCam = Moon.fn._camelize(pre + prop)
+                if document.documentElement.style[propertyCam]?
+                    Moon.fn._cssPrefixes[prop] = property
+                    return property
+
         # defines an animation step. Moon animations
         # must have at least one step
         animate: (args) ->
             animationProps =
-                duration: Moon.config.duration
-                delay: Moon.config.delay
-                easing: Moon.config.easing
-                before: Moon.config.before
-                after: Moon.config.after
+                duration: Moon.fn.config.duration
+                delay: Moon.fn.config.delay
+                easing: Moon.fn.config.easing
+                before: Moon.fn.config.before
+                after: Moon.fn.config.after

             for arg, value of args
                 animationProps[arg] = value
@@ -111,20 +135,52 @@

         # play the animation and sets the end callback
         play: (callback) ->
-            this._callback = callback
+            this._callback = callback || this._callback
             this._play()
             return this

         # play function for intern use
         _play: ->
-            this._step += 1
-            if this._direction
-                step = this._step
+
+            # return the animations properties for a step
+            getAnm = (step) =>
+                # direction can change with loop("alternate")
+                if !this._direction
+                    step = this._stack.length - 1 - this._step
+
+                # animation step
+                return this._stack[step]
+
+            anm = undefined
+            # if is paused, we need to recaulate delay and duration
+            if this._paused?
+                anm = getAnm(this._step)
+
+                timeDiff = this._paused - this._lastTimePlayed
+                totalTime = anm.delay + anm.duration
+
+                auxDelay = totalTime - timeDiff - anm.duration
+                auxDuration = totalTime - timeDiff
+
+                console.log (auxDelay + " | " + auxDuration)
+
+                if auxDelay < 0
+                    anm.delay = 0
+                else
+                    anm.delay = auxDelay
+
+                if auxDuration < anm.duration
+                    anm.duration = auxDuration
+
+                console.log anm.delay + " | " + anm.duration
+
+                this._paused = null
+                this._isResuming = true
+
             else
-                step = this._stack.length - 1 - this._step
-
+                this._step += 1
+                anm = getAnm(this._step)

-            anm = this._stack[step]
             if anm?
                 # before animation function
                 anm.before() if typeof anm.before == "function"
@@ -138,8 +194,15 @@
                             continue
                         el.style[this._getPrefix(key)] = value

-
+                # timer to continue animation
                 nextTimeout = setTimeout =>
+
+                    # if it is paused, clear and stop
+                    if this._isResuming?
+                        clearTimeout(nextTimeout)
+                        this._isResuming = null
+                        return undefined
+
                     # after animation function
                     anm.after() if typeof anm.after == "function"

@@ -149,6 +212,7 @@
                     clearTimeout(nextTimeout)
                 , anm.delay + anm.duration

+                this._lastTimePlayed = new Date()
             # no more stacked animations
             else
                 this._step = -1
@@ -181,12 +245,15 @@
                     el.style[this._getPrefix(key)] = value

         pause: ->
+            this._paused = new Date()
             for el in this._collection
-                el.style[this._getPrefix("transition")] = null
+                computedStyle = window.getComputedStyle(el)
                 for key, prop of this._stack[this._step]
                     if key == "duration" || key == "delay" || key == "easing" || key == "before" || key == "after"
                         continue
-                    el.style[this._getPrefix(key)] = window.getComputedStyle(el, null).getPropertyValue(key)
+                    el.style[this._getPrefix(key)] = computedStyle.getPropertyValue(this._getCssPrefix(key))
+
+                el.style[this._getPrefix("transition")] = ""

         # set loop
         loop: (looping) ->


let's try just the text

let's try just the text

--- src/slack_connection.coffee
+++ src/slack_connection.coffee
@@ -2,10 +2,8 @@

 class SlackConnection
   constructor: (@token) ->
-    console.log "Yaya"
     @client = new SlackClient @token, true, true

-    console.log "Yaya"
     @client.on 'error', @.error
     @client.on 'loggedIn', @.loggedIn
     @client.on 'open', @.open
@@ -13,7 +11,6 @@
     @client.on 'message', @.message
     @client.on 'userChange', @.userChange

-    console.log "Yaya"
     @client.login()

   error: (error) =>
@@ -38,7 +35,7 @@
   message: (msg) =>
     # Ignore our own messages
     return if msg.user == @self.id
-    console.log msg
+    console.log msg.text

   userChange: (user) =>


Adding an avatar square

Adding an avatar square

--- public/app/game.coffee
+++ public/app/game.coffee
@@ -3,7 +3,7 @@
         constructor: (@screen) ->
             $e.Ticker.addListener @
             @blocks = {}
-            @player = {x: 0, y: 0}
+            @player = {x: 0, y: 0, avatar: null}
             @camera = {x: 0, y: 0}

         ready: () =>
@@ -11,6 +11,8 @@

         load: (cb) ->
             @once "ready", cb if cb
+
+            @init_player()

             # TODO: Compute block index from position
             block = {x: 0, y: 0}
@@ -22,9 +24,22 @@
                 new Block data, (b) =>
                     @blocks[block.x] ?= {}
                     @blocks[block.x][block.y] = b
-                    @screen.stage.addChild b.container
+                    @screen.stage.addChildAt b.container, @screen.stage.getChildIndex(@player.avatar)
             .error =>
                 console.log "Failed to fetch block."
+
+            @ready()
+
+        init_player: () =>
+            g = new $e.Graphics()
+            g.beginStroke("#F00")
+             .beginFill("#0F0")
+             .drawRect(@player.x, -@player.y, 32, 32)
+
+            @player.avatar = new $e.Shape(g)
+            @player.avatar.regX = @player.avatar.regY = 16
+
+            @screen.stage.addChild @player.avatar

         tick: (elapsed, paused) =>
             window.elapsed = elapsed


Init cleanup

Init cleanup

--- public/app/game.coffee
+++ public/app/game.coffee
@@ -1,19 +1,25 @@
 define ['jquery', 'easel', 'EventEmitter', 'cs!block', 'cs!input', 'cs!entity', 'cs!player'], ($, $e, EventEmitter, Block, Input, Entity, Player) ->
     class Game extends EventEmitter
         constructor: (@screen, cb=(game, error)=>) ->
-            ## Store the game globally
-            window.$game = @
-
             ## Slots
             @blocks = {}
             @entities = []
             @camera = {x: 0, y: 0, vx: 0, vy: 0}
             @input = new Input()

-            # Handler binds
-            @on "error", (e) => cb(@, e)
-            @once "ready:blocks", => cb(@)
+            ## Store the game globally
+            window.$game = @
+            $e.Ticker.addListener @

+            # Handler binds for inital load/fail
+            do (on_error = (e) => cb(@, e)) =>
+                @once "error", on_error
+                @once "ready:blocks", =>
+                    @removeListener "error", on_error
+                    cb @
+
+            # Handle block streaming and signal when all
+            # requested blocks are ready
             @on "ready:block", (b) =>
                 complete = true
                 for own x of @blocks
@@ -21,12 +27,12 @@
                         do (block = @blocks[x][y]) => complete &= block.complete
                 @emit "ready:blocks" if complete

-            ## Bind the ticker
-            $e.Ticker.addListener @
-
-            ## Init secondary items
+            ## Init
+            @init_player()
             @init_stats()

+        ## Subinits
+        init_player: () =>
             ## Fetch a new player
             $.getJSON '/player/new/', (data) =>
                 console.log "Initial data:", data
@@ -34,7 +40,6 @@
                 @add_block new Block @player.pos.x, @player.pos.y
             .error (xhr, txt, e) => cb(@, e)

-        ## Init other things
         init_stats: () =>
             setInterval (=>
                 $(".camera.x").text @camera.x


send messages to clients with concurrency of 2

--- services/real-time/app/coffee/WebsocketLoadBalancer.coffee
+++ services/real-time/app/coffee/WebsocketLoadBalancer.coffee
@@ -91,7 +91,8 @@
 				}, "distributing event to clients"
 				seen = {}
 				# Send the messages to clients async, don't wait for them all to finish
-				Async.eachSeries clientList
+				Async.eachLimit clientList
+					, 2
 					, (client, cb) ->
 						Utils.getClientAttributes client, ['is_restricted_user'], (err, {is_restricted_user}) ->
 							return cb(err) if err?


Removed unnecessary comments

Removed unnecessary comments

--- src/tb/lib/ti/extend.coffee
+++ src/tb/lib/ti/extend.coffee
@@ -12,26 +12,3 @@
 elementCollection:: = fn

 module.exports = (element) -> elementCollection [element]
-
-  # return dom
-
-  # out = new Z(element)
-
-  # console.warn out.unbind?
-
-  # out
-
-  # collection = [ element ]
-
-  # _.extend collection, fn
-
-  # collection
-
-# module.exports = class Wrapper
-
-#   constructor: (element) ->
-
-#     @collection = [ element ]
-
-#     @[] = (index) ->
-#       @collection[index]


added blog url to config

added blog url to config

--- services/web/config/settings.defaults.coffee
+++ services/web/config/settings.defaults.coffee
@@ -122,6 +122,7 @@
 			url: "http://#{process.env['CHAT_HOST'] or 'localhost'}:3010"
 			internal_url: "http://#{process.env['CHAT_HOST'] or 'localhost'}:3010"
 		blog:
+			url: "http://localhost:3008"
 			port: 3008
 		university:
 			url: "http://localhost:3011"


Uncomment test that was previously not passing

Relates to 4ad082a47c39b62beb330df9cc794c6517937a84

--- test/node_http_client.spec.coffee
+++ test/node_http_client.spec.coffee
@@ -37,4 +37,4 @@
         runs ->
           expect(error.code).toEqual 'TimeoutError'
           expect(error.message).toEqual 'Connection timed out after 1ms'
-          #expect(numCalls).toEqual 1
+          expect(numCalls).toEqual 1


test app name, removed obsolete port from config.location

--- testApp/config/application.coffee
+++ testApp/config/application.coffee
@@ -1,7 +1,7 @@
 # -- Required settings --

-steroids.config.name = "Hello World"
-steroids.config.location = "http://localhost:13101/views/steroids/index.html"
+steroids.config.name = "Steroids.js test app"
+steroids.config.location = "http://localhost/views/steroids/index.html"

 # -- Tabs --


Fix for when an app has no displayName

Fix for when an app has no displayName

--- client/app/collections/application.coffee
+++ client/app/collections/application.coffee
@@ -30,5 +30,7 @@
         else if rightIsOfficial and not leftIsOfficial
             1
         else
-            modelLeft.get('displayName').localeCompare modelRight.get 'displayName'
+            left = modelLeft.get('displayName') or modelLeft.get('name')
+            right = modelRight.get('displayName') or modelRight.get('name')
+            left.localeCompare right


Admin: Integrations: Fix typo.

Admin: Integrations: Fix typo.

--- client/app/lib/helpers/integration.coffee
+++ client/app/lib/helpers/integration.coffee
@@ -157,7 +157,7 @@

       description     = description or integration.summary
       webhookUrl      = "#{globals.config.integration.url}/#{integration.name}/#{token}"
-      integrationType = 'confiured'
+      integrationType = 'configured'
       selectedEvents  = []
       data            = { channels, id, integration, token, createdAt,
                           updatedAt, description, integrationId, webhookUrl, isDisabled }


Passes CoffeeLint.

Passes CoffeeLint.

--- client/coffeescript/templates/post.coffee
+++ client/coffeescript/templates/post.coffee
@@ -8,7 +8,7 @@
   link_href: ->
     return "#{ @_id }"
   parent_href: ->
-    if @parent_id?
+    if @parent_id?
       return "#{ @parent_id }"
     else
       return false


ChatSettingsPane: Hide End Session button by default.

--- client/ide/views/chat/settingspane.coffee
+++ client/ide/views/chat/settingspane.coffee
@@ -36,7 +36,7 @@
     @endSession = new KDButtonView
       title    : 'END SESSION'
       disabled : yes
-      cssClass : 'solid red'
+      cssClass : 'solid red hidden'
       callback : @bound 'stopSession'

     @defaultSetting = new KDSelectBox


Fix issue with marker_rail resizing

Fix issue with marker_rail resizing

--- app/assets/javascripts/avalon_player.js.coffee
+++ app/assets/javascripts/avalon_player.js.coffee
@@ -110,11 +110,11 @@
               currentPlayer.setCurrentTime offset
             marker_rail.append(marker)
             marker_rail.append(''+title+'')
-            marker_rail.width(total.width())
           scrubber.append(marker_rail)
           _this.player.globalBind('resize', (e) ->
             marker_rail.width(total.width())
           )
+          marker_rail.width(total.width())
           $('.scrubber-marker').bind('mouseenter', (e) ->
             $('.mejs-time-float-marker[data-marker="'+this.dataset.marker+'"]').show()
           ).bind 'mouseleave', (e) ->


Remove old bottom-link code

Remove old bottom-link code

--- app/assets/javascripts/redcursor_app.js.coffee
+++ app/assets/javascripts/redcursor_app.js.coffee
@@ -107,8 +107,6 @@
     $(".more").toggleClass("show")

   $(document).on 'scroll', ->
-    $(".bottom-link").css("top", "#{document.body.scrollTop + 6}px")
-    $(".bottom-link").toggleClass("top", document.body.scrollTop < 32)
     if document.body.scrollTop < document.body.scrollHeight - document.body.clientHeight
       $(".bottom-link .octicon").removeClass("octicon-arrow-up").addClass("octicon-arrow-down")
     else
@@ -121,12 +119,6 @@
       window.scrollTo 0,document.body.scrollHeight
     return false

-  $(".bottom-link").addClass("top")
-
-  $(".top-link").click ->
-    window.scrollTo 0,0
-    return false
-
   $(".edit-channel-link").click ->
     $(".channel-text form").show()
     $(".channel-text .text-body").hide()


Fix scroll down behaviour

Fix scroll down behaviour

--- app/assets/javascripts/scroll-loader.js.coffee
+++ app/assets/javascripts/scroll-loader.js.coffee
@@ -16,7 +16,7 @@
           if !@hidden
             @loadMore()
             @hidden = true
-        else if t >= document.body.scrollHeight - document.body.clientHeight - 100
+        else if t <= document.body.scrollHeight - document.body.clientHeight - 100
           @hidden = false


Remove filters from removeItem

Remove filters from removeItem

--- modules/player/hud.coffee
+++ modules/player/hud.coffee
@@ -118,8 +118,6 @@
       reply.footer =
         icon_url: removedBy.staticAvatarURL
         text: "Removed by #{removedBy.name}"
-    if @util.displayFilters(item.filters)
-      reply.description += "\n**Filters**: #{@util.displayFilters(item.filters)}"
     reply

   addPlaylist: (user, playlist, channel)=>


fix test case

fix test case

--- apps/artwork/components/auction/test/view.coffee
+++ apps/artwork/components/auction/test/view.coffee
@@ -113,11 +113,11 @@
           .should.have.lengthOf 0

     describe 'reserve not met', ->
-      it 'gives a reserve error message', ->
+      it 'gives a reserve not met bid message', ->
         data = Object.assign({}, @data)
         data.accounting = accounting
         data.artwork.sale_artwork.reserve_message = 'Reserve not met'
-        # just user + me this in to meet the template conditions
+        # just shoving user + me into data to meet the template conditions for rendering
         data.user = true
         data.me =
           lot_standing:
@@ -128,7 +128,7 @@
         view = new @ArtworkAuctionView data: data
         view.render()
         view.$('.is_losing').text()
-          .should.equal 'foo'
+          .should.equal 'Reserve not met, increase your bid'

     describe 'preview auction', ->
       it 'renders correctly', ->


Adding a test for #2613

Adding a test for #2613

--- test/assignment.coffee
+++ test/assignment.coffee
@@ -293,10 +293,10 @@
   doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ?= 10"),  "allow (x = null; do -> x ?= 10)"
   doesNotThrow (-> CoffeeScript.compile "x = null; do -> x ||= 10"), "allow (x = null; do -> x ||= 10)"
   doesNotThrow (-> CoffeeScript.compile "x = null; do -> x or= 10"), "allow (x = null; do -> x or= 10)"
-
+
   throws (-> CoffeeScript.compile "-> -> -> x ?= 10"), null, "prohibit (-> -> -> x ?= 10)"
   doesNotThrow (-> CoffeeScript.compile "x = null; -> -> -> x ?= 10"), "allow (x = null; -> -> -> x ?= 10)"
-
+
 test "more existential assignment", ->
   global.temp ?= 0
   eq global.temp, 0
@@ -311,7 +311,7 @@
   eq nonce, b
   #the first ?= compiles into a statement; the second ?= compiles to a ternary expression
   eq a ?= b ?= 1, nonce
-
+
   if a then a ?= 2 else a = 3
   eq a, nonce

@@ -375,3 +375,8 @@
   !
   "
   """
+
+test "#2613: parens on LHS of destructuring", ->
+  a = {}
+  [(a).b] = [1, 2, 3]
+  eq a.b, 1


tidied up

tidied up

--- assets/coffee/helper/radian-module-helper.coffee
+++ assets/coffee/helper/radian-module-helper.coffee
@@ -22,8 +22,7 @@
       helper.inject module, deps

       # Register the module.
-      app = A.module cfg.ngApp
-      app[type] name, module
+      (A.module cfg.ngApp)[type] name, module

     uppercaseFirstLetter: (str) ->
       str.charAt(0).toUpperCase() + str.slice 1


Rewrite conditions tests

Rewrite conditions tests

--- test/conditions.coffee
+++ test/conditions.coffee
@@ -1,84 +1,61 @@
-Liquid = require("../src/index")
-Promise = require "bluebird"
+describe "Liquid.Condition", ->
+  context "if", ->
+    it "renders on `true` variables", ->
+      renderTest('X', '{% if var %}X{% endif %}', var: true)

-describe "Conditions", ->
-  it "test_if", ->
-    Promise.all([
-      renderTest('  ',' {% if false %} this text should not go into the output {% endif %} ')
-      renderTest('  this text should go into the output  ',
-                             ' {% if true %} this text should go into the output {% endif %} ')
-      renderTest('  you rock ?','{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
-    ])
+    it "doesn't render on `false` variables", ->
+      renderTest('', '{% if var %}X{% endif %}', var: false)

+    it "renders on truthy variables", ->
+      renderTest('X', '{% if var %}X{% endif %}', var: "abc")

-  it "test_if_else", ->
-    Promise.all([
-      renderTest(' YES ','{% if false %} NO {% else %} YES {% endif %}')
-      renderTest(' YES ','{% if true %} YES {% else %} NO {% endif %}')
-      renderTest(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}')
-    ])
+    it "doesn't render on falsy variables", ->
+      renderTest('', '{% if var %}X{% endif %}', var: null)

-  it "test_if_boolean", ->
-    Promise.all([
-      renderTest(' YES ','{% if var %} YES {% endif %}', 'var': true)
-    ])
+    it "renders on truthy object properties", ->
+      renderTest('X', '{% if foo.bar %}X{% endif %}', foo: { bar: "abc" })

-  it "test_if_or", ->
-    Promise.all([
-      renderTest(' YES ','{% if a or b %} YES {% endif %}', 'a': true, 'b': true)
-      renderTest(' YES ','{% if a or b %} YES {% endif %}', 'a': true, 'b': false)
-      renderTest(' YES ','{% if a or b %} YES {% endif %}', 'a': false, 'b': true)
-      renderTest('',     '{% if a or b %} YES {% endif %}', 'a': false, 'b': false)
+    it "doesn't render on falsy object properties", ->
+      renderTest('', '{% if foo.bar %}X{% endif %}', foo: { bar: null })

-      renderTest(' YES ','{% if a or b or c %} YES {% endif %}', 'a': false, 'b': false, 'c': true)
-      renderTest('',     '{% if a or b or c %} YES {% endif %}', 'a': false, 'b': false, 'c': false)
-    ])
+    it "renders on truthy constants", ->
+      renderTest('X','{% if "foo" %}X{% endif %}')

-  it "test_if_or_with_operators", ->
-    Promise.all([
-      renderTest(' YES ','{% if a == true or b == true %} YES {% endif %}', 'a': true, 'b': true)
-      renderTest(' YES ','{% if a == true or b == false %} YES {% endif %}', 'a': true, 'b': true)
-      renderTest('','{% if a == false or b == false %} YES {% endif %}', 'a': true, 'b': true)
-    ])
+    it "doesn't render on falsy constants", ->
+      renderTest('','{% if null %}X{% endif %}', null: 42)

-  it "test_comparison_of_strings_containing_and_or_or", ->
-    awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar"
-    assigns = {'a': 'and', 'b': 'or', 'c': 'foo and bar', 'd': 'bar or baz', 'e': 'foo', 'foo': true, 'bar': true}
-    renderTest(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns)
+    context "with condition", ->
+      it "(true or true) renders", ->
+        renderTest('X','{% if a or b %}X{% endif %}', a: true, b: true)

-  it "test_if_from_variable", ->
-    Promise.all([
-      renderTest('','{% if var %} NO {% endif %}', 'var': false)
-      renderTest('','{% if var %} NO {% endif %}', 'var': null)
-      renderTest('','{% if foo.bar %} NO {% endif %}', 'foo': {'bar': false})
-      renderTest('','{% if foo.bar %} NO {% endif %}', 'foo': {})
-      renderTest('','{% if foo.bar %} NO {% endif %}', 'foo': null)
-      renderTest('','{% if foo.bar %} NO {% endif %}', 'foo': true)
+      it "(true or false) renders", ->
+        renderTest('X','{% if a or b %}X{% endif %}', a: true, b: false)

-      renderTest(' YES ','{% if var %} YES {% endif %}', 'var': "text")
-      renderTest(' YES ','{% if var %} YES {% endif %}', 'var': true)
-      renderTest(' YES ','{% if var %} YES {% endif %}', 'var': 1)
-      renderTest(' YES ','{% if var %} YES {% endif %}', 'var': {})
-      renderTest(' YES ','{% if var %} YES {% endif %}', 'var': [])
-      renderTest(' YES ','{% if "foo" %} YES {% endif %}')
-      renderTest(' YES ','{% if foo.bar %} YES {% endif %}', 'foo': {'bar': true})
-      renderTest(' YES ','{% if foo.bar %} YES {% endif %}', 'foo': {'bar': "text"})
-      renderTest(' YES ','{% if foo.bar %} YES {% endif %}', 'foo': {'bar': 1 })
-      renderTest(' YES ','{% if foo.bar %} YES {% endif %}', 'foo': {'bar': {} })
-      renderTest(' YES ','{% if foo.bar %} YES {% endif %}', 'foo': {'bar': [] })
+      it "(false or true) renders", ->
+        renderTest('X','{% if a or b %}X{% endif %}', a: false, b: true)

-      renderTest(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var': false)
-      renderTest(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var': null)
-      renderTest(' YES ','{% if var %} YES {% else %} NO {% endif %}', 'var': true)
-      renderTest(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}', 'var': "text")
+      it "(true or true) doesn't render", ->
+        renderTest('', '{% if a or b %}X{% endif %}', a: false, b: false)

-      renderTest(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo': {'bar': false})
-      renderTest(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo': {'bar': true})
-      renderTest(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo': {'bar': "text"})
-      renderTest(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo': {'notbar': true})
-      renderTest(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo': {})
-      renderTest(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'notfoo': {'bar': true})
-    ])
+    context "with operators", ->
+      it "that evaluate to true renders", ->
+        renderTest 'X','{% if a == 42 %}X{% endif %}', a: 42
+
+      it "that evaluate to false doesn't render", ->
+        renderTest '','{% if a != 42 %}X{% endif %}', a: 42
+
+    context "with awful markup", ->
+      it "renders correctly", ->
+        awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar"
+        assigns = {'a': 'and', 'b': 'or', 'c': 'foo and bar', 'd': 'bar or baz', 'e': 'foo', 'foo': true, 'bar': true}
+        renderTest(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns)
+
+    context "with else-branch", ->
+      it "renders else-branch on falsy variables", ->
+        renderTest 'ELSE', '{% if var %}IF{% else %}ELSE{% endif %}', var: false
+
+      it "renders if-branch on truthy variables", ->
+        renderTest 'IF', '{% if var %}IF{% else %}ELSE{% endif %}', var: true

   describe "unless", ->
     it "negates 'false'", ->


added tests for yield, yield from, yield return and yield in if statements

--- test/generators.coffee
+++ test/generators.coffee
@@ -7,23 +7,18 @@
   ok -> yield 0

 test "generator definition", ->
-  x = ->
+  x = do ->
     yield 0
     yield 1
     yield 2
-  y = x()
-  z = y.next()
-  eq z.value, 0
-  eq z.done, false
-  z = y.next()
-  eq z.value, 1
-  eq z.done, false
-  z = y.next()
-  eq z.value, 2
-  eq z.done, false
-  z = y.next()
-  eq z.value, undefined
-  eq z.done, true
+  y = x.next()
+  ok y.value is 0 and y.done is false
+  y = x.next()
+  ok y.value is 1 and y.done is false
+  y = x.next()
+  ok y.value is 2 and y.done is false
+  y = x.next()
+  ok y.value is undefined and y.done is true

 test "bound generator", ->
   obj =
@@ -42,3 +37,45 @@
   eq obj, obj.bound().next().value
   ok obj isnt obj.unbound().next().value
   eq obj, obj.nested().next().value.next().value.next().value
+
+test "error if `yield` occurs outside of a function", ->
+  throws -> CoffeeScript.compile 'yield 1'
+
+test "error if `yield from` occurs outside of a function", ->
+  throws -> CoffeeScript.compile 'yield from 1'
+
+test "`yield from` support", ->
+  x = do ->
+    yield from do ->
+      yield i for i in [3..4]
+
+  y = x.next()
+  ok y.value is 3 and y.done is false
+
+  y = x.next 1
+  ok y.value is 4 and y.done is false
+
+  y = x.next 2
+  arrayEq y.value, [1, 2]
+  ok y.done is true
+
+test "empty generator", ->
+  x = do -> yield return
+
+  y = x.next()
+  ok y.value is undefined and y.done is true
+
+test "`yield` by itself not at the end of a function errors", ->
+  throws -> CoffeeScript.compile 'x = -> yield; return'
+
+test "`yield from` at the end of a function errors", ->
+  throws -> CoffeeScript.compile 'x = -> x = 1; yield from'
+
+test "yield in if statements", ->
+  x = do -> if 1 is yield 2 then 3 else 4
+
+  y = x.next()
+  ok y.value is 2 and y.done is false
+
+  y = x.next 1
+  ok y.value is 3 and y.done is true


adding sampleResult data structure

adding sampleResult data structure

--- test/giphy.spec.coffee
+++ test/giphy.spec.coffee
@@ -21,6 +21,10 @@
       url: sampleUri
     }
   }
+}
+
+sampleResult = {
+  data: sampleData
 }

 sampleCollectionResult = {


fix doc tests to be less coupled to connection internals

--- test/server/doc.coffee
+++ test/server/doc.coffee
@@ -1,4 +1,5 @@
 {expect} = require 'chai'
+{Connection} = require '../../lib/client'
 {Doc}    = require '../../lib/client/doc'
 textType = require('ot-text').type

@@ -6,34 +7,34 @@

   numberType = require('../helpers/ot_number')

-  # TODO Use the real Connection class and stub things out on demand
-  connection =
-    state: 'connected'
-    sent: []
-    canSend: true
-    send: (data)-> @sent.push(data)
-    sendSubscribe: (collection, name, version)->
-      msg = {a: 'sub', c: collection, d: name}
-      msg.v = version if version?
-      @send msg
-    id: '42'
-    seq: 1
+  createConnection = ->
+    socket = {}
+    connection = new Connection(socket)
+    connection.id = '42'
+    connection.state = 'connected'
+    connection.canSend = true
+    connection.sent = []
+    connection.sendBackTo = null
+    connection.send = (data) ->
+      if @sendBackTo
+        @sendBackTo._onMessage data
+      else
+        @sent.push data
+    return connection
+
+  beforeEach ->
+    @connection = createConnection()
+    @doc = new Doc(@connection, 'notes', 'music')

   # Call this in a group to work with a created document
   beforeEachCreateDocument = ->
     beforeEach ->
-      @doc.create(numberType, 1)
-      @doc.flush()
-      sendMessage connection.sent.pop()
-
-  sendMessage = (msg)->
-    connection.receiver._onMessage(msg)
-
-  beforeEach ->
-    @doc = new Doc(connection, 'notes', 'music')
-    connection.receiver = @doc
-    connection.sent = []
-
+      # Mirror message right back to doc
+      @connection.sendBackTo = @doc
+      @doc.create numberType, 1
+      # Flush forces the doc to send immediately instead of async
+      @doc.flush()
+      @connection.sendBackTo = null

   describe '#subscribe', ->

@@ -42,26 +43,29 @@
       @doc.subscribe =>
         expect(@doc.subscribed).to.be.true
         done()
-      sendMessage c: 'notes', d: 'music', a: 'sub'
+      @doc._onMessage {c: 'notes', d: 'music', a: 'sub'}

     it 'emits subscribe', (done)->
       @doc.subscribe()
       @doc.on('subscribe', done)
-      sendMessage c: 'notes', d: 'music', a: 'sub'
+      @doc._onMessage {c: 'notes', d: 'music', a: 'sub'}

     it 'sends subscription message', ->
       @doc.subscribe()
-      expect(connection.sent[0].c).to.equal 'notes'
-      expect(connection.sent[0].d).to.equal 'music'
-      expect(connection.sent[0].a).to.equal 'sub'
+      expect(@connection.sent[0].c).to.equal 'notes'
+      expect(@connection.sent[0].d).to.equal 'music'
+      expect(@connection.sent[0].a).to.equal 'sub'

     it 'retrives snapshot', ->
       @doc.subscribe()
       expect(@doc.snapshot).to.be.undefined
-      sendMessage c: 'notes', d: 'music', a: 'sub', data:
-        {data: 'snapshot', v: 0, type: 'text'}
+      @doc._onMessage
+        c: 'notes'
+        d: 'music'
+        a: 'sub'
+        data:
+          {data: 'snapshot', v: 0, type: 'text'}
       expect(@doc.snapshot).to.equal 'snapshot'
-

   describe '#unsubscribe', ->
@@ -73,31 +77,31 @@
       @doc.unsubscribe =>
         expect(@doc.subscribed).to.be.false
         done()
-      sendMessage c: 'notes', d: 'music', a: 'unsub'
+      @doc._onMessage {c: 'notes', d: 'music', a: 'unsub'}

     it 'emits unsubscribe', (done)->
       @doc.on('unsubscribe', done)
-      sendMessage c: 'notes', d: 'music', a: 'unsub'
+      @doc._onMessage {c: 'notes', d: 'music', a: 'unsub'}

     it 'sends unsubscription message', ->
       @doc.unsubscribe()
-      expect(connection.sent[0]).to.deep.equal {c: 'notes', d: 'music', a: 'unsub'}
+      expect(@connection.sent[0]).to.deep.equal {c: 'notes', d: 'music', a: 'unsub'}

     it 'calls subscribe callbacks', (done)->
       @doc.subscribe(done)
       @doc.unsubscribe()
-      sendMessage c: 'notes', d: 'music', a: 'unsub'
+      @doc._onMessage {c: 'notes', d: 'music', a: 'unsub'}

   describe '#fetch', ->

     it 'sends fetch message', ->
       @doc.fetch()
-      expect(connection.sent[0]).to.deep.equal {c: 'notes', d: 'music', a: 'fetch'}
+      expect(@connection.sent[0]).to.deep.equal {c: 'notes', d: 'music', a: 'fetch'}

     it 'sets snapshot', ->
       @doc.fetch()
-      sendMessage c: 'notes', d: 'music', a: 'fetch', data: { data: 'cool' , v: 0, type: 'text'}
+      @doc._onMessage {c: 'notes', d: 'music', a: 'fetch', data: { data: 'cool' , v: 0, type: 'text'}}
       expect(@doc.snapshot).to.equal 'cool'

     it 'gets ready', (done)->
@@ -105,44 +109,43 @@
       @doc.on 'ready', =>
         expect(@doc.state).to.equal 'ready'
         done()
-      sendMessage c: 'notes', d: 'music', a: 'fetch', data: { data: 'cool' , v: 0}
-
-    it 'fetches ops when we have data', ->
-
-    it 'just calls the callback when we are subscribed', ->
-
-    it 'calls all callbacks when fetch is called multiple times', ->
+      @doc._onMessage {c: 'notes', d: 'music', a: 'fetch', data: { data: 'cool' , v: 0}}
+
+    it 'fetches ops when we have data'
+
+    it 'just calls the callback when we are subscribed'
+
+    it 'calls all callbacks when fetch is called multiple times'

   describe '#create', ->

     it 'calls callback', (done)->
-      @doc.create(textType, done)
-      @doc.flush()
-      sendMessage connection.sent[0]
+      @connection.sendBackTo = @doc
+      @doc.create textType, done

     it 'sends create message', ->
       @doc.create(textType)
       @doc.flush()
-      expect(connection.sent[0]).to.have.property('create')
+      expect(@connection.sent[0]).to.have.property('create')

     it 'immediately injests data locally', ->
       @doc.create(textType, 'a note on music')
       expect(@doc.snapshot).to.equal 'a note on music'

     it 'injests data on success', ->
+      @connection.sendBackTo = @doc
       @doc.create(textType, 'a note on music')
       @doc.flush()
-      sendMessage connection.sent[0]
       expect(@doc.snapshot).to.equal 'a note on music'

     it 'gets ready', (done)->
+      @connection.sendBackTo = @doc
       expect(@doc.state).to.be.null
       @doc.create(textType, 'a note on music')
       @doc.flush()
       @doc.on 'ready', =>
         expect(@doc.state).to.equal 'ready'
         done()
-      sendMessage connection.sent[0]

     it 'emits create after snapshot created', (done)->
       @doc.once 'create', =>
@@ -158,7 +161,7 @@
     it 'sends del message', ->
       @doc.del()
       @doc.flush()
-      expect(connection.sent[0]).to.have.property('del')
+      expect(@connection.sent[0]).to.have.property('del')

     it 'unsets type', ->
       @doc.del()
@@ -172,39 +175,36 @@

   describe 'subscribe unsubscribe and fetch', ->
-    it 'subscribes once and calls all callbacks when subscribe is called multiple times', ->
-    it 'unsubscribes once and calls all callbacks when unsubscribe is called multiple times', ->
+    it 'subscribes once and calls all callbacks when subscribe is called multiple times'
+    it 'unsubscribes once and calls all callbacks when unsubscribe is called multiple times'
     it 'hydrates the document if you call getOrCreate() with no data followed by getOrCreate() with data'

   describe 'editing contexts', ->

     beforeEach ->
+      @connection.sendBackTo = @doc
       @doc.create(textType, 'note')
       @doc.flush()
-      sendMessage connection.sent.pop()
       @context = @doc.createContext()
-
-    afterEach ->
-      @doc.removeContexts()
-

     it '#get exposes data', ->
       expect(@context.get()).to.equal 'note'

     it 'changes snapshot locally', ->
       @context.insert(0, 'delicious ')
-      @doc.flush()
       expect(@doc.snapshot).to.equal 'delicious note'

-      sendMessage connection.sent.pop()
+      @doc.flush()
       expect(@doc.snapshot).to.equal 'delicious note'

     it 'changes context data', ->
       @context.insert(0, 'delicious ')
       @doc.flush()
-      sendMessage connection.sent.pop()
       expect(@context.get()).to.equal 'delicious note'
+
+    it 'can call removeContexts', ->
+      @doc.removeContexts()

   describe 'rollback', ->
@@ -215,12 +215,12 @@
         @doc.create textType, 'a note on music'
         @doc.flush()
         expect(@doc.state).to.equal 'floating'
-        sendMessage c: 'notes', d: 'music', a: 'ack', error: 'rejected'
+        @doc._onMessage {c: 'notes', d: 'music', a: 'ack', error: 'rejected'}
         expect(@doc.state).to.be.null

-      it "when we're ready and the server rejects the op", ->
-
-      it "when the doc is floating and the document already exists on the server", ->
+      it "when we're ready and the server rejects the op"
+
+      it "when the doc is floating and the document already exists on the server"

     describe 'operation', ->

@@ -236,15 +236,15 @@
         @doc.flush()
         expect(@context.get()).to.equal 3

-        msg = connection.sent.pop()
+        msg = @connection.sent.pop()
         msg.error = 'rejected'
         msg.a = 'ack'
-        sendMessage msg
+        @doc._onMessage msg
         expect(@context.get()).to.equal 1

     it 'ends up in the right state if we create() then subscribe() synchronously'
-    it "abandons the document state if we can't recover from the rejected op", ->
+    it "abandons the document state if we can't recover from the rejected op"

   describe 'after op event', ->

@@ -265,7 +265,7 @@
       @doc.once 'after op', =>
         @doc.submitOp(1)
         @doc.flush()
-        expect(connection.sent[0].op).to.equal -1
+        expect(@connection.sent[0].op).to.equal -1
         done()
       @doc.submitOp(-1)

@@ -292,7 +292,7 @@

   describe 'ops', ->
-    it 'sends an op to the server', ->
-    it 'deletes a document', ->
-    it 'only sends one op to the server if ops are sent synchronously', ->
-    it 'reorders sent (but not acknowledged) operations on reconnect', ->
+    it 'sends an op to the server'
+    it 'deletes a document'
+    it 'only sends one op to the server if ops are sent synchronously'
+    it 'reorders sent (but not acknowledged) operations on reconnect'


修正新出现的一个DOM中有两个相同的绑定表达式时部分功能无法刷新的BUG

修正新出现的一个DOM中有两个相同的绑定表达式时部分功能无法刷新的BUG

--- src/widget/list/tree.coffee
+++ src/widget/list/tree.coffee
@@ -416,7 +416,7 @@
 		tree = @
 		itemsScope = node._itemsScope
 		if not itemsScope
-			node._itemsScope = itemsScope = new cola.ItemsScope(node._scope)
+			node._itemsScope = itemsScope = new cola.ItemsScope(node._scope, node._bind?._expression)
 			itemsScope.alias = node._alias
 			itemsScope._retrieveItems = (dataCtx) -> node._bind.retrieveChildNodes(node, null, dataCtx)
 			itemsScope.onItemsRefresh = () ->


Fix another spec that relied on black being first

--- spec/javascripts/random_playouts_spec.coffee
+++ spec/javascripts/random_playouts_spec.coffee
@@ -1,3 +1,7 @@
+forceNextMoveToBe = (color, board)->
+  oppositeColor = other_color(color)
+  board.moves.push create_pass_move(oppositeColor)
+
 describe 'random_move_generation', ->
   board = null

@@ -33,6 +37,7 @@

   it 'can generate a random move on the first line', ->
     board = small_first_line_board()
+    forceNextMoveToBe(BLACK, board)
     old_max_try = MAXIMUM_TRY_MODIFICATOR
     # minimize chances of falsely creating a pass move
     # (false in the context of this spec)
@@ -103,9 +108,9 @@
     beforeEach -> board = tiny_game()

     it 'finds the right number of black moves', ->
-      board.moves.push create_pass_move(WHITE)
+      forceNextMoveToBe(BLACK, board)
       expect(get_all_plausible_moves(board).length).toEqual 5

     it 'finds the right number of white moves', ->
-      board.moves.push create_pass_move(BLACK)
+      forceNextMoveToBe(WHITE, board)
       expect(get_all_plausible_moves(board).length).toEqual 4


Don't trample on @xs & @ys

Don't trample on @xs & @ys

Both parts of discontinuous patches are displayed and
correctly hit tests for each patches

--- bokehjs/src/coffee/renderer/glyph/patches.coffee
+++ bokehjs/src/coffee/renderer/glyph/patches.coffee
@@ -25,7 +25,7 @@
     ds = {}
     for i in [0...nanned_qs.length]
       ds[i] = []
-      qs = nanned_qs[i]
+      qs = _.toArray(nanned_qs[i])
       while qs.length > 0

         nan_index = _.findLastIndex(qs, (q) ->  _.isNaN(q))


Override frame height and width getters with deterministic values (#5122)

--- bokehjs/test/models/annotations/color_bar.coffee
+++ bokehjs/test/models/annotations/color_bar.coffee
@@ -46,41 +46,40 @@

     beforeEach ->
       # Stub solver computed values with deterministic frame height and width
-      frame_stub = sinon.stub(@plot.plot_canvas.frame, 'get')
-      frame_stub.withArgs('height').returns(500)
-      frame_stub.withArgs('width').returns(500)
+      Object.defineProperty(@plot.plot_canvas.frame, 'height', { get: () -> 500 })
+      Object.defineProperty(@plot.plot_canvas.frame, 'width', { get: () -> 500 })

     describe "ColorBar.Model._title_extent method", ->

-      it.skip "_title_height should return 0 if there is no title", ->
+      it "_title_height should return 0 if there is no title", ->
         title_height = @color_bar._title_extent()
         expect(title_height).to.be.equal(0)

-      it.skip "_title_height should calculate title height plus title_standoff if there is a title", ->
+      it "_title_height should calculate title height plus title_standoff if there is a title", ->
         @color_bar.title = "I'm a title"
         @color_bar.title_standoff = 5
         title_height = @color_bar._title_extent()
         expect(title_height).to.be.equal(20)

     describe "ColorBar.Model._tick_extent method", ->
-      it.skip "Should return zero if either low or high are unset", ->
+      it "Should return zero if either low or high are unset", ->
         @color_bar.color_mapper = new LinearColorMapper({palette: Viridis.Viridis10})
         expect(@color_bar._tick_extent()).to.be.equal(0)

-      it.skip "Should return major_tick_out if both low and high are set", ->
+      it "Should return major_tick_out if both low and high are set", ->
         @color_bar.color_mapper = new LinearColorMapper({low: 0, high: 10, palette: Viridis.Viridis10})
         @color_bar.major_tick_out = 6
         expect(@color_bar._tick_extent()).to.be.equal(6)

     describe "ColorBar.Model._tick_coordinate_mapper method", ->

-      it.skip "LinearColorMapper should yield LinearMapper instance with correct state", ->
+      it "LinearColorMapper should yield LinearMapper instance with correct state", ->
         @color_bar.color_mapper = new LinearColorMapper({low: 0, high: 10, palette: Viridis.Viridis10})
         mapper = @color_bar._tick_coordinate_mapper(100) #length of scale dimension
         expect(mapper).to.be.instanceof(LinearMapper)
         expect(mapper.mapper_state).to.be.deep.equal [10, 0]

-      it.skip "LogColorMapper should yield LogMapper instance with correct state", ->
+      it "LogColorMapper should yield LogMapper instance with correct state", ->
         @color_bar.color_mapper = new LogColorMapper({low: 0, high: 10, palette: Viridis.Viridis10})
         mapper = @color_bar._tick_coordinate_mapper(100) #length of scale dimension
         expect(mapper).to.be.instanceof(LogMapper)
@@ -93,7 +92,7 @@
           beforeEach ->
             @plot.add_layout(@color_bar)

-          it.skip "Should use set `width` and `height` if set", ->
+          it "Should use set `width` and `height` if set", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis3})
             @color_bar.width = 100
             @color_bar.height = 200
@@ -102,21 +101,21 @@
             expect(image_dimensions.width).to.be.equal(100)
             expect(image_dimensions.height).to.be.equal(200)

-          it.skip "Should return height = 0.30 * frame_height for 'short' palette", ->
+          it "Should return height = 0.30 * frame_height for 'short' palette", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis3})

             image_dimensions = @color_bar._computed_image_dimensions()
             expect(image_dimensions.width).to.be.equal(25)
             expect(image_dimensions.height).to.be.equal(150)

-          it.skip "Should return height = palette.length * 25 for 'medium' palette", ->
+          it "Should return height = palette.length * 25 for 'medium' palette", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis10})

             image_dimensions = @color_bar._computed_image_dimensions()
             expect(image_dimensions.width).to.be.equal(25)
             expect(image_dimensions.height).to.be.equal(250)

-          it.skip "Should return height = 0.80 * plot.height for 'long' palette", ->
+          it "Should return height = 0.80 * plot.height for 'long' palette", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis256})

             image_dimensions = @color_bar._computed_image_dimensions()
@@ -130,7 +129,7 @@
             document = new Document()
             document.add_root(@plot)

-          it.skip "Should return height = plot.height - 2 * padding for any palette in side panel", ->
+          it "Should return height = plot.height - 2 * padding for any palette in side panel", ->

             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis3})
             @color_bar.title = "I'm a title"
@@ -146,7 +145,7 @@
             @color_bar.orientation = 'horizontal'
             @plot.add_layout(@color_bar)

-          it.skip "Should use set `width` and `height` if set", ->
+          it "Should use set `width` and `height` if set", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis3})
             @color_bar.width = 100
             @color_bar.height = 200
@@ -155,21 +154,21 @@
             expect(image_dimensions.width).to.be.equal(100)
             expect(image_dimensions.height).to.be.equal(200)

-          it.skip "Should return width = 0.30 * plot.width for 'short' palette", ->
+          it "Should return width = 0.30 * plot.width for 'short' palette", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis3})

             image_dimensions = @color_bar._computed_image_dimensions()
             expect(image_dimensions.width).to.be.equal(150)
             expect(image_dimensions.height).to.be.equal(25)

-          it.skip "Should return width = palette.length * 25 for 'medium' palette", ->
+          it "Should return width = palette.length * 25 for 'medium' palette", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis10})

             image_dimensions = @color_bar._computed_image_dimensions()
             expect(image_dimensions.width).to.be.equal(250)
             expect(image_dimensions.height).to.be.equal(25)

-          it.skip "Should return width = 0.80 * plot.width for 'long' palette", ->
+          it "Should return width = 0.80 * plot.width for 'long' palette", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis256})

             image_dimensions = @color_bar._computed_image_dimensions()
@@ -185,7 +184,7 @@
             document = new Document()
             document.add_root(@plot)

-          it.skip "Should return width = plot.width - 2 * padding for any palette in side panel", ->
+          it "Should return width = plot.width - 2 * padding for any palette in side panel", ->
             @color_bar.color_mapper = new LinearColorMapper({low: 1, high: 100, palette: Viridis.Viridis10})
             @color_bar.title = "I'm a title"

@@ -201,7 +200,7 @@
         @lin_expected = new Float64Array([0, 20, 40, 60, 80, 100])
         @log_expected = new Float64Array([0, 76.70099985546604, 86.73533304426542, 92.60504167945479, 96.76966623306478, 100])

-      it.skip "Should correctly tick coords and labels for LinearColorMapper if orientation='vertical'", ->
+      it "Should correctly tick coords and labels for LinearColorMapper if orientation='vertical'", ->
         @color_bar.color_mapper = new LinearColorMapper({low: 10, high: 20, palette: Viridis.Viridis10})
         @color_bar.height = 100
         @color_bar.orientation = 'vertical'
@@ -212,7 +211,7 @@
         expect(tick_coords.major[1]).to.be.deep.equal(new Float64Array([100, 80, 60, 40, 20, 0]))
         expect(tick_coords.major_labels).to.be.deep.equal([10, 12, 14, 16, 18, 20])

-      it.skip "Should correctly determine tick coords and labels for LinearColorMapperif orientation='horizontal'", ->
+      it "Should correctly determine tick coords and labels for LinearColorMapperif orientation='horizontal'", ->
         @color_bar.color_mapper = new LinearColorMapper({low: 10, high: 20, palette: Viridis.Viridis10})
         @color_bar.width = 100
         @color_bar.orientation = 'horizontal'
@@ -223,7 +222,7 @@
         expect(tick_coords.major[0]).to.be.deep.equal(new Float64Array([0, 20, 40, 60, 80, 100]))
         expect(tick_coords.major_labels).to.be.deep.equal([10, 12, 14, 16, 18, 20])

-      it.skip "Should correctly determine tick coords and labels for LogColorMapper if orientation='vertical'", ->
+      it "Should correctly determine tick coords and labels for LogColorMapper if orientation='vertical'", ->
         @color_bar.color_mapper = new LogColorMapper({low: 0, high: 1000, palette: Viridis.Viridis10})
         @color_bar.height = 100
         @color_bar.orientation = 'vertical'
@@ -234,7 +233,7 @@
         expect(tick_coords.major[1]).to.be.deep.equal(new Float64Array([100, 23.299000144533963, 13.264666955734583, 7.394958320545214, 3.2303337669352175, 0]))
         expect(tick_coords.major_labels).to.be.deep.equal([0, 200, 400, 600, 800, 1000])

-      it.skip "Should correctly determine tick coords and labels for LogColorMapper if orientation='vertical'", ->
+      it "Should correctly determine tick coords and labels for LogColorMapper if orientation='vertical'", ->
         @color_bar.color_mapper = new LogColorMapper({low: 0, high: 1000, palette: Viridis.Viridis10})
         @color_bar.width = 100
         @color_bar.orientation = 'horizontal'
@@ -267,37 +266,37 @@
         plot_view: @plot_canvas_view
       })

-    it.skip "Should reset scale image if color_mapper changes", ->
+    it "Should reset scale image if color_mapper changes", ->
       # Reset spy count to zero (method was called during view initialization)
       @_set_canvas_image_stub.reset()
       @color_bar.color_mapper.palette = Viridis.Viridis3
       expect(@_set_canvas_image_stub.called).to.be.true

-    it.skip "ColorBar.View._get_image_offset method", ->
+    it "ColorBar.View._get_image_offset method", ->
       @color_bar.title = "I'm a title"
       expect(@color_bar_view._get_image_offset()).to.be.deep.equal({ x: 10, y: 27 })

-    it.skip "ColorBar.View._get_label_extent method (orientation='vertical')", ->
+    it "ColorBar.View._get_label_extent method (orientation='vertical')", ->
       # Note: ctx.measureText is stubbed to return {'width': 1, 'ascent': 1} in test/utils
       expect(@color_bar_view._get_label_extent()).to.be.equal(6)

-    it.skip "ColorBar.View._get_label_extent method (orientation='horizontal')", ->
+    it "ColorBar.View._get_label_extent method (orientation='horizontal')", ->
       @color_bar_view.model.orientation = "horizontal"
       expect(@color_bar_view._get_label_extent()).to.be.equal(20)

-    it.skip "ColorBar.View.compute_legend_dimensions method (orientation='vertical')", ->
+    it "ColorBar.View.compute_legend_dimensions method (orientation='vertical')", ->
       # Note: ctx.measureText is stubbed to return {'width': 1, 'ascent': 1} in test/utils
       @color_bar.height = 100
       @color_bar.width = 25

       expect(@color_bar_view.compute_legend_dimensions()).to.be.deep.equal({ height: 120, width: 51 })

-    it.skip "ColorBar.View.compute_legend_dimensions method (orientation='horizontal')", ->
+    it "ColorBar.View.compute_legend_dimensions method (orientation='horizontal')", ->
       @color_bar.orientation = "horizontal"
       @color_bar.height = 25
       @color_bar.width = 100

       expect(@color_bar_view.compute_legend_dimensions()).to.be.deep.equal({ height: 65, width: 120 })

-    it.skip "ColorBar.View._get_size method", ->
+    it "ColorBar.View._get_size method", ->
       expect(@color_bar_view._get_size()).to.be.equal(51)


Add license override for buffers@0.1.1

Add license override for buffers@0.1.1

--- build/tasks/license-overrides.coffee
+++ build/tasks/license-overrides.coffee
@@ -24,3 +24,14 @@

       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     """
+  'buffers@0.1.1':
+    repository: "http://github.com/substack/node-buffers"
+    license: 'MIT'
+    source: 'README.markdown'
+    sourceText: """
+      <content omitted>
+      license
+      =======
+
+      MIT/X11
+    """


Formatting

Formatting

--- app/assets/javascripts/rglossa/react/components/centre/start/corpus_info.jsx.coffee
+++ app/assets/javascripts/rglossa/react/components/centre/start/corpus_info.jsx.coffee
@@ -6,13 +6,13 @@
     corpusLogoUrl: React.PropTypes.string

   render: ->
-      `

-

-

-

-              {this.props.corpusName}
-              {this.props.corpusLogoUrl ?  : ''}
-

-

+    `

+

+

+

+            {this.props.corpusName}
+            {this.props.corpusLogoUrl ?  : ''}
+

-
`
+

+
`


added rewriting of protocol to https:

added rewriting of protocol to https:

--- src/zombie/resources.coffee
+++ src/zombie/resources.coffee
@@ -579,6 +579,7 @@
     httpRequest.timeout =        request.timeout || 0
     httpRequest.plain =		 false
     httpRequest.servername =     hostname
+    httpRequest.protocol =	 'https:'

     prxy = @resources.browser.getProxy()
     if prxy


removed debug output

removed debug output

--- src/zombie/resources.coffee
+++ src/zombie/resources.coffee
@@ -599,7 +599,7 @@
       port = 443
     httpRequest.port =           httpRequest.port || port
     #httpRequest.protocol =	 'https:'
-    console.log JSON.stringify(httpRequest, null, '\t')
+    #console.log JSON.stringify(httpRequest, null, '\t')

     entry =
       startedDateTime:  new Date()


fix menu elements

fix menu elements

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -438,6 +438,9 @@
 					_delta = (offset) ->
 						"delta:#{offset}"

+					_specified = (time) ->
+						"specified:#{time}"
+
 					boxSelectedClass = 'J-LC-JR-Jp'

 					props =
@@ -486,6 +489,7 @@

 					isValid()
+					now = new Date

 					if self.debug
 						addMenuElement 'in 1 minute', false, presets, (e, checked) ->
@@ -509,6 +513,37 @@
 						isValid()
 						return

+					addMenuElement 'in 4 hours', false, presets, (e, checked) ->
+						if checked
+							props.when = _delta _1h * 4
+							(presets.find '.selected').html 'In four hours'
+						isValid()
+						return
+
+					addMenuElement 'tomorrow 8am', false, presets, (e, checked) ->
+						if checked
+							tomorrow = new Date
+							tomorrow.setDate now.getDate() + 1
+							tomorrow.setHours 8
+							tomorrow.setMinutes 0
+							tomorrow.setSeconds 0
+							props.when = _specified tomorrow.getTime()
+							(presets.find '.selected').html 'Tomorrow morning'
+						isValid()
+						return
+
+					addMenuElement 'tomorrow 2pm', false, presets, (e, checked) ->
+						if checked
+							tomorrow = new Date
+							tomorrow.setDate now.getDate() + 1
+							tomorrow.setHours 14
+							tomorrow.setMinutes 0
+							tomorrow.setSeconds 0
+							props.when = _specified tomorrow.getTime()
+							(presets.find '.selected').html 'Tomorrow afternoon'
+						isValid()
+						return
+
 					addMenuElement 'in 2 days', false, presets, (e, checked) ->
 						if checked
 							props.when = _delta _1d * 2
@@ -516,15 +551,23 @@
 						isValid()
 						return

-					addMenuElement 'in 1 week', false, presets, (e, checked) ->
+					addMenuElement 'in 7 days', false, presets, (e, checked) ->
 						if checked
 							props.when = _delta _1d * 7
-							(presets.find '.selected').html 'Next week'
+							(presets.find '.selected').html 'Next week this time'
+						isValid()
+						return
+
+					addMenuElement 'in 1 month', false, presets, (e, checked) ->
+						if checked
+							tomorrow = new Date
+							tomorrow.setMonth now.getMonth() + 1
+							props.when = _specified tomorrow.getTime()
+							(presets.find '.selected').html 'Next month this time'
 						isValid()
 						return

 					addHovering menu
-

 					# Close the popup
@@ -547,7 +590,7 @@
 						reset = =>
 							div.removeClass cls
 							return
-
+
 						self.onSchedule props, loading, reset
 						close()
 						return


fix auth search

fix auth search

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -113,7 +113,7 @@
     @isAuthorisationErrorPage: (contents) -> /reauth/i.test contents

     @isAuthorisationErrorResponse: (resp) ->
-      resp?.toLowerCase().indexOf "authorization" isnt -1
+      (resp?.toLowerCase().indexOf "authorization") isnt -1

     checkAuthorised: (resp) ->
       url = @getServiceURL() + M.SETUP_URL_SUFFIX


show world label in class

show world label in class

--- src/draw.coffee
+++ src/draw.coffee
@@ -4,7 +4,7 @@

   draw_path = (label, world) ->
     dict = {
-      class: "draw_path #{label}"
+      class: ['draw_path', label]
       stroke: world.get 'stroke'
       fill: world.get 'fill'
     }
@@ -16,7 +16,7 @@

   draw_world = (label, world) ->
     SVG.g {
-      class: 'transform'
+      class: ['draw_world', label]
       transform: world.bind -> world.get('transform')
     }, world.bind -> _.flatten [
       draw_path(label,world)


Use createReadStream to get a stream in the read method

--- src/file.coffee
+++ src/file.coffee
@@ -258,12 +258,7 @@
     else
       promise = new Promise (resolve, reject) =>
         content = []
-        encoding = @getEncoding()
-        if encoding is 'utf8'
-          readStream = fs.createReadStream(@getPath(), {encoding})
-        else
-          iconv ?= require 'iconv-lite'
-          readStream = fs.createReadStream(@getPath()).pipe(iconv.decodeStream(encoding))
+        readStream = @createReadStream()

         readStream.on 'data', (chunk) ->
           content.push(chunk)


Game.Application must be an Observer

Game.Application must be an Observer

--- src/game.coffee
+++ src/game.coffee
@@ -1,5 +1,6 @@
-class Game.Application
+class Game.Application extends Game.Observer
   constructor: () ->
+    super
   init: () ->
     @state = new Game.State()
     @achievements = new Game.Achievements(@state)


Fixup Background asset loading.

Fixup Background asset loading.

--- src/game.coffee
+++ src/game.coffee
@@ -65,7 +65,7 @@
       loader.image 'balcony', 'assets/tiles-auto-balcony.png'
       for zIndex in [16..1]
         id = (16 - zIndex + 10000).toString().substr(1)
-        loader.image "bg#{zIndex}", "assets/bg-_#{id}_#{zIndex}.png", @width, MorningStroll.mapH
+        loader.image "bg#{zIndex}", "assets/bg-_#{id}_#{zIndex}.png"
       loader.spritesheet 'mate', 'assets/mate.png', MorningStroll.playerW, MorningStroll.playerH
       loader.spritesheet 'player', 'assets/player.png', MorningStroll.playerW, MorningStroll.playerH


ok, it works to write something to the blockchain

--- src/insert.iced
+++ src/insert.iced
@@ -29,15 +29,13 @@
     @args = minimist argv, {
       alias :
         c : 'config'
-        d : 'data'
         f : 'from-address'
         a : 'amount'
-        l : 'data-log'
         b : 'bitcoin-config'
         u : 'bitcoin-user'
         p : 'bitcoin-password'
         n : 'confirmations'
-      string : [ 'a', 'l', 'b' ]
+      string : [ 'l', 'b' ]
     }
     cb null

@@ -50,6 +48,7 @@
   icfg : (k, def = null) ->
     v = @config[k] unless (v = @args[k])?
     if not v? then def
+    else if typeof(v) is 'number' then v
     else if isNaN(v = parseInt(v, 10)) then null
     else v

@@ -83,7 +82,7 @@
   #-----------------------------------

   amount : () -> @icfg('amount', btcjs.networks.bitcoin.dustThreshold+1)
-  min_amount : () -> @amount() + btcjs.networks.bitcoin.feePerKb
+  min_amount : () -> @amount() + btcjs.networks.bitcoin.feePerKb - 800
   max_amount : () -> 2*btcjs.networks.bitcoin.feePerKb
   min_confirmations : () -> @icfg('confirmations', 3)
   from_address : () -> @cfg('from-address')
@@ -138,18 +137,61 @@

   #-----------------------------------

+  get_private_key : (cb) ->
+    await @client.dumpPrivKey @from_address(), defer err, @priv_key
+    cb err
+
+  #-----------------------------------
+
+  make_post_data : (cb) ->
+    raw = @args._[0]
+    err = null
+    if not raw? then err = new Error "not post data given"
+    else if not raw.match /^[0-9a-fA-F]{40}$/ then err = new Error "post data must be a 40-btye hex hash"
+    else @post_data = new Buffer(raw, 'hex')
+    cb err
+
+  #-----------------------------------
+
+  make_transaction : (cb) ->
+    @data_to_address = (new btcjs.Address @post_data, 0).toBase58Check()
+    tx = new btcjs.Transaction
+    tx.addInput @input_tx.txid, @input_tx.vout
+    tx.addOutput @data_to_address, @amount()
+    skey = btcjs.ECKey.fromWIF @priv_key
+    tx.sign 0, skey
+    @out_tx = tx
+    cb null
+
+  #-----------------------------------
+
+  submit_transaction : (cb) ->
+    await @client.sendRawTransaction @out_tx.toHex(), defer err, @out_tx_id
+    cb err
+
+  #-----------------------------------
+
+  write_output : (cb) ->
+    console.log JSON.stringify {
+      @out_tx_id,
+      @data_to_address
+    }
+    cb null
+
+  #-----------------------------------
+
   run : (argv, cb) ->
     esc = make_esc cb, "Runner::main"
     await @parse_args argv, esc defer()
     await @read_config esc defer()
     await @check_args esc defer()
+    await @make_post_data esc defer()
     await @make_bitcoin_client esc defer()
     await @find_transaction esc defer()
-    console.log @input_tx
-    #await @find_post_data esc defer()
-    #await @get_private_key esc defer()
-    #await @make_transaction esc defer()
-    #await @submit_transaction esc defer()
+    await @get_private_key esc defer()
+    await @make_transaction esc defer()
+    await @submit_transaction esc defer()
+    await @write_output esc defer()
     cb null

   #-----------------------------------


try location

try location

--- app/assets/javascripts/shared/mobile_sms.js.coffee
+++ app/assets/javascripts/shared/mobile_sms.js.coffee
@@ -9,6 +9,6 @@
   $sms_field = $('.mobile-sms')
   if $sms_field.length > 0
     mobile_href = $sms_field.prop('href').replace('?', sms_delimiter())
-    window.open(mobile_href, '_system')
+    $(location).attr('href', mobile_href);


Return JSON object instead of a primitive

--- controllers/temperature-controller.coffee
+++ controllers/temperature-controller.coffee
@@ -10,7 +10,7 @@

       res.status(body.cod)
       return res.send(body.message) unless body.cod == 200
-      res.send "#{body.main.temp}"
+      res.send temperature: body.main.temp

   fahrenheit: (req, res) =>
     {city, state, country} = req.query
@@ -20,7 +20,7 @@

       res.status(body.cod)
       return res.send(body.message) unless body.cod == 200
-      res.send "#{body.main.temp}"
+      res.send temperature: body.main.temp

   request: (units, location, callback=->) =>
     options =


Bigger helicopter bullets

Bigger helicopter bullets

--- app/scripts/lazerscripts/stage1/heli_attack.coffee
+++ app/scripts/lazerscripts/stage1/heli_attack.coffee
@@ -21,7 +21,7 @@
     )
     p.addComponent('BurstShot').burstShot
       burstCooldown: 2500
-      burstAmount: 7
+      burstAmount: 4
       angle: -15
       angleDeviation: 10
       aim: 45
@@ -31,8 +31,8 @@
           .crop(6, 21, 18, 7)
           .flip()
           .attr(
-            w: 12
-            h: 4
+            w: 20
+            h: 8
             speed: 350
           )


Require patch

Require patch

--- functions/main.coffee
+++ functions/main.coffee
@@ -6,7 +6,7 @@
 exec = require('child_process').exec
 https = require 'https'
 open = require 'open'
-pkg = require '../package.json'
+pkg = require './package.json'

 ###*


activityTicker : removeLike from activityTicker when user like is removed

--- client/Social/Activity/ContentDisplays/ActivityTicker.coffee
+++ client/Social/Activity/ContentDisplays/ActivityTicker.coffee
@@ -42,6 +42,7 @@
     # and comments doesnt have slug
     # group.on "ReplyIsAdded"       , @bound "addComment"
     group.on "PostIsDeleted"        , @bound "deleteActivity"
+    group.on "LikeIsRemoved"        , @bound "removeLike"

     @listController.listView.on 'ItemWasAdded', (view, index) =>
       if viewData = view.getData()
@@ -215,6 +216,17 @@
           else
             @addNewItem eventObj

+  removeLike : (data)->
+    {origin, subject, liker} = data
+    unless @getConstructorName(origin) and @getConstructorName(subject)
+      return console.warn "data is not valid"
+    source  = KD.remote.revive liker
+    target  = KD.remote.revive origin
+    subject = KD.remote.revive subject
+    as      = "like"
+
+    @removeItem {source, target, as, subject}
+
   addComment: (data) ->
     {origin, reply, subject, replier} = data


fix computeds in membership_card

fix computeds in membership_card

--- client/vue/components/membership_card/membership_card.coffee
+++ client/vue/components/membership_card/membership_card.coffee
@@ -87,17 +87,13 @@
       return unless !_.isEmpty @fragment
       Records.memberships.fetchByNameFragment(@fragment, @group.key)

-  computed:
     cardTitle: ->
       if @pending
         'membership_card.invitations'
       else
         "membership_card.#{@group.targetModel().constructor.singular}_members"
-    order: ->
-      if @pending
-        '-createdAt'
-      else
-        '-admin'
+
+  computed:
     pollType: ->
       @group.targetModel().translatedPollType() if @group.targetModel().isA('poll')

@@ -111,7 +107,7 @@
     >


CONFIG: Add refresh task end point

CONFIG: Add refresh task end point

--- src/app/api/models/project.coffee
+++ src/app/api/models/project.coffee
@@ -3,6 +3,6 @@
 .factory("Project", (resourcePlus) ->
   Project = resourcePlus "/projects/:id", { id: "@id" }
   Project.tutorialEnrolment = resourcePlus "/units/:id/tutorials/:tutorial_abbreviation/enrolments/:project_id", {id: "@id", tutorial_abbreviation: "@tutorial_abbreviation", project_id: "@project_id"}
-
+  Project.refreshTasks = resourcePlus '/projects/:project_id/refresh_tasks/:task_definition_id', {task_definition_id: "@task_definition_id", project_id: "@project_id"}
   Project
 )


[atom] add emmet

[atom] add emmet

--- .atom/packages.cson
+++ .atom/packages.cson
@@ -2,6 +2,7 @@
   "busy-signal"
   "dash"
   "dracula-theme"
+  "emmet"
   "file-icons"
   "highlight-selected"
   "intentions"


Complete implementation of editing states

--- src/components/image-chooser.cjsx
+++ src/components/image-chooser.cjsx
@@ -10,37 +10,77 @@
   propTypes:
     exerciseId: React.PropTypes.string

-  getInitialState: -> {}
+  getInitialState: ->
+
+    ex = ExerciseStore.get(@props.exerciseId)
+    console.log ex.attachments
+    {asset: _.first(ex.attachments)}
+
+  # TBD
+  # componentWillMount:   -> ExerciseStore.on('saved',  @saveImage)
+  # componentWillUnmount: -> ExerciseStore.off('saved', @saveImage)
+
+  updateUploadStatus: (status) ->
+    if status.error
+      @setState(error: status.error)
+    if status.progress?
+      @setState(progress: status.progress, error: false)
+    if status.response # 100%, we're done
+      _.delay =>
+        # N.B. replaceState, not setState.
+        @replaceState(asset: status.response.asset)
+      , 500 # wait a 1/2 second then display

   uploadImage: ->
     return unless @state.file
-    api.uploadExerciseImage(@props.exerciseId, @state.file, (status) ->
-      console.log status
-    )
+    number = ExerciseStore.getNumber(@props.exerciseId)
+    api.uploadExerciseImage(number, @state.file, @updateUploadStatus)
+    @setState(progress: 0)
+
+  renderUploadStatus: ->
+    return unless @state.progress
+    <BS.ProgressBar now={@state.progress} />

   onImageChange: (ev) ->
     ev.preventDefault()
     file = ev.target.files[0]
     reader = new FileReader()
     reader.onloadend = =>
-      ExerciseActions.updateImage(@props.exerciseId, reader.result)
-
-      @setState({file, imageData: reader.result})
+      @setState({file, imageData: reader.result, asset: null})
     reader.readAsDataURL(file)

+  renderTextCopyNPaste: ->
+    return unless @state.asset
+    console.log @state.asset
+    markdown = """
+      ![Image](#{@state.asset.url}         "Optional title")
+      ![Large](#{@state.asset.large.url}   "Optional title")
+      ![Medium](#{@state.asset.medium.url} "Optional title")
+      ![Small](#{@state.asset.small.url}   "Optional title")
+    """
+    <textarea value={markdown} readOnly />
+
+  renderUploadBtn: ->
+    return unless @state.imageData and not @state.progress
+    <BS.Button onClick={@uploadImage}>Upload</BS.Button>
+
   render: ->
-    imageSrc = @state.imageData or @props.url
+    imageSrc = @state.imageData or @state.asset?.large.url
     image =  if imageSrc
     classNames = classnames('image-chooser', {
       'with-image': image
     })

       {image}
-
-        {if image then 'Choose different image' else 'Choose Image'}
-        <input id='file' className="file" type="file" onChange={@onImageChange} />
-
-      <BS.Button onClick={@uploadImage}>Save</BS.Button>
+

+
+          {if image then 'Choose different image' else 'Choose Image'}
+          <input id='file' className="file" type="file" onChange={@onImageChange} />
+
+        {@renderUploadBtn()}
+

+      {@renderTextCopyNPaste()}
+      {@renderUploadStatus()}

 module.exports = ImageChooser


Reset selection after correcting for autocapitalization

--- src/trix/controllers/input_controller.coffee
+++ src/trix/controllers/input_controller.coffee
@@ -86,9 +86,8 @@
     # by the extra <br> rendered to represent them.
     if textDeleted is "\n" and unhandledDeletion
       if textAdded and not unhandledAddition
-        Trix.selectionChangeObserver.update()
         if range = @responder?.getSelectedRange()
-          if @responder?.positionIsBlockBreak(range[1])
+          if @responder?.positionIsBlockBreak(range[1] + textAdded.length)
             unhandledDeletion = false

     not (unhandledAddition or unhandledDeletion)
@@ -155,9 +154,11 @@
       {data} = event
       {textAdded} = @inputSummary
       if textAdded and textAdded isnt data and textAdded.toUpperCase() is data
-        @expandSelectionInDirection("forward")
+        range = @responder?.getSelectedRange()
+        @responder?.setSelectedRange([range[0], range[1] + textAdded.length])
         @responder?.insertString(data)
         @setInputSummary(textAdded: data)
+        @responder?.setSelectedRange(range)

     dragenter: (event) ->
       event.preventDefault()


Run selection observer timer while the editor element is present in the DOM

--- src/trix/observers/selection_observer.coffee
+++ src/trix/observers/selection_observer.coffee
@@ -1,16 +1,27 @@
 class Trix.SelectionObserver
-  events = ["DOMFocusIn", "DOMFocusOut", "mousedown", "mousemove", "keydown"]
+  events = ["DOMFocusIn", "DOMFocusOut", "mousedown", "keydown"]

   constructor: (@element) ->
-    @element.addEventListener(event, @refresh) for event in events
+    @element.addEventListener(event, @start) for event in events
     @range = getRange()

-  refresh: =>
-    requestAnimationFrame =>
+  start: =>
+    return if @running
+    @running = true
+    requestAnimationFrame(@tick)
+
+  stop: ->
+    delete @running
+
+  tick: =>
+    if elementWasRemovedFromDOM(@element)
+      @stop()
+    else
       range = getRange()
       unless rangesAreEqual(range, @range)
         @delegate?.selectionDidChange?()
         @range = range
+      requestAnimationFrame(@tick)

   getRange = ->
     selection = window.getSelection()
@@ -21,3 +32,6 @@
       left?.startOffset is right?.startOffset and
       left?.endContainer is right?.endContainer and
       left?.endOffset is right?.endOffset
+
+  elementWasRemovedFromDOM = (element) ->
+    not element.parentNode


Make handsontable widget listen for changes on all properties

--- bokehjs/src/coffee/widget/handson_table.coffee
+++ bokehjs/src/coffee/widget/handson_table.coffee
@@ -11,7 +11,7 @@
     initialize: (options) ->
       super(options)
       @render()
-      @listenTo(@model, 'change:source', () => @renderFn())
+      @listenTo(@model, 'change', () => @renderFn())
       source = @mget("source")
       @listenTo(source, 'change:data', () => @renderFn())
       @listenTo(source, 'change:selected', () => @changeSelection())
@@ -33,8 +33,9 @@
         columns = []

         for column in @mget("columns")
-          headers.push(column.get("header"))
-          columns.push({
+          if column?
+            headers.push(column.get("header"))
+            columns.push({
               data: column.get("field")
               type: column.get("type")
               format: column.get("format")
@@ -42,7 +43,7 @@
               strict: column.get("strict")
               checkedTemplate: column.get("checked")
               uncheckedTemplate: column.get("unchecked")
-          })
+            })

         @$el.handsontable({
           data: source.datapoints()


basic args capture

basic args capture

--- console/runner.coffee
+++ console/runner.coffee
@@ -11,7 +11,12 @@
 read_classfile = (cls) -> read_binary_file "#{relpath}/../third_party/#{cls}.class"

 # first two are 'coffee', 'scriptname.coffee'
-fname = if process.argv.length > 2 then process.argv[2] else '/dev/stdin'
+if process.argv.length > 2
+	fname = process.argv[2]
+	args = process.argv.slice(3)
+else
+    fname = '/dev/stdin'
+    args = process.argv.slice(2)
+
 class_data = new ClassFile read_binary_file(fname)
-
-jvm.run class_data, console.log, read_classfile, []
+jvm.run class_data, console.log, read_classfile, args


bignum test

bignum test

--- 03-visual-add/test/test.coffee
+++ 03-visual-add/test/test.coffee
@@ -17,4 +17,8 @@

   it 'should +1 correctly for special case', ->
     added = adder.add [0, 0, 4]
-    assert.deepEqual added, [5]+    assert.deepEqual added, [5]
+
+  it 'should support any number', ->
+    added = adder.add [9, 0, 0, 7, 1, 9, 9, 2, 5, 4, 7, 4, 0, 9, 9, 2]
+    assert.deepEqual added, [9, 0, 0, 7, 1, 9, 9, 2, 5, 4, 7, 4, 0, 9, 9, 3]


Added IsLoggedIn tests.

Added IsLoggedIn tests.

--- src/client/authorization/authorization-service_test.coffee
+++ src/client/authorization/authorization-service_test.coffee
@@ -6,6 +6,28 @@

     it 'exposes IsLoggedIn', inject (IsLoggedIn)->
         should.exist IsLoggedIn
+
+    describe 'IsLoggedIn', ->
+        it 'rejects on 401', inject (IsLoggedIn, $httpBackend)->
+            $httpBackend.whenGET('/doorman').respond 401
+            $httpBackend.expectGET '/doorman'
+            isLoggedIn = IsLoggedIn()
+            isLoggedIn.should.have.property 'then'
+            err = null
+            isLoggedIn.catch (e)-> err = e
+            $httpBackend.flush()
+            should.exist err
+
+        it 'resolves on 200', inject (IsLoggedIn, $httpBackend)->
+            $httpBackend.whenGET('/doorman').respond 200, {id: '123abc'}
+            $httpBackend.expectGET '/doorman'
+            isLoggedIn = IsLoggedIn()
+            user = null
+            isLoggedIn.then (u)-> user = u
+            $httpBackend.flush()
+            should.exist user
+            user.should.have.property 'id'
+            user.id.should.equal '123abc'

     describe 'oauth login flow', ->
         window =


Show correct placeholder in status update widget under avatarView

--- client/app/MainApp/avatararea.coffee
+++ client/app/MainApp/avatararea.coffee
@@ -163,24 +163,18 @@
 # avatar popup box Status Update Form
 class AvatarPopupShareStatus extends AvatarPopup

-  # show:->
-  #   super()
-
-  #   if (visitor = KD.getSingleton('mainController').getVisitor())
-  #     {profile} = visitor.currentDelegate
-  #     if @statusField.getOptions().placeholder is ""
-  #       @statusField.setPlaceHolder "What's new, #{Encoder.htmlDecode profile.firstName}?"
-
   viewAppended:->
     super()
+
+    {profile} = KD.whoami()

     @avatarPopupContent.addSubView @statusField = new KDHitEnterInputView
       type          : "textarea"
       validate      :
         rules       :
           required  : yes
+      placeholder   : "What's new, #{Encoder.htmlDecode profile.firstName}?"
       callback      : (status)=> @updateStatus status
-

   updateStatus:(status)->


Onboarding: OnboardingItem can work with a jQuery selector now.

--- client/Main/onboarding/onboardingitemview.coffee
+++ client/Main/onboarding/onboardingitemview.coffee
@@ -16,12 +16,18 @@
     @hasPrev       = index isnt 0 and @hasNext

     try
-      @parentElement = eval path
+      @parentElement = eval Encoder.htmlDecode path
       if @parentElement instanceof KDView
         @createContextMenu()
         @listenEvents()
+      else if @parentElement instanceof jQuery
+        @parentElement = @getKDViewFromJQueryElement @parentElement
+        return  unless @parentElement
+
+        @createContextMenu()
+        @listenEvents()
       else
-        console.warn "Parent element is not a KDView instance", { appName, itemName }
+        console.warn "Target element should be an instance of KDView or jQuery", { appName, itemName }
     catch e
       console.warn "Path parse error for onboarding item", { appName, itemName, e }

@@ -87,6 +93,16 @@

     return view

+  getKDViewFromJQueryElement: ($element) ->
+    element = $element[0] # first is jQuery method
+    kdview  = null
+
+    for key, kdinstance of KD.instances
+      if kdinstance.getElement?() is element
+        kdview = kdinstance
+
+    return kdview
+
   listenEvents: ->
     @on "NavigationRequested", (direction) =>
       @overlay?.destroy()


Client: If message doesn't exist, don't bug out.

--- client/app/js/views/private_messages_view.coffee
+++ client/app/js/views/private_messages_view.coffee
@@ -111,7 +111,7 @@
     message?.add(user.id, "#{user.id} is now offline.", type: "alert")

   isOpen: (message) =>
-    $findPopup(message.id).length > 0
+    message && $findPopup(message.id).length > 0

   # Returns true if the chat is scrolled to the bottom of the screen.
   # This also returns true if the messages are hidden.


[Bugfix] Reset pagination when filter changes

--- src/desktop/components/artwork_filter_2/views/filters_view.coffee
+++ src/desktop/components/artwork_filter_2/views/filters_view.coffee
@@ -14,6 +14,7 @@
     'click .js-artwork-filter-toggle'   : 'toggleBool'
     'click .js-artwork-filter-remove'   : 'filterDeselected'
     'click .js-artwork-filter-view-all' : 'seeAllClicked'
+    'click' : 'resetPagination'

   initialize: ({ @params, @counts, stickyOffset = 0 }) ->
     @sticky = new Sticky
@@ -45,6 +46,9 @@
     }
     this

+  resetPagination: ->
+    @params.set { page: 1 }, { silent: true }
+
   toggleBool: (e) ->
     e.preventDefault()
     key = ($el = $(e.target)).prop('name')


WIP: action/switch GUI working

WIP: action/switch GUI working

--- client/src/app/game/board/boardController.coffee
+++ client/src/app/game/board/boardController.coffee
@@ -163,7 +163,22 @@
     throw new Error("Cannot find simple destination for parameter '#{name}'")

   findParameterReferences = (name, outValues) ->
-    return ({ drain: drain, source: source } for drain, source of outValues when expression.indexOf("params.#{name}") != -1)
+    return ({ drain: drain, source: source } for drain, source of outValues when source.indexOf("params.#{name}") != -1)
+
+  convertPinsToModel = (pins) ->
+    params =
+      in: {}
+      out: {}
+    for pin in pins
+      if pin.direction in ["in", "inout"]
+        params.in[pin.name] = if pin.linkage is "simple" then pin.simpleDestination else pin.customDestinations.in
+      if pin.direction in ["out", "inout"]
+        if pin.linkage is "simple"
+          params.out[pin.simpleDestination] = "params.#{pin.name}"
+        else
+          for destination in pin.customDestinations.out
+            params.out[destination.drain] = destination.source
+    return params

   # Convert between "param form" used in game serialization and "pin form" used in GUI
   $scope.LINKAGES = ["simple", "custom"]
@@ -184,6 +199,7 @@
   else
     throw new Error("Model is not an action or switch")

+  # TODO: refactor into function
   $scope.pins = []
   for paramName, paramDef of typeDef.paramDefs
     pin =
@@ -212,5 +228,5 @@
   # Reply with the new data
   $scope.done = -> liaison.done
     comment: $scope.name
-    send: _.object(([output, input] for {input: input, output: output} in $scope.pins))
+    params: convertPinsToModel($scope.pins)
   $scope.cancel = -> liaison.cancel()


OutputView: set of improvements, delegate scroll, optional separator

--- client/stacks/lib/views/stacks/outputview.coffee
+++ client/stacks/lib/views/stacks/outputview.coffee
@@ -25,6 +25,12 @@

     @highlight = (@getOption 'highlight') or 'profile'

+    @stringifyOptions = {}
+
+    if separator = @getOption 'separator'
+      @stringifyOptions = { separator }
+
+
   viewAppended: -> @addSubView @container

   raise : -> @setClass   'raise'
@@ -36,18 +42,18 @@
     @code.updatePartial ''
     return this

-  stringify = (content) ->
+  stringify: (content) ->

     for item, i in content
-      content[i] = if typeof item is 'object' \
-                   then objectToString item else item
+      content[i] = if typeof item is 'object'
+      then objectToString item, @stringifyOptions else item

     content = content.join ' '

   add: (content...) ->

-    content = stringify content
+    content = @stringify content
     content = "[#{dateFormat Date.now(), 'HH:MM:ss'}] #{content}\n"
     @code.setPartial hljs.highlight(@highlight, content).value
     @scrollToBottom()
@@ -78,7 +84,7 @@

   set: (content...) ->

-    content = stringify content
+    content = @stringify content
     @code.updatePartial hljs.highlight(@highlight, content).value
     @scrollToBottom()

@@ -94,3 +100,8 @@
     return @add prefix, err.message or err

+  scrollToBottom: ->
+
+    @getDelegate()?.scrollToBottom?()
+
+    super


Improve Redux action logging

Improve Redux action logging

--- src/chromeExtension/store/createStore.coffee
+++ src/chromeExtension/store/createStore.coffee
@@ -18,7 +18,9 @@
     devToolsEnhancer = DevTools.instrument()

   ##   createLogger = require 'redux-logger'
-  ##   logger = createLogger()
+  ##   logger = createLogger
+  ##     predicate: (getState, action) ->
+  ##       return not(action.type in ['EFFECT_TRIGGERED', 'EFFECT_RESOLVED', 'MSG_RECEIVED'])
   ##   addMiddlewares = Redux.applyMiddleware thunk, saga, logger
   ##
   ##   # Use Chrome extension "Redux DevTools", if available


Include user to admin

Include user to admin

--- coffees/src/controllers/league_controller.coffee
+++ coffees/src/controllers/league_controller.coffee
@@ -8,7 +8,9 @@

   leagueIncludes = [
     { model: models.Player, required: false },
-    { model: models.Admin, required: false },
+    { model: models.Admin, required: false, include: [
+      { model: models.User, required: false }
+    ]},
     { model: models.Frame, required: false, include: [
       { model: models.Player, as: 'Player1', required: false },
       { model: models.Player, as: 'Player2', required: false },
@@ -23,7 +25,7 @@
       include: leagueIncludes

   router.get '/leagues', (request, response) ->
-    request.user.getLeagues().then (leagues) ->
+    request.user.getLeagues(include: leagueIncludes).then (leagues) ->
       response.json(leagues)

   router.post '/leagues', (request, response) ->


Set textarea to readOnly and select on click

This way the teacher can't inadvertantly modify the contents

--- src/components/course-settings/period-enrollment-code.cjsx
+++ src/components/course-settings/period-enrollment-code.cjsx
@@ -58,7 +58,7 @@
         </p>
       </div>

-      <textarea ref="textarea" onFocus={@selectText}>{msg}</textarea>
+      <textarea readOnly onClick={@selectText} value={msg} />

     </div>


Use HTML emails instead of Text

Use HTML emails instead of Text

--- server/lib/accounts.coffee
+++ server/lib/accounts.coffee
@@ -20,15 +20,15 @@
 Accounts.emailTemplates.siteName = RocketChat.settings.get 'Site_Name';
 Accounts.emailTemplates.from = "#{RocketChat.settings.get 'Site_Name'} <#{RocketChat.settings.get 'From_Email'}>";

-verifyEmailText = Accounts.emailTemplates.verifyEmail.text
-Accounts.emailTemplates.verifyEmail.text = (user, url) ->
+verifyEmailHtml = Accounts.emailTemplates.verifyEmail.html
+Accounts.emailTemplates.verifyEmail.html = (user, url) ->
 	url = url.replace Meteor.absoluteUrl(), Meteor.absoluteUrl() + 'login/'
-	verifyEmailText user, url
+	verifyEmailHtml user, url

-resetPasswordText = Accounts.emailTemplates.resetPassword.text
-Accounts.emailTemplates.resetPassword.text = (user, url) ->
+resetPasswordHtml = Accounts.emailTemplates.resetPassword.html
+Accounts.emailTemplates.resetPassword.html = (user, url) ->
 	url = url.replace /\/#\//, '/'
-	resetPasswordText user, url
+	resetPasswordHtml user, url

 Accounts.emailTemplates.enrollAccount.subject = (user) ->
 	if RocketChat.settings.get 'Accounts_Enrollment_Customized'
@@ -38,7 +38,7 @@

 	return RocketChat.placeholders.replace(subject);

-Accounts.emailTemplates.enrollAccount.text = (user, url) ->
+Accounts.emailTemplates.enrollAccount.html = (user, url) ->

 	if RocketChat.settings.get 'Accounts_Enrollment_Customized'
 		html = RocketChat.settings.get 'Accounts_Enrollment_Email'


remove the workaround

remove the workaround

--- src/main.coffee
+++ src/main.coffee
@@ -37,12 +37,9 @@
         else
             command = compileCommands[rawCodeType]
             if command?
-                # To workaround a (maybe) Node.js's bug. The bug is: If you don't
-                # use "cat" but instead using the `input` property, then some Chinese
-                # characters (very rare) will be garbled.
-                # TODO: But this workaround is not good, for it's not Windows compatible.
                 cprocess.execSync("cat \"" + filePath + "\" | " + command, {
                     encoding: "utf8"
+                    input: rawCode
                 })
             else
                 rawCode


Minor coding style update.

Minor coding style update.

--- src/main.coffee
+++ src/main.coffee
@@ -172,6 +172,7 @@
     socket.on 'close', ->
       $debug '- WebSocket connection'
       $bind connection.close, connection
+      return

     # Handles each request in a separate fiber.
     socket.on 'message', $bind jsonRpc.exec, jsonRpc


Fix limit 30 problem

Fix limit 30 problem

--- server/publications.coffee
+++ server/publications.coffee
@@ -1,8 +1,8 @@
 Meteor.publish "needs", (query) ->
   if query
-    Needs.find({completedAt: {$exists: false}, content: {$regex: query, $options: 'i'}}, {sort: {score: 1}, limit: 30})
+    Needs.find({completedAt: {$exists: false}, content: {$regex: query, $options: 'i'}}, {sort: {score: 1}})
   else
-    Needs.find {completedAt: {$exists: false}}, sort: {score: 1}, limit: 30
+    Needs.find {completedAt: {$exists: false}}, sort: {score: 1}

 Meteor.publish "userNeeds", (username) ->
   Needs.find {username: username, completedAt: {$exists: false}}, sort: {score: 1}


TextareaWithCharCounter: Documentation.

TextareaWithCharCounter: Documentation.

--- src/lib/components/textarea/TextareaWithCharCounter.coffee
+++ src/lib/components/textarea/TextareaWithCharCounter.coffee
@@ -8,6 +8,11 @@

   ###*
     Texarea component with built-in char counter like Twitter 140 char input.
+    By default char limit is 140. See `charLimit` option. Counter element may be
+    configured to be invisible. See `isCounterVisible` option. Default char
+    counter is a remaining counter also it may be an incremental counter. See
+    `showRemainingCount` option. By default the counter view will be appended to
+    `document.body`. To change it see `counterContainer` option.

     @constructor
     @export