Simplify tests with the receive helper

Simplify tests with the receive helper

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -53,18 +53,51 @@
         command: command
         payload: payload

-    receive = (expects, done) ->
+    messageMatches = (msg, expected) ->
+      return false unless msg.protocol is expected.protocol
+      return false unless msg.command is expected.command
+      true
+    getPacketSchema = (packet) ->
+      return "#{packet.protocol}/output/#{packet.command}"
+
+    receive = (expects, done, allowExtraPackets = false) ->
       listener = (message) ->
         # Basic validation
         chai.expect(message.utf8Data).to.be.a 'string'
         data = JSON.parse message.utf8Data
         chai.expect(data.protocol).to.be.a 'string'
         chai.expect(data.command).to.be.a 'string'
-        validateSchema data, "/#{data.protocol}/output/#{data.command}"
-        # Ignore messages we don't care about in context of the test
-        return unless (data.command is expects[0].command and data.protocol is expects[0].protocol)
+        # Validate all received packets against schema
+        validateSchema data, getPacketSchema data
+        # Don't ever expect payloads to return a secret
+        chai.expect(data.payload.secret, 'Payload should not contain secret').to.be.a 'undefined'
+        if allowExtraPackets and not messageMatches data, expects[0]
+          # Ignore messages we don't care about in context of the test
+          connection.once 'message', listener
+          return
+
         # Validate actual payload
         expected = expects.shift()
+        chai.expect(getPacketSchema(data)).to.equal getPacketSchema expected
+
+        # Clear values that can't be checked with deep equal. We already
+        # know they passed schema validation
+        if data.protocol is 'network'
+          if data.command is 'started'
+            delete data.payload.time
+            delete expected.payload.time
+          if data.command is 'stopped'
+            delete data.payload.time
+            delete expected.payload.time
+            delete data.payload.uptime
+            delete expected.payload.uptime
+        if data.command is 'error'
+          delete data.payload.stack
+          delete expected.payload.stack
+
+        # Don't ever expect payloads to return a secret
+        delete expected.payload.secret
+
         chai.expect(data.payload).to.eql expected.payload
         # Received all expected packets
         return done() unless expects.length
@@ -95,7 +128,7 @@
           expects = [
               protocol: 'graph'
               command: 'clear',
-              payload:
+              payload:
                 baseDir: path.resolve __dirname, '../'
                 id: 'foo'
                 main: true
@@ -118,7 +151,6 @@
                 metadata: {}
                 graph: 'foo'
           ]
-          #receive expects, done
           connection.once 'message', (message) ->
             data = JSON.parse message.utf8Data
             validateSchema data, '/graph/output/clear'
@@ -162,15 +194,7 @@
                 route: 5
               graph: 'foo'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-
-            validateSchema data, '/graph/output/addedge'
-            chai.expect(data.payload.src).to.eql expects[0].payload.src
-            chai.expect(data.payload.tgt).to.eql expects[0].payload.tgt
-
-            done()
-
+          receive expects, done
           send 'graph', 'addedge', expects[0].payload
       # describe 'adding an edge to a non-existent node', ->
       #   it 'should return an error', (done) ->
@@ -224,13 +248,7 @@
                   sort: 1
                 graph: 'foo'
             ]
-            connection.once 'message', (message) ->
-              data = JSON.parse message.utf8Data
-              validateSchema data, '/graph/output/changenode'
-              chai.expect(data.payload.metadata).to.eql expects[0].payload.metadata
-
-              done()
-
+            receive expects, done
             send 'graph', 'changenode', expects[0].payload

         describe 'to a node with existing metadata', ->
@@ -245,13 +263,7 @@
                   tag: 'awesome'
                 graph: 'foo'
             ]
-            connection.once 'message', (message) ->
-              data = JSON.parse message.utf8Data
-              validateSchema data, '/graph/output/changenode'
-              chai.expect(data.payload.metadata).to.eql expects[0].payload.metadata
-
-              done()
-
+            receive expects, done
             send 'graph', 'changenode',
               id: 'Drop1'
               metadata:
@@ -270,13 +282,7 @@
                   tag: 'awesome'
                 graph: 'foo'
             ]
-            connection.once 'message', (message) ->
-              data = JSON.parse message.utf8Data
-              validateSchema data, '/graph/output/changenode'
-              chai.expect(data.payload.metadata).to.eql expects[0].payload.metadata
-
-              done()
-
+            receive expects, done
             send 'graph', 'changenode',
               id: 'Drop1'
               metadata: {}
@@ -292,13 +298,7 @@
                 metadata: {}
                 graph: 'foo'
             ]
-            connection.once 'message', (message) ->
-              data = JSON.parse message.utf8Data
-              validateSchema data, '/graph/output/changenode'
-              chai.expect(data.payload.metadata).to.eql expects[0].payload.metadata
-
-              done()
-
+            receive expects, done
             send 'graph', 'changenode',
               id: 'Drop1'
               metadata:
@@ -320,14 +320,7 @@
               metadata: {}
               graph: 'foo'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/addinitial'
-            delete expects[0].payload.secret
-            chai.expect(data.payload).to.eql expects[0].payload
-
-            done()
-
+          receive expects, done
           send 'graph', 'addinitial', expects[0].payload

       describe 'removing a node', ->
@@ -350,19 +343,9 @@
               id: 'Drop1'
               graph: 'foo'
           ]
-          listener = (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, "/graph/output/#{data.command}"
-            return unless data.command is expects[0].command
-            expected = expects.shift()
-            chai.expect(data.payload).to.eql expected.payload
-            return if expects.length
-            # Received all packets
-            connection.removeListener 'message', listener
-            done()
-            return
-          connection.on 'message', listener
-
+          # Runtimes can send 'changeedge' and 'changenode' as part
+          # of the response for better fbp-graph/Journal behavior
+          receive expects, done, true
           send 'graph', 'removenode',
             id: 'Drop1'
             graph: 'foo'
@@ -380,13 +363,7 @@
                 port: 'in'
               graph: 'foo'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/removeinitial'
-            chai.expect(data.payload.src).to.eql expects[0].payload.src
-
-            done()
-
+          receive expects, done
           send 'graph', 'removeinitial',
             tgt:
               node: 'Repeat1'
@@ -403,14 +380,7 @@
               to: 'RepeatRenamed'
               graph: 'foo'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/renamenode'
-            delete expects[0].payload.secret
-            chai.expect(data.payload).to.eql expects[0].payload
-
-            done()
-
+          receive expects, done
           send 'graph', 'renamenode', expects[0].payload

       describe 'adding a node to a non-existent graph', ->
@@ -420,14 +390,8 @@
             command: 'error',
             payload:
               message: 'Requested graph not found'
-              stack: String
-          ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/error'
-
-            done()
-
+          ]
+          receive expects, done
           send 'graph', 'addnode',
             id: 'Repeat1'
             component: "#{collection}/Repeat"
@@ -440,14 +404,8 @@
             command: 'error',
             payload:
               message: 'No graph specified'
-              stack: String
-          ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/error'
-
-            done()
-
+          ]
+          receive expects, done
           send 'graph', 'addnode',
             id: 'Repeat1'
             component: "#{collection}/Repeat"
@@ -463,13 +421,7 @@
               public: 'in'
               port: 'in'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/addinport'
-            chai.expect(data.payload).to.eql expects[0].payload
-
-            done()
-
+          receive expects, done
           send 'graph', 'addinport',
             public: 'in'
             node: 'RepeatRenamed'
@@ -500,13 +452,7 @@
               port: 'out'
               public: 'out'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/addoutport'
-            chai.expect(data.payload).to.eql expects[0].payload
-
-            done()
-
+          receive expects, done
           send 'graph', 'addoutport',
             public: 'out'
             node: 'RepeatRenamed'
@@ -548,13 +494,7 @@
               graph: 'foo'
               public: 'out'
           ]
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/graph/output/removeoutport'
-            chai.expect(data.payload).to.eql expects[0].payload
-
-            done()
-
+          receive expects, done
           send 'graph', 'removeoutport',
             public: 'out'
             graph: 'foo'
@@ -666,23 +606,7 @@
                src: { node: 'Hello', port: 'out' }
                tgt: { node: 'World', port: 'in' }
           ]
-
-          listener = (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, "/#{data.protocol}/output/#{data.command}"
-            return unless data.command is expects[0].command
-            expected = expects.shift()
-            if data.command is 'started'
-              delete data.payload.time
-              delete expected.payload.time
-            chai.expect(data.payload).to.eql expected.payload
-            return if expects.length
-            # Received all packets
-            connection.removeListener 'message', listener
-            done()
-            return
-          connection.on 'message', listener
-
+          receive expects, done, true
           send 'network', 'start',
             graph: 'bar'

@@ -695,31 +619,33 @@
               running: false
               started: true
           ]
-          receive expects, done
+          receive expects, done, true
           send 'network', 'getstatus',
             graph: 'bar'
       describe 'on stopping the network', ->
         it 'should be stopped', (done) ->
-          connection.once 'message', (message) ->
-            data = JSON.parse message.utf8Data
-            validateSchema data, '/network/output/stopped'
-            chai.expect(data.payload.running).to.be.false
-
-            done()
-
-          send 'network', 'stop',
-            graph: 'bar'
-
-        it "should provide a 'stopped' status", (done) ->
-          expects = [
-            protocol: 'network'
-            command: 'status'
+          expects = [
+            protocol: 'network'
+            command: 'stopped'
             payload:
               graph: 'bar'
               running: false
               started: false
           ]
           receive expects, done
+          send 'network', 'stop',
+            graph: 'bar'
+
+        it "should provide a 'stopped' status", (done) ->
+          expects = [
+            protocol: 'network'
+            command: 'status'
+            payload:
+              graph: 'bar'
+              running: false
+              started: false
+          ]
+          receive expects, done, true
           send 'network', 'getstatus',
             graph: 'bar'
       # describe 'on console output', ->


Handle empty room.

Handle empty room.

--- src/aftership.coffee
+++ src/aftership.coffee
@@ -39,9 +39,10 @@
   robot.router.post '/aftership', (req, res) ->
     query = querystring.parse(url.parse(req.url).query)
     data   = if req.body.payload? then JSON.parse req.body.payload else req.body
-    room   = data.msg.custom_fields.room
     secret = query.secret
     return res.status(403).send("NOK") if secret != process.env.AFTERSHIP_SECRET
+    room   = data.msg.custom_fields?.room
+    return res.status(400) if not room

     checkpoints = data.msg.checkpoints
     msgs = checkpoints.map (checkpoint) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/animation.coffee
+++ src/animation.coffee
@@ -19,8 +19,7 @@

       frames: -> return activeAnimation.frames

-      update: ->
-        advanceFrame(activeAnimation)
+      update: -> return advanceFrame(activeAnimation)

       active: (name) ->
         if (name != undefined)


Separate the css from keyframe generation

--- src/animation.coffee
+++ src/animation.coffee
@@ -29,8 +29,8 @@

 	AnimationProperties: [
 		"view", "properties", "curve", "time",
-		"origin", "tolerance", "precision", "modifiers", "limits"
-		"debug", "profile", "callback"
+		"origin", "tolerance", "precision", "modifiers",
+		"limits", "debug", "profile", "callback"
 	]
 	AnimatableCSSProperties: {
 		opacity: "",
@@ -186,11 +186,12 @@

 		########################################################
 		# Finalize
-
-		if @debug
-			endTime = new Date().getTime() - startTime
-			console.log "Animation[#{@animationId}].setupTime = #{endTime}ms"
-			console.log "Animation[#{@animationId}].totalTime = #{utils.round @totalTime, 0}ms"
+
+		@view.once "webkitAnimationStart", =>
+			if @debug
+				endTime = new Date().getTime() - startTime
+				console.log "Animation[#{@animationId}].setupTime = #{endTime}ms"
+				console.log "Animation[#{@animationId}].totalTime = #{utils.round @totalTime, 0}ms"

 		console.profileEnd @animationName if @profile

@@ -273,58 +274,66 @@
 		@callback? @
 		@emit "end"

-	_css: ->
-
-		# Build the css for the keyframe animation. I wish there was a nicer
-		# way to do this, but this will work for now.
+	_keyFrames: ->
+
+		# Build keyframes based on our values

 		stepIncrement = 0
 		stepDelta = 100 / (@curveValues.length - 1)

+		deltas = @_deltas()
+		keyFrames = {}
+
+		for curveValue in @curveValues
+
+			position = stepIncrement * stepDelta
+			position = utils.round position, config.roundingDecimals
+
+			currentKeyFrame = {}
+
+			for propertyName of @propertiesA
+				currentKeyFrame[propertyName] = curveValue * deltas[propertyName] + @propertiesA[propertyName]
+
+			keyFrames[position] = currentKeyFrame
+
+			stepIncrement++
+
+		keyFrames
+
+	_css: ->
+
+		# Create the css from a set of keyframes
+
+		keyFrames = @_keyFrames()
+
 		cssString = []
 		cssString.push "@-webkit-keyframes #{@animationName} {\n"

-		deltas = @_deltas()
-
-		# We define this object outside of the loop to re-use it.
-		# In theory this should help a bit with perfomance, in practise
-		# I'm too lazy to prove it.
-		m = new Matrix()
-
-		for springValue in @curveValues
-
-			position = stepIncrement * stepDelta
-
-			cssString.push "\t#{utils.round position, config.roundingDecimals}%\t{ -webkit-transform: "
+		matrix = new Matrix()
+
+		for position, values of keyFrames
+
+			cssString.push "\t#{position}%\t{ -webkit-transform: "

 			# Add the matrix based values
 			for propertyName in @AnimatableMatrixProperties
-
-				# Calculate the clean spring value for this point
-				value = springValue * deltas[propertyName] + @propertiesA[propertyName]
-
-				# Modify the value if we have a modifier set up. This let's us do
-				# special stuff like drop the friction once we run into the scroll
-				# bounds for a scrollview.
-				if @modifiers?[propertyName]?
-					value = @modifiers[propertyName](value)
-
-				m[propertyName] = value
-
-			cssString.push m.matrix().cssValues() + "; "
-
+				if values.hasOwnProperty propertyName
+					matrix[propertyName] = values[propertyName]
+				else
+					matrix[propertyName] = @view[propertyName]
+
+			cssString.push matrix.matrix().cssValues() + "; "
+
+			# Add the css based values
 			for propertyName, unit of @AnimatableCSSProperties
-				continue if not @propertiesA.hasOwnProperty propertyName
-				value = springValue * deltas[propertyName] + @propertiesA[propertyName]
-				cssString.push "#{propertyName}:#{ utils.round value, config.roundingDecimals}#{unit}; "
+				continue if not values.hasOwnProperty propertyName
+				cssString.push "#{propertyName}:#{ utils.round values[propertyName], config.roundingDecimals}#{unit}; "

 			cssString.push "}\n"

-			stepIncrement++
-
 		cssString.push "}\n"
 		cssString.join ""
-
+
 	_deltas: ->

 		deltas = {}


Terminal: preCondition temporary fix

Terminal: preCondition temporary fix

--- client/Terminal/AppController.coffee
+++ client/Terminal/AppController.coffee
@@ -32,7 +32,7 @@
           vmController.info vmName, KD.utils.getTimedOutCallback (err, vm, info)->
             cb  info?.state is 'RUNNING', {vmName, info}
           , ->
-            cb yes
+            cb  no
             unless KD.isGuest()
               KD.logToExternal "failed to fetch vminfo, couldn't open terminal"
           , 2500


Terminal: minor removed log

Terminal: minor removed log

--- client/Terminal/AppController.coffee
+++ client/Terminal/AppController.coffee
@@ -58,7 +58,6 @@
     @getView().on 'TerminalFailed', @bound 'checkOSKiteStatus'

     # sometimes terminal is so fast we don't even need to ask oskite status
-    log 'alreadyStarted', alreadyStarted
     @checkOSKiteStatus()  unless alreadyStarted

   checkOSKiteStatus:-> @askOSKiteStatus @bound 'tellOSKiteStatus'


Remove Console.log

Remove Console.log

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -420,7 +420,6 @@

         _message = _.clone @state
         @state.isSaving = true
-        console.log 'SAVE', _message.html
         MessageActionCreator.send _message, (error, message) =>
             delete @state.isSaving
             if error? or not message?


Muda evento de window para this

Muda evento de window para this

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -359,7 +359,7 @@
 				# console.log 'Adding SKU to cart:', sku
 				$.get($.skuSelector.getAddUrlForSku(sku, 1, 1, productData.salesChannel, false))
 				.done (data) ->
-					$(window).trigger 'productAddedToCart'
+					$(this).trigger 'productAddedToCart'
 				.fail (jqXHR, status) ->
 					window.location.href = $.skuSelector.getAddUrlForSku(sku, 1, productData.salesChannel)
 				return false


Corrige bug de trigger de eventos em elementos inexistentes

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -334,7 +334,7 @@
 		throw new Error('Sku Selector was activated on 0 elements')

 	this.addClass('sku-selector-loading')
-	context = this
+	context = $(this)

 	# Gather options
 	domOptions = this.data()
@@ -348,18 +348,18 @@
 	renderer = new SkuSelectorRenderer(this, options, selector)

 	# Handler for the buy button
-	buyButtonHandler = (event) =>
+	buyButtonHandler = (event) ->
 		selectedSku = selector.findSelectedSku()
 		if selectedSku
 			if options.confirmBuy
 				event.preventDefault()
 				renderer.showConfirmButton(selectedSku)
 			else
-				$(this).trigger 'vtex.modal.hide'
+				context.trigger 'vtex.modal.hide'
 				# console.log 'Adding SKU to cart:', sku
 				$.get($.skuSelector.getAddUrlForSku(selectedSku.sku, 1, 1, productData.salesChannel, false))
 				.done (data) ->
-					$(this).trigger 'productAddedToCart'
+					$(window).trigger 'productAddedToCart'
 				.fail (jqXHR, status) ->
 					window.location.href = $.skuSelector.getAddUrlForSku(selectedSku.sku, 1, productData.salesChannel)
 				return false


Move loginDialog into own creation function

--- client/lanes/access/Extension.coffee
+++ client/lanes/access/Extension.coffee
@@ -16,7 +16,4 @@
             Lanes.current_user.access_data = data.access

     onAvailable: (view)->
-        this.login_dialog = new Lanes.Access.LoginDialog({ parent: view })
-
-        # screen=Lanes.Models.Screens.all.get("user-management")
-        # screen.display(view.viewport)
+        Lanes.Access.createLoginDialog(view)


Added current workaround for commonjs

Added current workaround for commonjs

--- src/bodyguard.coffee
+++ src/bodyguard.coffee
@@ -22,4 +22,8 @@

   bodyguard.silent = true

-  bodyguard+  bodyguard
+
+# Our current CommonJS workaround
+if udefine.env.commonjs
+  udefine.require 'bodyguard', (bodyguard) -> module.exports = bodyguard


SplitView: some test code commented out

SplitView: some test code commented out

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -313,8 +313,8 @@
       view.detach()
       view.unsetParent()
       options.views = [view]
-    options.colored = yes
-    options.type    = ['vertical','horizontal'][KD.utils.getRandomNumber(2)-1]
+    # options.colored = yes
+    # options.type    = ['vertical','horizontal'][KD.utils.getRandomNumber(2)-1]

     {splitClass}    = @getOptions()
     split           = new (splitClass or KDSplitView) options


activity feed: wrap fetchSomeActivity in timed callback

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -210,17 +210,21 @@
       sort        :
         createdAt : -1

-    KD.remote.api.CActivity.some selector, options, (err, activities) =>
-      if err then warn err
-      else
-        # FIXME: SY
-        # if it is exact 20 there may be other items
-        # put a separator and check for new items in between
-        if activities.length is 20
-          warn "put a separator in between new and old activities"
-
-        @activitiesArrived activities.reverse()
+    KD.remote.api.CActivity.some selector, options,\
+      KD.utils.getTimedOutCallback (err, activities) =>
+        if err then warn err
+        else
+          # FIXME: SY
+          # if it is exact 20 there may be other items
+          # put a separator and check for new items in between
+          if activities.length is 20
+            warn "put a separator in between new and old activities"
+
+          @activitiesArrived activities.reverse()
+          @isLoading = no
+      , ->
         @isLoading = no
+        log "fetchSomeActivities timeout reached"

   fetchCachedActivity:(options = {}, callback)->


Add FavouriteTopic collection to the user

--- core/app/backbone/models/user.coffee
+++ core/app/backbone/models/user.coffee
@@ -3,6 +3,7 @@
     @channels = []
     @followers = new Followers([], user: @)
     @following = new Following([], user: @)
+    @favourite_topics = new FavouriteTopics([])

   setChannels: (channels) -> @channels = channels


修复 rails4 render json 会加上controller 名称bug

--- app/assets/javascripts/china_city/jquery.china_city.js.coffee
+++ app/assets/javascripts/china_city/jquery.china_city.js.coffee
@@ -8,6 +8,7 @@
         $("option:gt(0)", next_selects).remove()
         if next_selects.first()[0] and $this.val() # init next child
           $.get "/china_city/#{$(@).val()}", (data) ->
+            data = data.data if data.data?
             next_selects.first()[0].options.add(new Option(option[0], option[1])) for option in data
             # init value after data completed.
             next_selects.trigger('china_city:load_data_completed');


Dashboard: added two class that extend InlineImageSettings to be used as a itemClass variable in GroupSettings.

--- client/Dashboard/views/grouplogosettings.coffee
+++ client/Dashboard/views/grouplogosettings.coffee
@@ -82,3 +82,14 @@

+class GroupLogoSettings extends InlineImageSettings
+  constructor: (options = {}, data) ->
+    options = uploaderTitle: "Drop your 55x55 logo here!"
+    super options, data
+
+class GroupFaviconSettings extends InlineImageSettings
+  constructor: (options = {}, data) ->
+    options         =
+      type          : "favicon"
+      uploaderTitle : "Drop your 5x5 favicon here!"
+    super options, data


Fixed status update preview

Fixed status update preview

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -302,13 +302,15 @@
     previewData =
       on            : ->
       watch         : ->
-      account       : KD.whoami()
+      account       : KD.whoami().bongo_
       body          : value
       typeConstant  : 'post'
-      createdAt     : '2014-05-02T12:40:36.540356-07:00'
+      interactions  :
+        like        :
+          actorsCount : 0
+          actorsPreview : []
       meta          :
-        likes       : 0
-        createdAt   : '2014-05-02T12:40:36.540356-07:00'
+        createdAt   : new Date

     if not @preview


activity: set filter of message pane only if it's different

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -374,7 +374,9 @@
     @populate()

-  setFilter: (@currentFilter) ->
+  setFilter: (filter) ->
+
+    return  if @currentFilter is filter

     @filterLinks.selectFilter @currentFilter
     @populate()


Fix tests

Fix tests

--- client/app/actions/layout_action_creator.coffee
+++ client/app/actions/layout_action_creator.coffee
@@ -49,6 +49,10 @@

         AppDispatcher.handleViewAction
             type: type
+
+    minimizePreview: ->
+        AppDispatcher.handleViewAction
+            type: ActionTypes.MINIMIZE_PREVIEW_PANE

     refresh: ->
         AppDispatcher.handleViewAction


PaymentController: do not call deprecated paymentApi.subscriptions

--- client/app/lib/payment/paymentcontroller.coffee
+++ client/app/lib/payment/paymentcontroller.coffee
@@ -52,12 +52,7 @@

   subscriptions: (callback) ->

-    # return plan as 'koding' on default environment
-    if globals.config.environment is 'default'
-      # checkout servers/models/computeproviders/plans.coffee
-      return callback null, { planTitle: 'koding' }
-
-    @api().subscriptions {}, callback
+    callback null, { planTitle: 'koding' }

   invoices: (callback) -> @api().invoices {}, callback


Filter empty values in scale_color domain for visualization legend

--- app/frontend/javascripts/views/visualization-legend.js.coffee
+++ app/frontend/javascripts/views/visualization-legend.js.coffee
@@ -42,16 +42,21 @@
     else
       @$el.find('.visualization-graph-legend-size').hide()

+    # get scale color domain filtering empty values
+    color_domain = scale_color.domain().filter (d) -> !Number.isNaN(d) and d != null and d != ''
+
     # Setup color legend
-    if ( @parameters.nodesColor == 'qualitative' or @parameters.nodesColor == 'quantitative' ) and scale_color.domain().length > 1
+    if ( @parameters.nodesColor == 'qualitative' or @parameters.nodesColor == 'quantitative' ) and color_domain.length > 1
+
+      console.log color_domain
+
+      # Avoid quantitative scales with same values
+      if color_domain.length == 2 and color_domain[0] == color_domain[1]
+        @$el.find('.visualization-graph-legend-color').hide()
+        return

       # add class color to element
       @$el.addClass 'color'
-
-      # Avoid quantitative scales with same values
-      if scale_color.domain().length == 2 and scale_color.domain()[0] == scale_color.domain()[1]
-        @$el.find('.visualization-graph-legend-color').hide()
-        return

       # Create legend color group
       legend_color = @$el.find('.visualization-graph-legend-color').show()
@@ -61,32 +66,29 @@
       # Add legend color title
       legend_color.find('.visualization-graph-legend-title').html( @parameters.nodesColorColumn.replace('_',' ').trim().capitalize() )

-      items = scale_color.domain()
-
       # Insert values inside domain for quantitative scales
       if @parameters.nodesColor == 'quantitative'
-        items = items.sort (a, b) -> return a - b
-        steps = (items[0] - items[1]) / 5
-        min = items.pop()
-        items.push @formatNumber(items[0]-steps)
-        items.push @formatNumber(items[1]-steps)
-        items.push @formatNumber(items[2]-steps)
-        items.push @formatNumber(items[3]-steps)
-        items.push min
+        color_domain = color_domain.sort (a, b) -> return a - b
+        steps = (color_domain[0] - color_domain[1]) / 5
+        min = color_domain.pop()
+        color_domain.push @formatNumber(color_domain[0]-steps)
+        color_domain.push @formatNumber(color_domain[1]-steps)
+        color_domain.push @formatNumber(color_domain[2]-steps)
+        color_domain.push @formatNumber(color_domain[3]-steps)
+        color_domain.push min

       # loop through all colors
-      items.forEach (item, i) =>
-        if item != null and item != ''
-          # create legend item
-          legend_item = $('

')
-          # add circle & amount to legend item
-          legend_item_color = $('')
-          legend_item_color.css('background-color': scale_color(item) )
-          legend_item.append legend_item_color
-          # skip first item label if scale is quantitative
-          if i > 0 || @parameters.nodesColor == 'qualitative'
-            legend_item.append item
-          legend_color.find('ul').append legend_item
+      color_domain.forEach (item, i) =>
+        # create legend item
+        legend_item = $('

')
+        # add circle & amount to legend item
+        legend_item_color = $('')
+        legend_item_color.css('background-color': scale_color(item) )
+        legend_item.append legend_item_color
+        # skip first item label if scale is quantitative
+        if i > 0 || @parameters.nodesColor == 'qualitative'
+          legend_item.append item
+        legend_color.find('ul').append legend_item
     else
       @$el.find('.visualization-graph-legend-color').hide()


Put damage modifiers/ch in the proper place.

--- server/move.coffee
+++ server/move.coffee
@@ -11,8 +11,9 @@
   # if the move attacks all opponents
   execute: (battle, user, target) =>
     damage = @baseDamage(battle, user, target)
+    damage = damage * 2  if @isCriticalHit(battle, user, target)
+    damage = Math.floor(((100 - battle.rng.randInt(0, 15)) * damage) / 100)
     damage = Math.round((@stab(user) * damage) / 0x1000)
-    damage = Math.floor(((100 - battle.rng.randInt(0, 15)) * damage) / 100)
     damage = Math.max(damage, 1)
     @damage(user, target, damage)

@@ -57,7 +58,6 @@
   baseDamage: (battle, attacker, defender) =>
     floor = Math.floor
     baseDamage = floor((2 * attacker.level) / 5 + 2)
-    baseDamage *= 2  if @isCriticalHit(battle, attacker, defender)
     # TODO: Apply variable base power
     baseDamage *= @basePower()
     baseDamage *= attacker.stat(whichAttackStat(@spectra))


Fix broken localstorage test

Fix broken localstorage test

--- Web/js/storage.coffee
+++ Web/js/storage.coffee
@@ -2,10 +2,10 @@

 store = window.localStorage
 if store?
-	datum = Date.now()
+	datum = Date.now() + "a"
 	store.setItem 'localStorage test', datum
 	store = null unless datum == store.getItem 'localStorage test'
-	store?.deleteItem 'localStorage test'
+	store?.removeItem 'localStorage test'

 unless store?
 	s = {}


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -25,7 +25,7 @@
   # => 2.5 # half the player's width

   player.I.y
-  # => 311.5 # The default App
+  # => 311.5 # The default App.height ()


Update config.cson

Update config.cson

--- dotfiles/.atom/config.cson
+++ dotfiles/.atom/config.cson
@@ -1,4 +1,6 @@
 "*":
+  "atom-beautify":
+    general: {}
   "atom-clock": {}
   "atom-material-ui":
     colors:


Add a subscript operator to the glsl api

--- src/generator/compile.glsl.api.coffee
+++ src/generator/compile.glsl.api.coffee
@@ -1,6 +1,12 @@
 # The GLSL builder API (This builder produces strings rather than a tree)

 glsl =
+  subscript: (a, index) ->
+    if Array.isArray a
+      return a[index]
+    else
+      "#{a}[#{index}]"
+
   mul: (a, b) ->
     if typeof a == 'number' and typeof b == 'number'
       a * b


[ #316179 ] Loading indicator is not hidden after a retry

--- src/resources-packaged/ops/javascript/orbeon/xforms/LoadingIndicator.coffee
+++ src/resources-packaged/ops/javascript/orbeon/xforms/LoadingIndicator.coffee
@@ -49,15 +49,16 @@
                     @shownCounter++

         # When an Ajax call ends, we might want to hide the indicator
-        Events.ajaxResponseProcessedEvent.subscribe =>
-            if @nextConnectShow
-                # Defer hiding the indicator to give a chance to next request to start, so we don't flash the indicator
-                _.defer =>
-                    @shownCounter--
-                    @hide() if @shownCounter == 0
+        requestEnded = =>
+            # Defer hiding the indicator to give a chance to next request to start, so we don't flash the indicator
+            _.defer =>
+                @shownCounter--
+                @hide() if @shownCounter == 0
             # Reset show and message
             @nextConnectShow = true
             @nextConnectMessage = DEFAULT_LOADING_TEXT
+        Events.ajaxResponseProcessedEvent.subscribe requestEnded
+        Connect.failureEvent.subscribe requestEnded

     setNextConnectProgressShown: (shown) -> @nextConnectShow = shown
     setNextConnectProgressMessage: (message) -> @nextConnectMessage = message


Minor: formatting

Minor: formatting

--- src/resources-packaged/ops/javascript/orbeon/xforms/LoadingIndicator.coffee
+++ src/resources-packaged/ops/javascript/orbeon/xforms/LoadingIndicator.coffee
@@ -28,7 +28,7 @@
         @loadingSpan = _.detect @form.childNodes, (node) -> YD.hasClass node, "xforms-loading-loading"
         @loadingSpan.style.display = "none"

-        # Differ creation of the overlay to the first time we need it,
+        # Differ creation of the overlay to the first time we need it
         @loadingOverlay = null
         # On scroll or resize, move the overlay so it stays visible
         Overlay.windowScrollEvent.subscribe => @_updateLoadingPosition()


optimized tracking

optimized tracking

--- server-transform.coffee
+++ server-transform.coffee
@@ -32,22 +32,22 @@
       return doc
     computations = {}

+    startTracking = (doc) ->
+      computations[doc._id].stop() if computations[doc._id]?
+      computations[doc._id] = Tracker.autorun ->
+        publication.changed collectionName, doc._id, transform(doc)
+
     handle = cursor.observe
       added: (doc) ->
-        publication.added collectionName, doc._id, transform(doc)
+        publication.added collectionName, doc._id, doc
+        startTracking doc
+      changed: (doc) ->
+        startTracking doc
+      removed: (doc) ->
+        if computations[doc._id]?
+          computations[doc._id].stop()
+          delete computations[doc._id]

-        computations[doc._id].stop() if computations[doc._id]?
-        computations[doc._id] = Tracker.autorun ->
-          publication.changed collectionName, doc._id, transform(doc)
-
-      changed: (doc) ->
-        publication.changed collectionName, doc._id, transform(doc)
-
-        computations[doc._id].stop() if computations[doc._id]?
-        computations[doc._id] = Tracker.autorun ->
-          publication.changed collectionName, doc._id, transform(doc)
-
-      removed: (doc) ->
         publication.removed collectionName, doc._id

     publication.onStop ->


Ram usage bar: Minor fixes

Ram usage bar: Minor fixes

--- components/progressbar/KDProgressBarView.coffee
+++ components/progressbar/KDProgressBarView.coffee
@@ -1,6 +1,6 @@
 class KDProgressBarView extends KDCustomHTMLView
   constructor:(options = {})->
-    options.cssClass += " progressbar-container"
+    options.cssClass = KD.utils.curryCssClass "progressbar-container"

     super options

@@ -17,6 +17,5 @@

   updateBar:(value, unit, label)->
     @bar.setWidth value, unit
-    window.bar = @bar
     @darkLabel.updatePartial "#{label} "
     @lightLabel.updatePartial "#{label} "


fixes

fixes

--- scripts/slugs.coffee
+++ scripts/slugs.coffee
@@ -18,6 +18,8 @@
     return next(null, team) if team.peopleIds.length is 0  # skip empty
     # return next(null, team) if team.slug is team.slugBase
     # return next(null, team) # don't modify right now
+
+    team.setup ?= {}

     if team.setup.status == "ready"
       return next(null, team)
@@ -56,8 +58,6 @@
       "red-hot-pink-daisy-s": "red-hot-pink-daisy"

-
-
     if team.name == "ヽ( ´¬`)ノ" #special that there was no slug previously
       old = team.slug
       team.slug = 'waving'


Improve stat charts

Improve stat charts

--- scripts/stats.coffee
+++ scripts/stats.coffee
@@ -1,19 +1,4 @@
 window.activateStats = () ->
-    applyScale = (min, max) ->
-        if max > 2000
-            return Math.ceil(10 / max - min)
-        if max > 1000
-            return 50
-        if max > 500
-            return 25
-        if max > 200
-            return 20
-        if max > 100
-            return 10
-        if max > 50
-            return 5
-        return 1
-
     months = ['Janurary', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
     colors = [
         ['rgba(222,93,93,0.7)', 'rgba(179,74,74,1)'],
@@ -29,16 +14,15 @@
             chart = document.getElementById('downloads-over-time')
             labels = []
             entries = []
-            max = 0
             color = 0
             key = []
-            for i in [1..30]
+            for i in [0...30]
                 a = new Date(thirty_days_ago.getTime())
                 a.setDate(a.getDate() + i)
                 labels.push("#{months[a.getMonth()]} #{a.getDate()}")
             for v in window.versions
                 data = []
-                for i in [1..30]
+                for i in [0...30]
                     a = new Date(thirty_days_ago.getTime())
                     a.setDate(a.getDate() + i)
                     events = _.filter(download_stats, (d) ->
@@ -51,8 +35,6 @@
                             return m + e.downloads
                         , 0)
                     data.push(downloads)
-                    if downloads > max
-                        max = downloads
                 if _.some(data, (d) -> d != 0)
                     entries.push({
                         fillColor: colors[color][0],
@@ -65,17 +47,11 @@
                     color++
                     if color >= colors.length
                         color = 0
-            jump = applyScale(0, max)
             entries.reverse()
             key.reverse()
             new Chart(chart.getContext("2d")).Line({
                 labels : labels,
                 datasets : entries
-            }, {
-                scaleOverride: true,
-                scaleSteps: max / jump,
-                scaleStepWidth: jump,
-                scaleStartValue: 0
             })
             # Create key
             keyUI = document.getElementById('downloads-over-time-key')
@@ -96,8 +72,6 @@
             chart = document.getElementById('followers-over-time')
             labels = []
             entries = []
-            min = 0
-            max = 0
             color = 0
             for i in [0..30]
                 a = new Date(thirty_days_ago.getTime())
@@ -117,10 +91,6 @@
                         return m + e.delta
                     , 0)
                 data.push(delta)
-                if delta > max
-                    max = delta
-                if delta < min
-                    min = delta
             if _.some(data, (d) -> d != 0)
                 entries.push({
                     fillColor: colors[color][0],
@@ -132,15 +102,9 @@
                 color++
                 if color >= colors.length
                     color = 0
-            jump = 1 # TODO: Consider making this a more sensible value
             entries.reverse()
             new Chart(chart.getContext("2d")).Line({
                 labels : labels,
                 datasets : entries
-            }, {
-                scaleOverride: true,
-                scaleSteps: max / jump,
-                scaleStepWidth: jump,
-                scaleStartValue: min
             })
         )()


Make show answer button toggleable

Make show answer button toggleable

--- templates/coffee/src/modules/problem.coffee
+++ templates/coffee/src/modules/problem.coffee
@@ -42,16 +42,18 @@
       Logger.log 'problem_show', problem: @id
       $.postWithPrefix "/modx/problem/#{@id}/problem_show", (response) =>
         $.each response, (key, value) =>
-          if $.isArray(data[key])
-            $.each data[key], (index, answer_index) =>
-              @$("#label[for='input_#{key}_#{data[key][answer_index]}']").attr
+          if $.isArray(value)
+            $.each value, (index, answer_index) =>
+              @$("#label[for='input_#{key}_#{value[answer_index]}']").attr
                 correct_answer: 'true'
           @$("#answer_#{key}").text(value)
+        @$('.show').val 'Hide Answer'
         @element.addClass 'showed'
     else
       @$('[id^=answer_]').text('')
       @$('[correct_answer]').attr(correct_answer: null)
-
+      @element.removeClass 'showed'
+      @$('.show').val 'Show Answer'

   save: =>
     Logger.log 'problem_save', @answers


Simplified require

Simplified require

--- test/fixtures/general/src/app.coffee
+++ test/fixtures/general/src/app.coffee
@@ -1,4 +1,4 @@
-Middleware = require '../../../../src/index'
+Middleware = require '../../../..'

 # Approach used to AVOID subtle-close of the window, in order to make it
 # possible to collect coverage analysis before closing the browser


Who needs newlines in the middle of functions?

Not this guy

--- app/assets/javascripts/latlong_map.coffee
+++ app/assets/javascripts/latlong_map.coffee
@@ -7,7 +7,6 @@
   map = new google.maps.Map $('.map')[0], { zoom: 15, center: latLng }
   if placeInitialMarker
     placeMarker map, latLng
-
   google.maps.event.addListener map, 'click', (event) ->
     placeMarker map, event.latLng
     fillLatLngFields event.latLng


Fix Directory.contains method error on Windows

Fixes #20

--- src/directory.coffee
+++ src/directory.coffee
@@ -66,7 +66,7 @@
     return false unless pathToCheck

     # Normalize forward slashes to back slashes on windows
-    fullPath = fullPath.replace(/\//g, '\\') if process.platform is 'win32'
+    pathToCheck = pathToCheck.replace(/\//g, '\\') if process.platform is 'win32'

     if fs.isCaseInsensitive()
       directoryPath = @lowerCasePath


Fix innerHTML assignment bug in IE

Fix innerHTML assignment bug in IE

--- app/assets/javascripts/livefyre.js.coffee
+++ app/assets/javascripts/livefyre.js.coffee
@@ -7,8 +7,7 @@
 (->
   __loadedScripts = []
   fjs = null
-  load = (source, id, content, options) ->
-    content = null if !content
+  load = (source, id, options) ->
     return if (document.getElementById(id))
     return if __loadedScripts[id]
     __loadedScripts[id] = true
@@ -17,7 +16,6 @@
     js.id = id
     js.async = true
     js.src = source
-    js.innerHTML = content
     js[k] = v for k, v of options if options

     fjs.parentNode.insertBefore(js, fjs)
@@ -100,7 +98,7 @@
             window.console.log "Error logging in:", e if window.console

     unless options.manualLoad
-      element = load "http://#{options.root}/wjs/v3.0/javascripts/livefyre.js", null, null, {"data-lf-domain": options.network}
+      element = load "http://#{options.root}/wjs/v3.0/javascripts/livefyre.js", null, {"data-lf-domain": options.network}
     returnable

   else


#96 Fix cannot read property name of null

--- app/assets/javascripts/oxalis/view.coffee
+++ app/assets/javascripts/oxalis/view.coffee
@@ -221,7 +221,7 @@
       if treeId != lastTreeId
         newContent.appendChild((
           $('<li>').append($('<i>', {"class": "icon-sitemap"}),
-          $('<span>', {"data-treeid": treeId, "text": @model.cellTracing.getTree(treeId).name})))[0])
+          $('<span>', {"data-treeid": treeId, "text": @model.cellTracing.getTree(treeId)?.name})))[0])
         lastTreeId = treeId
       newContent.appendChild((
         $('<li>').append($('<i>', {"class": "icon-angle-right"}),


Don't include events.coffee, since it's just for demo purposes

--- generators/app/templates/scripts/events.coffee
+++ generators/app/templates/scripts/events.coffee
@@ -1,19 +0,0 @@
-# Description:
-#   Event system related utilities
-#
-# Commands:
-#   hubot fake event <event> - Triggers the <event> event for debugging reasons
-#
-# Events:
-#   debug - {user: <user object to send message to>}
-
-util = require 'util'
-
-module.exports = (robot) ->
-
-  robot.respond /FAKE EVENT (.*)/i, (msg) ->
-    msg.send "fake event '#{msg.match[1]}' triggered"
-    robot.emit msg.match[1], {user: msg.message.user}
-
-  robot.on 'debug', (event) ->
-    robot.send event.user, util.inspect event


Add Addresses To Errors In Kitchen Sink

Add Addresses To Errors In Kitchen Sink

Add address to errors in kitchen sink because this will show how to do
set errors in a nested fieldset

--- examples/kitchen-sink/coffeescript/kitchen-sink.coffee
+++ examples/kitchen-sink/coffeescript/kitchen-sink.coffee
@@ -35,6 +35,7 @@
     errors:
       base: ["Test Error", "Moo"]
       email: ["This Error is a Test"]
+      addresses: [{address: "This is a Nested Field Error Test"}]
     saves:
       select_example: true
       time_of_day: true


Adding an example of displaySelections

Adding an example of displaySelections

--- examples/kitchen-sink/coffeescript/kitchen-sink.coffee
+++ examples/kitchen-sink/coffeescript/kitchen-sink.coffee
@@ -2,7 +2,7 @@

 React = require "react/addons"
 {frig} = require("frig").dsl
-{div, h2, h3, h4, img} = React.DOM
+{div, h2, h3, h4, img, ul, li} = React.DOM

 AccountForm = React.createClass
   mixins: [
@@ -98,6 +98,30 @@
           ),

         div className: "row",
+          f.input("customSelectionRendering",
+            type: "typeahead",
+            multiple: true,
+            displaySelections: false,
+            options: [
+              # The 3 formats for options
+              # 1. An object with a label and a value (perfered)
+              {label: "Stuff", value: "stuff-value"},
+              # 2. An array of the [value, label]
+              ["thing-value", "Things"],
+              # 3. A string. For scenarios where the label equals the value.
+              "why not both?"
+            ]
+          ),
+          # Rendering the selection independently of the typeahead for
+          # customization purposes
+          div className: "col-xs-12",
+            h4 {}, "Custom Selections"
+            ul {},
+              (@state.account.customSelectionRendering||[]).map (selection) =>
+                li {key: "o-#{selection}"}, "I choose #{selection}"
+            if !@state.account.customSelectionRendering?
+              div {}, "No Selection"
+        div className: "row",
           f.input "password", xs: 6
           f.input "passwordConfirmation", xs: 6


Include owner information in output

Include owner information in output

--- src/flips-cli.coffee
+++ src/flips-cli.coffee
@@ -26,7 +26,8 @@
         day = "#{date.getDate()}"
         day = "0#{day}" if date.length is 1
         date = "#{month}/#{day}/#{date.getFullYear()}"
-        console.log date, "$#{humanize.intcomma(Math.floor(profit))}"
+        owner = form.getOwner()
+        console.log date, "$#{humanize.intcomma(Math.floor(profit))} #{owner.name} #{owner.title}"
     callback()

 module.exports =


Add messageRoom test

Add messageRoom test

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -35,7 +35,7 @@

   robot.brain.on "loaded", =>
     start(robot, game)
-
+
   # combo hear and respond, prepends ^ to hear regex
   # good for allowing same commands in room and DM
   robot.hearspond = (regex, cb) ->
@@ -43,6 +43,10 @@
     args = Array.prototype.slice(arguments, 1)
     this.hear.call(this, alt, cb)
     this.respond.call(this, regex, cb)
+
+  robot.respond /message ([^\s]+)/i, (res) ->
+    room = res.match[1];
+    robot.messageRoom(room, 'here is a message');

   robot.hearspond /cah db$/i, (res) ->
     res.reply JSON.stringify game.db


Bug fix in gravatarHash generator.

Bug fix in gravatarHash generator.

--- server/person.coffee
+++ server/person.coffee
@@ -22,7 +22,7 @@
         source = fields.person?.displayName unless source

         return [null, undefined] unless fields.person?._id and source
-        [fields.person._id, crypto.createHash('md5').update(address).digest('hex')]
+        [fields.person._id, crypto.createHash('md5').update(source).digest('hex')]

       fields


Code style.

Code style.

--- server/search.coffee
+++ server/search.coffee
@@ -17,13 +17,13 @@

   # TODO: Not reactive, can we make it?
   person = Person.documents.findOne
-      _id: @personId
-    ,
-      fields:
-        # _id field is implicitly added
-        isAdmin: 1
-        inGroups: 1
-        library: 1
+    _id: @personId
+  ,
+    fields:
+      # _id field is implicitly added
+      isAdmin: 1
+      inGroups: 1
+      library: 1

   findQuery = Publication.requireReadAccessSelector person, findQuery


:memo: doc new methods

:memo: doc new methods

--- src/installer.coffee
+++ src/installer.coffee
@@ -220,6 +220,11 @@

             async.waterfall(commands, callback)

+  # Install all the package dependencies found in the package.json file.
+  #
+  #  * options: The installation options
+  #  * callback: The callback function to invoke when done with an error as the
+  #    first argument.
   installPackageDependencies: (options, callback) ->
     process.stdout.write 'Installing packages '

@@ -248,6 +253,7 @@

     async.waterfall commands, callback

+  # Get all package dependency name and versions.
   getPackageDependencies: ->
     try
       metadata = fs.readFileSync('package.json', 'utf8')


Remove duplicated key

Remove duplicated key

--- server_config.coffee
+++ server_config.coffee
@@ -53,7 +53,6 @@
   username: process.env.COCO_MAIL_SERVICE_USERNAME or ''
   supportPrimary: process.env.COCO_MAIL_SUPPORT_PRIMARY or ''
   supportPremium: process.env.COCO_MAIL_SUPPORT_PREMIUM or ''
-  username: process.env.COCO_MAIL_SERVICE_USERNAME or ''
   mailchimpAPIKey: process.env.COCO_MAILCHIMP_API_KEY or ''
   mailchimpWebhook: process.env.COCO_MAILCHIMP_WEBHOOK or '/mail/webhook'
   sendwithusAPIKey: process.env.COCO_SENDWITHUS_API_KEY or ''


Mention that the legacy network is indeed legacy

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -25,7 +25,8 @@

 # ## Network interface
 #
-# [Network](../Network/) is used for running NoFlo graphs.
+# [Network](../Network/) is used for running NoFlo graphs. The direct Network inteface is only provided
+# for backwards compatibility purposes. Use `createNetwork` instead.
 exports.Network = require('./LegacyNetwork').Network

 # ### Platform detection


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -5,7 +5,7 @@

   # create a player and include Controllable
   player = GameObject
-    includedModules: ["ClampBounds"]
+    includedModules: ["Controllable"]
     width: 5
     height: 17


lint for too long of line

lint for too long of line

--- src/exercise/index.cjsx
+++ src/exercise/index.cjsx
@@ -90,7 +90,11 @@
   render: ->
     {id} = @props

-    <Reactive topic={id} store={exercises} apiChannelName={apiChannelName} getter={exercises.getAllParts}>
+    <Reactive
+      topic={id}
+      store={exercises}
+      apiChannelName={apiChannelName}
+      getter={exercises.getAllParts}>
       <ExerciseBase {...@props}/>
     </Reactive>


(fix) Add missing fs import.

(fix) Add missing fs import.

--- src/https_server.coffee
+++ src/https_server.coffee
@@ -1,4 +1,5 @@

+fs = require 'fs'
 http = require 'http'
 https = require 'https'


Clean up

Clean up

--- ozaria/site/views/play/level/LevelDialogueView.coffee
+++ ozaria/site/views/play/level/LevelDialogueView.coffee
@@ -5,51 +5,9 @@
 marked = require 'marked'
 Shepherd = require('shepherd.js').default

-calculateMinSize = (length) ->
-  innerHeight = window.innerHeight
-  innerWidth = window.innerWidth
-  # Large size with big text
-  if innerHeight >= 600 && innerWidth >= 1000
-    if length < 65
-      return 24
-    else if length < 128
-      return 20
-    else
-      return 14
-  # Small size - which is not handled well by our UI now anyway, but we'll do the best we can...
-  else if innerHeight < 500 || innerWidth < 800
-    if length < 65
-      return 8
-    else if length < 128
-      return 6
-    else
-      return 4
-  # Any other combination of large/small width/height
-  else
-    if length < 65
-      return 16
-    else if length < 128
-      return 12
-    else
-      return 8
-
-runFitty = (length) ->
-  if not length
-    return
-
-  return fitty('.fit', {
-    minSize: calculateMinSize(length)
-    maxSize: 24
-    multiLine: true
-  })[0]
-
-
 module.exports = class LevelDialogueView extends CocoView
   id: 'level-dialogue-view'
   template: template
-
-  subscriptions:
-    'sprite:speech-updated': 'onSpriteDialogue'

   events:
     'click .backButton': 'onClickBack'
@@ -58,159 +16,132 @@
     super options
     @level = options.level
     @sessionID = options.sessionID
-    @currentMessage = ''
-    @doneAnimating = false
-    @SHEPHERD_TIMEOUT = 25 * 1000
-    # Resizing is debounced to avoid performance issues and spamming the text fitting.
-    @onWindowResize = _.debounce(@onWindowResize, 100)
-    $(window).on('resize', @onWindowResize)
-
-    # Set the initial speaking character.
     @character = @level.get('characterPortrait') or 'vega'
-
-  destroy: ->
-    @clearAsyncTimers()
-    @resetFitty()
-    super()

   onClickBack: (e) ->
     @tour.show(@movingTutorialSteps - 1)

-  onWindowResize: (e) ->
-    console.log('resizing...')
+  startTutorial: ->
+    @tour = new Shepherd.Tour({
+      defaultStepOptions: {
+        classes: 'shepherd-rectangle'
+        highlightClass: '.golden-highlight-border'
+        scrollTo: true
+      }
+      useModalOverlay: true
+    })

-  startTutorial: (runShepherd = true) =>
-    if runShepherd
-      @tour = new Shepherd.Tour({
-        defaultStepOptions: {
-          classes: 'shepherd-rectangle'
-          highlightClass: '.golden-highlight-border'
-          scrollTo: true
+    @tutorial = [{
+      message: 'Wordy words about intros and such kind of words that meant to introduce some stuffs.'
+      targetElement: 'Intro / Center'
+    }, {
+      message: 'Herby der helpful words lookit this list of goals here'
+      targetElement: 'Goal List'
+      animation: 'Outline'
+    }, {
+      message: "Herby der helpful words lookit this Code Editor Box it's a place for code"
+      targetElement: 'Code Editor Window'
+      animation: 'Outline'
+    }, {
+      message: 'Herby der helpful words lookit this Run button'
+      targetElement: 'Run Button'
+      animation: 'Outline'
+    }, {
+      message: 'Herby der helpful words lookit this bank of code here'
+      targetElement: 'Code Bank button'
+      animation: 'Outline'
+    }, {
+      message: "This is where I give you hints and stuff. Aren't you going to read my helpful hints?"
+    }, {
+      message: "Ah, you're reading my hints and stuff!"
+    }, {
+      message: 'What a reader you are. Truly.'
+    }]
+
+    backButton = {
+      text: '<-'
+      action: =>
+        @tour.back()
+        # store.dispatch('tutorial/goToPreviousStep')
+    }
+    nextButton = {
+      text: '->'
+      action: =>
+        if @tour.currentStep.id >= @movingTutorialSteps
+          @tour.hide()
+        else
+          @tour.next()
+          # store.dispatch('tutorial/goToNextStep')
+    }
+
+    console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+
+    @movingTutorialSteps = @tutorial.filter((s) -> s.targetElement).length
+    console.log('@movingTutorialSteps')
+    console.log(@movingTutorialSteps)
+
+    steps = @tutorial
+      .filter((s) -> s.targetElement)
+      .map((tutorialStep, index) ->
+        defaultButtons = []
+        if index == @movingTutorialSteps
+          defaultButtons.push(backButton)
+        else if index == 0
+          defaultButtons.push(nextButton)
+        else
+          defaultButtons.push(backButton)
+          defaultButtons.push(nextButton)
+
+        step = {
+          id: index
+          text: tutorialStep.message
+          buttons: defaultButtons
         }
-        useModalOverlay: true
-      })

-      @tutorial = [{
-        message: 'Wordy words about intros and such kind of words that meant to introduce some stuffs.'
-        targetElement: 'Intro / Center'
-      }, {
-        message: 'Herby der helpful words lookit this list of goals here'
-        targetElement: 'Goal List'
-        animation: 'Outline'
-      }, {
-        message: "Herby der helpful words lookit this Code Editor Box it's a place for code"
-        targetElement: 'Code Editor Window'
-        animation: 'Outline'
-      }, {
-        message: 'Herby der helpful words lookit this Run button'
-        targetElement: 'Run Button'
-        animation: 'Outline'
-      }, {
-        message: 'Herby der helpful words lookit this bank of code here'
-        targetElement: 'Code Bank button'
-        animation: 'Outline'
-      }, {
-        message: "This is where I give you hints and stuff. Aren't you going to read my helpful hints?"
-      }, {
-        message: "Ah, you're reading my hints and stuff!"
-      }, {
-        message: 'What a reader you are. Truly.'
-      }]
+        if tutorialStep.targetElement != 'Intro / Center'
+          step['attachTo'] = switch
+            when tutorialStep.targetElement == 'Run Button' then { element: '#run', on: 'top' }
+            when tutorialStep.targetElement == 'Next Button' then { element: '#next', on: 'top' }
+            when tutorialStep.targetElement == 'Play Button' then { element: '#run', on: 'top' }
+            when tutorialStep.targetElement == 'Update Button' then { element: '#update-game', on: 'top' }
+            when tutorialStep.targetElement == 'Goal List' then { element: '#goals-view', on: 'left' }
+            when tutorialStep.targetElement == 'Code Bank button' then { element: '.ace_editor', on: 'right' }
+            when tutorialStep.targetElement == 'Code Editor Window' then { element: '#spell-palette-view', on: 'left' }

-      backButton = {
-        text: '<-'
-        action: =>
-          @tour.back()
-          # store.dispatch('tutorial/goToPreviousStep')
-      }
-      nextButton = {
-        text: '->'
-        action: =>
-          if @tour.currentStep.id >= @movingTutorialSteps
-            @tour.hide()
-          else
-            @tour.next()
-            # store.dispatch('tutorial/goToNextStep')
-      }
-
-      console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
-
-      @movingTutorialSteps = @tutorial.filter((s) -> s.targetElement).length
-      console.log('@movingTutorialSteps')
-      console.log(@movingTutorialSteps)
-
-      steps = @tutorial
-        .filter((s) -> s.targetElement)
-        .map((tutorialStep, index) ->
-          defaultButtons = []
-          if index == @movingTutorialSteps
-            defaultButtons.push(backButton)
-          else if index == 0
-            defaultButtons.push(nextButton)
-          else
-            defaultButtons.push(backButton)
-            defaultButtons.push(nextButton)
-
-          step = {
-            id: index
-            text: tutorialStep.message
-            buttons: defaultButtons
-          }
-
-          if tutorialStep.targetElement != 'Intro / Center'
-            step['attachTo'] = switch
-              when tutorialStep.targetElement == 'Run Button' then { element: '#run', on: 'top' }
-              when tutorialStep.targetElement == 'Next Button' then { element: '#next', on: 'top' }
-              when tutorialStep.targetElement == 'Play Button' then { element: '#run', on: 'top' }
-              when tutorialStep.targetElement == 'Update Button' then { element: '#update-game', on: 'top' }
-              when tutorialStep.targetElement == 'Goal List' then { element: '#goals-view', on: 'left' }
-              when tutorialStep.targetElement == 'Code Bank button' then { element: '.ace_editor', on: 'right' }
-              when tutorialStep.targetElement == 'Code Editor Window' then { element: '#spell-palette-view', on: 'left' }
-
-          console.log('step: ')
-          console.log(step)
-          return step
-        )
-
-      console.log('@tour: ')
-      console.log(@tour)
-
-      @tour.addSteps(steps)
-
-      @dialogueViewDisplayCss = $('#level-dialogue-view').css('display')
-      $('#level-dialogue-view').css('display', 'none')
-
-      Shepherd.on('complete', =>
-        $('#level-dialogue-view').css('display', @dialogueViewDisplayCss)
-        e = @tutorial[@movingTutorialSteps]
-
-        message = e.message.replace /<i class='(.+?)'><\/i>/, ""
-        $('.vega-dialogue').text(message)
-        @adjustText()
+        console.log('step: ')
+        console.log(step)
+        return step
       )

-      @tour.on('show', =>
-        # TODO: If the new current step is not a Moving Vega step, change the class, change the picture
-        # TODO: Make it attach to each separate step - the step that is visible
-        $('.shepherd-content').prepend($(''))
-      )
+    console.log('@tour: ')
+    console.log(@tour)

-      @tour.on('end', =>
-        # TODO: Add remaining stationary steps to regular vega box
-      )
+    @tour.addSteps(steps)

-      @tour.start()
+    @dialogueViewDisplayCss = $('#level-dialogue-view').css('display')
+    $('#level-dialogue-view').css('display', 'none')
+
+    Shepherd.on('complete', =>
+      $('#level-dialogue-view').css('display', @dialogueViewDisplayCss)
+      e = @tutorial[@movingTutorialSteps]
+
+      message = e.message.replace /<i class='(.+?)'><\/i>/, ""
+      $('.vega-dialogue').text(message)
+      @adjustText()
+    )
+
+    @tour.on('show', =>
+      # TODO: If the new current step is not a Moving Vega step, change the class, change the picture
+      # TODO: Make it attach to each separate step - the step that is visible
       $('.shepherd-content').prepend($(''))
+    )

-  resetFitty: ->
-    @fitty?.element.removeEventListener('fit', @adjustText)
-    @fitty?.unsubscribe()
+    @tour.on('end', =>
+      # TODO: Add remaining stationary steps to regular vega box
+    )

-  clearAsyncTimers: ->
-    clearInterval(@messageInterval)
-    clearTimeout(@messageTimeout)
-    @messageInterval = null
-    @messageTimeout = null
+    @tour.start()
+    $('.shepherd-content').prepend($(''))

   isFullScreen: ->
     document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen


Init pendingUpdateOperation in ctor

Init pendingUpdateOperation in ctor

--- src/menu-manager.coffee
+++ src/menu-manager.coffee
@@ -11,9 +11,8 @@
 # An instance of this class is always available as the `atom.menu` global.
 module.exports =
 class MenuManager
-  pendingUpdateOperation: null
-
   constructor: ({@resourcePath}) ->
+    @pendingUpdateOperation = null
     @template = []
     atom.keymap.on 'bundled-keymaps-loaded', => @loadPlatformItems()


Make Persona login background matching new design

--- app/assets/javascripts/persona.js.coffee
+++ app/assets/javascripts/persona.js.coffee
@@ -4,7 +4,7 @@
     navigator.id.request {
       siteName: 'Asciinema',
       siteLogo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wHGBMiFVqqanYAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVRo3u3KoQEAAAgDoP3/tBaD0WiATAJwVIsgCIIgCIIgCIIgCIIgCIIgCMKHADAafyL3ebnQxskAAAAASUVORK5CYII=',
-      backgroundColor: '#d95525',
+      backgroundColor: '#06989a',
       privacyPolicy: 'https://asciinema.org/privacy',
       termsOfService: 'https://asciinema.org/tos'
     }


prevent preview wysiswyg duplication

prevent preview wysiswyg duplication

--- app/coffee/modules/common/wisiwyg.coffee
+++ app/coffee/modules/common/wisiwyg.coffee
@@ -73,16 +73,24 @@
             closePreviewMode()

         cancelablePromise = null
+        previewInProgress = false

         preview = ->
+            return if previewInProgress
+
+            previewInProgress = true
+
             markdownDomNode = element.parents(".markdown")
             markItUpDomNode = element.parents(".markItUp")
+
             $rs.mdrender.render($scope.projectId, $model.$modelValue).then (data) ->
                 html = previewTemplate({data: data.data})
                 html = $compile(html)($scope)

                 markdownDomNode.append(html)
                 markItUpDomNode.hide()
+
+                previewInProgress = false

                 markdown = element.closest(".markdown")


Remove JS link handling

Remove JS link handling

The original intention was to have the header link to the first unread
comment like pressing “o” does.

Fixes #73

--- app/js/views/notification_details.coffee
+++ app/js/views/notification_details.coffee
@@ -12,7 +12,6 @@
   events:
     'click a': 'clickLink'
     'click *[rel=back]': 'unfocus'
-    'click header h1 a': 'open'

   # Required options:
   # model - a notification object


Working orientation. Doesn't account for camera zoom

--- app/lib/surface/CoordinateDisplay.coffee
+++ app/lib/surface/CoordinateDisplay.coffee
@@ -93,17 +93,35 @@
     #  -adjust the cache position
     # both can use the current orientation to do their work without knowing about the other

-    startPos =
-      x: -fullPointMarkerLength
-      y: -totalHeight + fullPointMarkerLength
-    posShift =
-      x: 0
-      y: contentHeight
+    if @isNearTopEdge()
+      verticalEdge =
+        startPos: -fullPointMarkerLength
+        posShift: -contentHeight + 4
+    else
+      verticalEdge =
+        startPos: -totalHeight + fullPointMarkerLength
+        posShift: contentHeight

-    @orient startPos, posShift, totalHeight, totalWidth
+    if @isNearRightEdge()
+      horizontalEdge =
+        startPos: -totalWidth + fullPointMarkerLength
+        posShift: totalWidth
+    else
+      horizontalEdge =
+        startPos: -fullPointMarkerLength
+        posShift: 0

-  orient: (startPos, posShift, totalHeight, totalWidth) ->
-    @label.regY = @background.regY = posShift.y
+    @orient verticalEdge, horizontalEdge, totalHeight, totalWidth
+
+  isNearTopEdge: ->
+    @camera.distanceToTopEdge(@lastPos.y) <= 1
+
+  isNearRightEdge: ->
+    @camera.distanceToRightEdge(@lastPos.x) <= 4
+
+  orient: (verticalEdge, horizontalEdge, totalHeight, totalWidth) ->
+    @label.regY = @background.regY = verticalEdge.posShift
+    @label.regX = @background.regX = horizontalEdge.posShift

     @containerOverlay.graphics
       .clear()
@@ -111,10 +129,10 @@
       .drawRect(0, 0, totalWidth, totalHeight)
       .endFill()
       .beginFill('rgba(0,0,255,0.4)') # Cache position
-      .drawRect(startPos.x, startPos.y, totalWidth, totalHeight)
+      .drawRect(horizontalEdge.startPos, verticalEdge.startPos, totalWidth, totalHeight)
       .endFill()

-    #@cache orientationX, orientationY, totalWidth, totalHeight
+    #@cache horizontalEdge.startPos, verticalEdge.startPos, totalWidth, totalHeight

   updateCoordinates: (contentWidth, contentHeight, offset) ->
     # Center label horizontally and vertically


Revert "Replace soundex-code node module with node-soundex which also works in browser"

This reverts commit 13b5430c3609922430aa44428762dff31d36c25c.

--- app/static/script/directive/chart.coffee
+++ app/static/script/directive/chart.coffee
@@ -156,7 +156,7 @@
         if (words = str.split(' ')).length > 1
           colors = R.map fuzzyColor, R.filter(R.I, words)
           return R.reduce tinycolor.mix, R.head(colors), R.tail(colors)
-        sndx = Soundex(str or ' ')
+        sndx = soundexPhonetics(str or ' ')
         # hue = (sndx[0].charCodeAt() % 64)  * (360 / 64) # modulo is for unicode chars. Or 360?
         # hue = (((sndx[0].charCodeAt() - (65)) + 26) % 26) * (360 / 26)
         hue = (((sndx[0].charCodeAt())) % 26) * (360 / 26)


Do not show starter licenses to Belgians

--- app/views/courses/EnrollmentsView.coffee
+++ app/views/courses/EnrollmentsView.coffee
@@ -46,7 +46,7 @@
         'available': []
         'pending': []
       }
-      shouldUpsell: true
+      shouldUpsell: false
     })
     window.tracker?.trackEvent 'Classes Licenses Loaded', category: 'Teachers', ['Mixpanel']
     super(options)
@@ -78,7 +78,7 @@
     leadPriorityRequest = me.getLeadPriority()
     @supermodel.trackRequest leadPriorityRequest
     leadPriorityRequest.then ({ priority }) =>
-      shouldUpsell = (priority is 'low')
+      shouldUpsell = (priority is 'low') and (me.get('preferredLanguage') isnt 'nl-BE')
       @state.set({ shouldUpsell })
       if shouldUpsell
         application.tracker?.trackEvent 'Starter License Upsell: Banner Viewed', {price: @state.get('centsPerStudent'), seats: @state.get('quantityToBuy')}


Fix tests

Fix tests

--- src/smc-webapp/test/math_katex.coffee
+++ src/smc-webapp/test/math_katex.coffee
@@ -10,7 +10,7 @@
         rendered_math = math_katex.render(target_string)
         rendered_by_katex = katex.renderToString(math_string)

-        expect(rendered_math)
+        expect(rendered_math.html)
             .toEqual(rendered_by_katex)

@@ -24,4 +24,4 @@
         rendered_by_katex = katex.renderToString(math_string)

         expect(rendered_math)
-            .toEqual("
" + rendered_by_katex + "
")
+            .toEqual("
" + rendered_by_katex + "
\n")


Apply @kosssi suggestion

Apply @kosssi suggestion

--- src/remote/couch.coffee
+++ src/remote/couch.coffee
@@ -43,12 +43,12 @@
                 @goingOnline()
             else if not online and @online
                 @goingOffline()
-            @online = online
             callback @online

     # Couch is available again!
     goingOnline: ->
         log.info 'The network is available again'
+        @online = true
         cb() for cb in @upCallbacks
         @upCallbacks = []

@@ -56,6 +56,7 @@
     # Check every minute if the network is back.
     goingOffline: ->
         log.info "The Cozy can't be reached currently"
+        @online = false
         interval = setInterval =>
             @ping (available) ->
                 clearInterval interval if available


if hubot is run under a different name, help should refer to it under the new name

--- src/scripts/help.coffee
+++ src/scripts/help.coffee
@@ -10,5 +10,7 @@
     cmds = robot.helpCommands()
     if msg.match[1]
       cmds = cmds.filter (cmd) -> cmd.match(new RegExp(msg.match[1]))
-    msg.send cmds.join("\n")
-
+    emit = cmds.join("\n")
+    unless robot.name is 'Hubot'
+      emit = emit.replace(/(H|h)ubot/g, robot.name)
+    msg.send emit


titlecontroller: do not notify when there are posts to followed topics

--- client/app/lib/pagetitlecontroller.coffee
+++ client/app/lib/pagetitlecontroller.coffee
@@ -24,8 +24,10 @@

     return  if @focused

-    { socialapi } = kd.singletons
-    { id }        = notification.channelMessage
+    { socialapi }        = kd.singletons
+    { id, typeConstant } = notification.channelMessage
+
+    return  if typeConstant isnt 'privatemessage'

     socialapi.message.byId {id}, (err, message) =>
       id              = message.account._id


app: rm unused shortcutscontroller

app: rm unused shortcutscontroller

--- client/app/lib/shortcutscontroller.coffee
+++ client/app/lib/shortcutscontroller.coffee
@@ -1,58 +0,0 @@
-Shortcuts = require 'shortcuts'
-kd        = require 'kd'
-events    = require 'events'
-globals   = require 'globals'
-defaults  = require './shortcuts/defaults'
-
-# XXX: globals.modules should not be used, and should not be exposed indeed
-# lazyrouter also makes use of this -og
-index = globals.modules.reduce (acc, x) ->
-  acc[x.name] = x.shortcuts
-  return acc
-, {}
-
-shortcuts = new Shortcuts defaults
-
-module.exports =
-
-class ShortcutsController extends kd.Object
-
-  constructor: ->
-
-    #wc = kd.singleton 'windowController'
-    #am = kd.singleton 'appManager'
-
-    #@_kv  = null
-
-    #am.on 'AppIsBeingShown', (app) =>
-      #@_reset app  unless @_app or @_app.name isnt app.name
-
-    #wc.on 'WindowChangeKeyView', (view) =>
-      #@_handleKeyViewChange view
-
-    #super()
-
-
-  #_reset: ->
-
-    #if @_app
-
-      #_unlistenApp
-
-
-    #name = app.getOption('name').toLowerCase()
-    #sets = index[name]
-    #return undefined  unless Array.isArray sets
-
-
-  #_dispatcher: (e) ->
-
-
-
-
-  #_handleKeyViewChange: (view) ->
-
-    #console.log 'keyview changed', view
-    #@_kv = view
-
-


testrunner: minor

testrunner: minor

--- client/testrunner/lib/routehandler.coffee
+++ client/testrunner/lib/routehandler.coffee
@@ -1,7 +1,6 @@
-lazyrouter           = require 'app/lazyrouter'
-kd                   = require 'kd'
-KodingAppsController = require 'app/kodingappscontroller'
-
+lazyrouter            = require 'app/lazyrouter'
+kd                    = require 'kd'
+KodingAppsController  = require 'app/kodingappscontroller'
 RunnerSocketConnector = require './runnersocketconnector'

 addToHead = KodingAppsController.appendHeadElement.bind KodingAppsController
@@ -40,8 +39,7 @@
   mochaContainer.id = 'mocha'
   document.body.appendChild mochaContainer

-  appendScripts =>
-
+  appendScripts ->
     window.socket = io "http://localhost:#{SOCKET_PORT}"
     runMocha mochaContainer, socket


better comment

better comment

--- src/smc-hub/auth.coffee
+++ src/smc-hub/auth.coffee
@@ -476,7 +476,11 @@

     init_google = (cb) ->
         dbg("init_google")
-        # Strategy: Google OAuth 2 -- https://github.com/jaredhanson/passport-google-oauth2
+        # Strategy: Google OAuth 2 -- should be https://github.com/jaredhanson/passport-google-oauth2
+        # but is https://github.com/passport-next/passport-google-oauth2
+        # ATTENTION:
+        # We have to use a fork of passport-google-oauth2, since jaredhanson is MIA.
+        # See https://github.com/jaredhanson/passport-google-oauth2/pull/51/files
         PassportStrategy = require('@passport-next/passport-google-oauth2').Strategy
         strategy = 'google'
         get_conf strategy, (err, conf) ->
@@ -490,10 +494,6 @@
             #
             # require 'c'; db()
             # db.set_passport_settings(strategy:'google', conf:{clientID:'...',clientSecret:'...'}, cb:console.log)
-
-            # ATTENTION:
-            # We have to use a fork of passport-google-oauth2, since jaredhanson is MIA.
-            # See https://github.com/jaredhanson/passport-google-oauth2/pull/51/files
             opts =
                 clientID     : conf.clientID
                 clientSecret : conf.clientSecret


Style applyStyleQueriesInDocument.localQueries param added

--- src/styles/style.coffee
+++ src/styles/style.coffee
@@ -29,10 +29,10 @@
     JSON_CHILDREN = i++
     JSON_ARGS_LENGTH = @JSON_ARGS_LENGTH = i

-    @applyStyleQueriesInDocument = (file) ->
+    @applyStyleQueriesInDocument = (file, localQueries = queries) ->
         assert.instanceOf file, File

-        for elem in queries
+        for elem in localQueries
             nodes = file.node.queryAll elem.query
             for node in nodes
                 unless node instanceof Tag
@@ -55,7 +55,7 @@

         forNode = (file, node, parentStyle) ->
             isText = node instanceof Text
-            if isText or (attr = node.attrs['neft:style'])
+            if isText or node.attrs['neft:style']
                 style = new Style
                 style.file = file
                 style.node = node


Using namespace function and no longer using jQuery

--- lib/assets/javascripts/pixie/editor/pixel/tools.coffee
+++ lib/assets/javascripts/pixie/editor/pixel/tools.coffee
@@ -1,11 +1,8 @@
 # All the default editor tools for the pixel editor

 # Old school namespacing. Stuck with it until Sprockets supports exports
-window.Pixie ||= {}
-Pixie.Editor ||= {}
-Pixie.Editor.Pixel ||= {}
+namespace "Pixie.Editor.Pixel", (Pixel) ->

-Pixie.Editor.Pixel.tools = (($) ->
   # Import tools and actions from other files
   {
     config: {
@@ -15,8 +12,8 @@
   } = Pixie.Editor.Pixel

   colorNeighbors = (color) ->
-    this.color(color)
-    $.each this.canvas.getNeighbors(this.x, this.y), (i, neighbor) ->
+    @color(color)
+    @canvas.getNeighbors(@x, @y).each (neighbor) ->
       neighbor?.color(color)

   erase = (pixel, opacity) ->
@@ -40,7 +37,7 @@

       neighbors = canvas.getNeighbors(pixel.x, pixel.y)

-      $.each neighbors, (index, neighbor) ->
+      neighbors.each (neighbor) ->
         if neighbor?.color().equal(originalColor)
           neighbor.color(newColor)
           q.push(neighbor)
@@ -133,7 +130,7 @@
       lastPosition = currentPosition
   )()

-  return tools =
+  Pixel.tools =
     pencil: pencilTool

     mirror_pencil:
@@ -172,4 +169,3 @@
       hotkeys: ['f', '6']
       mousedown: floodFill
       mouseenter: floodFill
-)(jQuery)


Punt on rotation sanitation for now

Punt on rotation sanitation for now

--- framer/GestureInputRecognizer.coffee
+++ framer/GestureInputRecognizer.coffee
@@ -373,7 +373,6 @@
 			event.touchDistance = Utils.pointDistance(touchPointA, touchPointB)
 			event.rotation = Utils.pointAngle(touchPointA, touchPointB)

-
 		# Special cases

 		# Velocity
@@ -387,11 +386,12 @@
 			event.scale = event.touchDistance / @session.started.pinch.touchDistance
 			event.scaleDirection = @_getScaleDirection(event.scale - @session.lastEvent.scale)

+		# Sanitize the rotation
 		# if @session?.lastEvent
-		# 	if @session.lastEvent.fingers == 1 and event.fingers == 2
-		# 		print "1 -> 2"
-		# 	if @session.lastEvent.fingers == 2 and event.fingers == 1
-		# 		print "2 -> 1"
+		# 	if event.rotation - @session.lastEvent.rotation > 180
+		# 		event.rotation -= 360
+		# 	if event.rotation - @session.lastEvent.rotation < 180
+		# 		event.rotation += 360

 		# Convert point style event properties to dom style:
 		# event.delta -> event.deltaX, event.deltaY


Update e-mail field after change

Update e-mail field after change

--- client/app/Applications/Account.kdapplication/account/accSettingsPersUsernameEmail.coffee
+++ client/app/Applications/Account.kdapplication/account/accSettingsPersUsernameEmail.coffee
@@ -20,15 +20,19 @@
               title    : err.message
               duration : 2000
           else
+            if err and err.name is 'PINExistsError'
+              new KDNotificationView
+                title    : err.message
+                duration : 2000
             new VerifyPINModal 'Update E-Mail', (pin)=>
               KD.remote.api.JUser.changeEmail
                 email : formData.email
                 pin   : pin
-              , (err)->
+              , (err)=>
                 new KDNotificationView
                   title    : if err then err.message else "E-mail changed!"
                   duration : 2000
-                # FIXME update the view in some way
+                @emit "EmailChangedSuccessfully", formData.email

           emailSwappable.swapViews()

@@ -42,6 +46,7 @@
       defaultValue : user.email
       placeholder  : "you@yourdomain.com..."
       name         : "email"
+
     emailInputs.addSubView inputActions = new KDView cssClass : "actions-wrapper"
     inputActions.addSubView emailSave = new KDButtonView
       title        : "Save"
@@ -58,6 +63,10 @@
       tagName      : "span"
       partial      : user.email
       cssClass     : "static-text status-#{user.status}"
+
+    emailForm.on "EmailChangedSuccessfully", (email)->
+      emailSpan.updatePartial email
+
     nonEmailInputs.addSubView emailEdit = new KDCustomHTMLView
       tagName      : "a"
       partial      : "Edit"


payment: bring vm product form into line with latest form workflow api

--- client/landing/app/Applications/Environments.kdapplication/views/vms/vmproductform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/vms/vmproductform.coffee
@@ -5,42 +5,38 @@

   checkUsageLimits: (pack, callback) ->
     { subscription } = @collector.data
+    return callback { message: 'no subscription' }  unless subscription?
     subscription.checkUsage pack, (err, usage) =>
+      @collectData oldSubscription: subscription
       @clearData 'subscription'  if err

       callback err, usage
-

   createPackChoiceForm: -> new PackChoiceForm
     title     : 'Choose your VM'
     itemClass : VmProductView

-  setState: (state) ->
-    @showForm state
-
   setCurrentSubscriptions: (subscriptions) ->
     @currentSubscriptions = subscriptions
     switch subscriptions.length
       when 0
-        @setState 'upgrade'
+        @showForm 'upgrade'
       when 1
         [subscription] = subscriptions
         @collectData { subscription }
         @emit 'PackOfferingRequested', subscription
       else
-        @setState 'choice'
+        @showForm 'choice'

   setContents: (packs) ->
-    @setState 'pack choice'
+    @showForm 'pack choice'
     (@getForm 'pack choice').setContents packs
-
-  viewAppended: -> @prepareProductForm()

   createChoiceForm: -> new KDView partial: 'this is a plan choice form'

-  prepareProductForm: ->
+  prepareWorkflow: ->
     @requireData [
-
+
       @any('subscription', 'plan')

       'pack'


Workspace: Fixed creating multiple editor tabs in EditorPane.

--- client/Workspace/panes/editorpane.coffee
+++ client/Workspace/panes/editorpane.coffee
@@ -25,21 +25,27 @@
       @ace.editor.setValue content  if content

   createEditorTabs: ->
-    @tabHandleContainer = new ApplicationTabHandleHolder
-      delegate      : this
-      addPlusHandle : no
+    @editors                  = {}
+    @tabHandleContainer       = new ApplicationTabHandleHolder
+      delegate                : this
+      addPlusHandle           : no

-    @tabView = new ApplicationTabView
-      delegate           : this
-      tabHandleContainer : @tabHandleContainer
+    @tabView                  = new ApplicationTabView
+      delegate                : this
+      tabHandleContainer      : @tabHandleContainer
+      removePaneOnTabChange   : no

-    for fileOptions in @files
-      file   = FSHelper.createFileFromPath fileOptions.path
-      pane   = new KDTabPaneView
-        name : file.name or "Untitled.txt"
+    @files.forEach (options) =>
+      {name, path, content}   = options
+      file                    = FSHelper.createFileFromPath path
+      pane                    = new KDTabPaneView
+        name                  : file.name or "Untitled.txt"
+        removePaneOnTabChange : no

-      pane.addSubView @createEditorInstance file
+      editor = @createEditorInstance file, content
+      pane.addSubView editor
       @tabView.addPane pane
+      @editors[name] = editor  if name

   getValue: ->
     return  @ace.editor.getSession().getValue()


Modified replace to regex and added globakl flag

--- client/app/lib/util/applyMarkdown.coffee
+++ client/app/lib/util/applyMarkdown.coffee
@@ -5,7 +5,7 @@

   return null unless text

-  text = text.replace '/\\/', '\\\\'
+  text = text.replace /\\/g, '\\'

   options.gfm       ?= true
   options.pedantic  ?= false


added some documentation

added some documentation

--- src/metronome.coffee
+++ src/metronome.coffee
@@ -3,28 +3,39 @@
 class BummTschak.Metronome

   constructor: (@_bpm) ->
-    @_interval = null
-    @_onStep  = []
-    @_onReset  = []
+    @_interval  = null
+    @_onStep    = []
+    @_onReset   = []
+    @_isRunning = false

   setTempo: (@_bpm) ->
     @reset()

   stop: ->
+    # clear interval
     clearInterval(@_interval) if @_interval?
     @_interval = null

   start: ->
-    @stop()
+    if @_interval? then return

+    # calculate interval time
+    time = 60 / @_bpm / 4 * 1000
+
+    # set interval
     @_interval = setInterval(
+      # call every onStep-callback
       => fnc() for fnc in @_onStep
-    , 60 / @_bpm / 4 * 1000)
+    , time)

   reset: ->
     restart = @_interval?
+
     @stop()
+
+    # call every onReset-callback
     fnc() for fnc in @_onReset
+
     @start() if restart

   onStep: (fnc) ->


fix for blocking modal strings

fix for blocking modal strings

--- client/Framework/components/modals/KDBlockingModalView.coffee
+++ client/Framework/components/modals/KDBlockingModalView.coffee
@@ -2,7 +2,7 @@
   constructor:->
     super
     $(window).off "keydown.modal"
-
+
   putOverlay:()->
     @$overlay = $ "
",
       class : "kdoverlay"
@@ -11,7 +11,7 @@
     @$overlay.fadeIn 200

   setDomElement:(cssClass)->
-    @domElement = $ "
+    @domElement = $ """

@@ -19,6 +19,6 @@

-
"
+
"""

   click:(e)->


Environment: Assign default value for state text

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -481,6 +481,8 @@

     stackBasedStates =
       NotInitialized : 'is not build yet.'
+
+    stackText = stateTexts[@state]

     if not isKoding() and @stack
       stackText = stackBasedStates[@state] or stateTexts[@state]


Use insertOrUpsert for new messages in Messages model

--- packages/rocketchat-lib/server/models/Messages.coffee
+++ packages/rocketchat-lib/server/models/Messages.coffee
@@ -271,7 +271,7 @@

 		_.extend record, extraData

-		record._id = @insert record
+		record._id = @insertOrUpsert record
 		return record

 	createUserJoinWithRoomIdAndUser: (roomId, user, extraData) ->


feat(lights.cson): add LightShadow snippet

--- snippets/lights.cson
+++ snippets/lights.cson
@@ -12,6 +12,10 @@
     'prefix': 'hemispherelight'
     'body': 'new THREE.HemisphereLight(${1:skyColorHex}, ${2:groundColorHex}, ${3:intensity})'

+  'THREE.LightShadow':
+    'prefix': 'lightshadow'
+    'body': 'new THREE.LightShadow(${1:camera})'
+
   'THREE.PointLight':
     'prefix': 'pointlight'
     'body': 'new THREE.DirectionalLight(${1:hex}, ${2:intensity}, ${3:distance}, ${4:decay})'


Budget pls

Budget pls

--- source/z_game.coffee
+++ source/z_game.coffee
@@ -10,6 +10,9 @@
       @interval = setInterval $.proxy(this.doTick, this), (500 / rate)

   @doTick: ->
+    if Game.state.tick % 1440 == 0
+      Game.state.money += Game.state.calculateBudgetChange()
+
     Game.state.tick += 1

     Customer.spawn()


be more agressive in rounding codemirror lines since if it is too tall, browser gets annoying.

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -745,7 +745,7 @@
         button_bar_height = @element.find(".salvus-editor-codemirror-button-row").height()
         font_height = @codemirror.defaultTextHeight()

-        cm_height = Math.floor((elem_height - button_bar_height)/font_height) * font_height
+        cm_height = Math.floor((elem_height - button_bar_height - font_height/2)/font_height) * font_height

         @element.height(elem_height).show()


Delay refreshing

Delay refreshing

--- frontend/coffee/app.coffee
+++ frontend/coffee/app.coffee
@@ -118,12 +118,12 @@
               params = { sha: res.sha }
               $.ajax({ url: updateRefAPI, method: 'PATCH', data: JSON.stringify(params) }).done (res) ->
                 dz.removeAllFiles()
-                do refreshThumbnails
                 input.prop 'disabled', no
                 btn.button('reset').prop 'disabled', no
+                setTimeout refreshThumbnails, 1000
       no
   do refreshThumbnails = ->
-    getAPI "/repos/#{user}/#{repo}/contents/#{browsingPath}?ref=#{ref}", (res) ->
+    getAPI "/repos/#{user}/#{repo}/contents/#{browsingPath}?ref=#{ref}&_ts=#{new Date().getTime()}", (res) ->
       $('.current-items').empty()
       res = res.filter ({ path }) -> /\.(png|jpg|gif)$/.test path
       row = null
@@ -166,7 +166,6 @@
     ol.append if i == last
       """
#{text}
"""
     else
-      console.info paths[0..i]
       """
#{text}
"""

 handleRoute = ->


When cards correct or incorrect, show loading icon

--- app/assets/javascripts/cardsets.coffee
+++ app/assets/javascripts/cardsets.coffee
@@ -275,6 +275,7 @@
       kksystem.cardsets.study.finishStudy()

   ns.correct = ->
+    kksystem.cardsets.study.startLoading()
     index = ((if ns.use_card.current_cardbox_id == cardbox.id then \
               true else false) for cardbox in ns.cardboxes).indexOf(true)
     new_cardbox = if index == -1
@@ -292,6 +293,7 @@
       ns.displayNextCard()

   ns.incorrect = ->
+    kksystem.cardsets.study.startLoading()
     ns.use_card.set('current_cardbox_id', if ns.cardboxes[0] \
                                           then ns.cardboxes[0].id else null)
     ns.use_card.save (success) ->
@@ -345,10 +347,12 @@
       kksystem.cardsets.study.finishStudy()

   ns.correct = ->
+    kksystem.cardsets.study.startLoading()
     ns.use_card.done = true
     ns.displayNextCard()

   ns.incorrect = ->
+    kksystem.cardsets.study.startLoading()
     ns.use_card.done = true
     ns.displayNextCard()

@@ -399,10 +403,12 @@
       kksystem.cardsets.study.finishStudy()

   ns.correct = ->
+    kksystem.cardsets.study.startLoading()
     ns.use_card.done = true
     ns.displayNextCard()

   ns.incorrect = ->
+    kksystem.cardsets.study.startLoading()
     ns.use_card.done = true
     if ns.lowest_cardbox
       ns.use_card.set('current_cardbox_id', ns.lowest_cardbox.id)


update version comparison

update version comparison

--- test/bin/test.coffee
+++ test/bin/test.coffee
@@ -14,7 +14,7 @@

     log = console.log
     console.log = (s) ->
-      assert.equal s, 'kevas v3.0.1\n'
+      assert.equal s, 'kevas v4.0.0\n'
       console.log = log

     command ['--version']
@@ -23,7 +23,7 @@

     log = console.log
     console.log = (s) ->
-      assert.equal s, 'kevas v3.0.1\n'
+      assert.equal s, 'kevas v4.0.0\n'
       console.log = log

     command ['-v']


Animate rating change

Animate rating change

--- app/assets/javascripts/rooms.js.coffee
+++ app/assets/javascripts/rooms.js.coffee
@@ -70,8 +70,15 @@
       for user in data.users
         $user = @container.find("li[data-id=\"#{user.id}\"]")
         $rating = $user.find('.js-user-rating')
-        rating = Math.round(user.rating * 10) / 10
-        $rating.html("(#{rating})")
+        @_animateRatingChange($rating, user.rating)
+
+  _animateRatingChange: ($rating, to) =>
+    from = parseFloat($rating.html()[1..-1])
+    $({rating: from}).animate({rating: to},
+      step: ->
+        value = Math.round(@rating * 10) / 10
+        $rating.html("(#{value})")
+    )

 class Form
   constructor: (@room_id, @form) ->


Add acquireLocales service

Add acquireLocales service

--- src/app.coffee
+++ src/app.coffee
@@ -1,8 +1,9 @@
 endpointBase = "http://api.c4w.jp/api/v1/"
+getEndpoint = (key) -> "#{endpointBase}#{key}.json"

 app = angular.module 'disaster-information-client', [
     'pascalprecht.translate'
-    #'ngSanitize'
+    'ngSanitize'
 ]

@@ -21,55 +22,70 @@

 app.run [
+    'acquireLocales'
+    'acquireEntries'
+    '$rootScope'
+    (acquireLocales,acquireEntries, $rootScope) ->
+        promise = acquireLocales()
+        promise.then (data) ->
+            console.log acquireEntries
+        # $rootScope.err = false
+        #
+        # deferred = $q.defer()
+        #
+        #
+        #
+        #
+        # Promise.resolve()
+        #     .then (locales)->
+        #         Promise.all locales.map (locale) ->
+        #             new Promise (fulfilled, rejected) ->
+        #                 $http.get(getEndpoint locale)
+        #                     .then (res) ->
+        #                         desc = (a, b) -> Date.parse(b.date) - Date.parse(a.date)
+        #                         data.entries
+        #                             .sort desc
+        #                             .forEach (entry) ->
+        #                                 rawDate = Date.parse(entry.date);
+        #                                 entry.date = new Date(rawDate).toLocaleDateString()
+        #                                 entry.time = new Date(rawDate).toLocaleTimeString()
+        #                                 entry.body = entry.body # need sanitization?
+        #                                 entry.locale = locale
+        #                                 $rootScope.entries.push data.entries
+        #                         # .shim always refer selected locale
+        #                         if locale is $rootScope.selectedLocale
+        #                             $rootScope.entries.shim = $rootScope.entries.filter (entry) ->
+        #                                 entry.locale = locale
+        #                         fulfilled()
+        #
+]
+
+app.service 'acquireLocales', [
     '$http'
+    '$q'
     '$rootScope'
-    ($http, $rootScope) ->
-        getEndpoint = (key) -> "#{endpointBase}#{key}.json"
-        # method = 'GET'
-        # url = getEndpoint('locale')
-        # $http { method, url }
-        #     .then (res) ->
-        #         locales = res.data
-        #
-        #         $rootScope.locales = locales
-        locales = ['ja', 'en'] # [mocks] locale should be obtained by ajax
-        $rootScope.locales = locales
-        $rootScope.selectedLocale = locales[0]
+    ($http, $q, $rootScope) ->
+        deferred = $q.defer() # make defer instance
+        return ->
+            $http.get getEndpoint 'locale'
+                .then (res) ->
+                    # initialization
+                    locales = res.data
+                    $rootScope.locales = locales
+                    $rootScope.selectedLocale = locales[0]
+                    $rootScope.languageLabel = {
+                        ja: '日本語'
+                        en: 'English'
+                    }
+                    $rootScope.entries = []
+                    deferred.resolve locales
+                    return deferred.promise
+                , (res) ->
+                    $rootScope.err = true
+                    deferred.reject res
+                    return deferred.promise
+]

-        $rootScope.languageLabel = {
-            ja: '日本語'
-            en: 'English'
-        }
-
-        $rootScope.entries = []
-
-        promises = []
-
-        locales.forEach (locale) ->
-
-            promises.push ->
-                $http { Method: 'GET', url: getEndpoint locale } #[bugs] need to do this with Promise.all and emit 'loaded' event
-                    .then ({data}) ->
-
-                        desc = (a, b) -> Date.parse(b.date) - Date.parse(a.date)
-                        data.entries
-                            .sort desc
-                            .forEach (entry) ->
-                                rawDate = Date.parse(entry.date);
-                                entry.date = new Date(rawDate).toLocaleDateString()
-                                entry.time = new Date(rawDate).toLocaleTimeString()
-                                entry.body = entry.body # need sanitization?
-                                entry.locale = locale
-                                $rootScope.entries.push data.entries
-                        # .shim always refer selected locale
-                        if locale is $rootScope.selectedLocale
-                            $rootScope.entries.shim = $rootScope.entries.filter (entry) ->
-                                entry.locale = locale
-
-        Promise.all promises
-            .then ->
-                $rootScope.
-]

 app.service 'route', [
@@ -79,8 +95,8 @@
         $rootScope.$watch ->
             $location.path()
         , (aa) ->
-            alert $location.path()
-            alert aa
+            console.log $location.path()
+            console.log aa

         return {


Initialize on proper event

Initialize on proper event

Fix turbolinks

--- app/assets/javascripts/slabtext.coffee
+++ app/assets/javascripts/slabtext.coffee
@@ -1,4 +1,10 @@
-$ ->
+initializeOnEvent =
+  if window.Turbolinks? and window.Turbolinks.supported
+    'turbolinks:load'
+  else
+    'ready'
+
+$(document).on initializeOnEvent, ->
   WebFont.load
     google:
       families: ['Oswald', 'Roboto:400,700']


cleanup

cleanup

--- src/chz.coffee
+++ src/chz.coffee
@@ -1,21 +1,18 @@
-filter = (q, items)->
-  if q
-    items.filter (item)->
-      item.name.toLowerCase().indexOf(q.toLowerCase()) > -1
-  else
-    items
+comp = (a, b)->
+  a.toLowerCase().indexOf(b.toLowerCase()) > -1
+
+filter = (x, xs)->
+  q ? xs.filter ((i)-> comp(i.name, q)) : xs

 focuz = (el)->
-  f = (()-> el.focus())
-  window.setTimeout(f, 0)
+  window.setTimeout((()-> el.focus()) , 0)

 angular
 .module("angular-w", [])
-.directive "wFocus", [->
+.directive "wFocus", ->
   link: (scope, element, attrs) ->
     scope.$watch attrs.wFocus, (fcs)->
       focuz(element[0]) if fcs
-]

 angular
 .module("angular-w")
@@ -30,15 +27,21 @@
        href="javascript:void(0)"
        ng-hide="active"
        ng-click="active=true">
-    {{ (selectedItem || 'none') | json }}
-
-

-      <input ng-keydown="onkeys($event.keyCode)" w-focus="active" class="form-control" type="search" ng-model="search" />
-

-
{{ item.name }}

-

-

-

+       {{ (selectedItem || 'none') | json }}
+

+      <input ng-keydown="onkeys($event.keyCode)"
+             w-focus="active"
+             class="form-control"
+             type="search"
+             ng-model="search" />
+

+

+             {{ item.name }}

   """
   controller: ($scope, $element, $attrs) ->


update template

update template

--- src/cli.coffee
+++ src/cli.coffee
@@ -63,8 +63,6 @@
   # 今週のふりかえり

   #{posts.map((i) -> "- [#{i.date} #{i.title}][#{i.date}]").join('\n')}
-
-  #{posts.map((i) -> "[#{i.date}]: #{i.url}").join('\n')}
   """

 getTitle = (dir, date) ->