refactored deploy to azure

refactored deploy to azure

--- scripts/TM-Github.coffee
+++ scripts/TM-Github.coffee
@@ -5,6 +5,19 @@

 request = require 'request'
+
+deploy_To_Azure = (res)->
+  res.reply "About to trigger a deploy to azure..."
+  options =
+    url    : 'https://ci.appveyor.com/api/builds',
+    headers:
+      'Authorization': 'Bearer ' + process.env.auth_key
+      'Content-Type': 'application/x-www-form-urlencoded'
+    body   : 'accountName=DinisCruz&projectSlug=tm-ci-deploy&branch=master'
+
+  request.post options, (error, response, data)->
+    res.reply 'Request sent, take a look at https://ci.appveyor.com/project/DinisCruz/tm-ci-deploy where a new build will have just started'
+    res.reply 'In a bit, the deployed website will be at https://tm-qa-6.azurewebsites.net'

 module.exports = (robot) ->

@@ -43,21 +56,11 @@
       res.reply 'In a bit, the compiled node_modules will be at the https://github.com/tm-build/TM_4_0_Windows repo'

   robot.respond /deploy to azure/i,(res) ->
-    res.reply "About to trigger a deploy to azure..."
-    options =
-      url    : 'https://ci.appveyor.com/api/builds',
-      headers:
-        'Authorization': 'Bearer ' + process.env.auth_key
-        'Content-Type': 'application/x-www-form-urlencoded'
-      body   : 'accountName=DinisCruz&projectSlug=tm-ci-deploy&branch=master'
+    deploy_To_Azure res

-    request.post options, (error, response, data)->
-      res.reply 'Request sent, take a look at https://ci.appveyor.com/project/DinisCruz/tm-ci-deploy where a new build will have just started'
-      res.reply 'In a bit, the deployed website will be at https://tm-qa-6.azurewebsites.net'
-
   robot.hear /TM_4_0_Design(.*)commit/i,(res)->
     res.reply 'New commit to TM_4_0_Design/Dev detected ...'
-    res.reply '...time to deploy to azure'
+    deploy_To_Azure res

   robot.hear /show(.*)tm graph/i,(res) ->
     res.reply 'http://4.bp.blogspot.com/-7g82Ltxt7wI/VLqbd1CLOyI/AAAAAAAAKcc/kisGogPKcmo/s1600/Screen%2BShot%2B2014-11-02%2Bat%2B16.49.16.png'


modify a little

modify a little

--- scripts/amazon-ec.coffee
+++ scripts/amazon-ec.coffee
@@ -84,7 +84,7 @@
   )

 nexWeekSearch = (msg, query , isKindle) ->
-  nextWeek = moment().add(+1,'w').add(+1,'d').format('YYYY-MM-DD')
+  nextWeek = moment().add(+1,'w').format('YYYY-MM-DD')
   futureTimeSearch(msg,query,isKindle, nextWeek, "来週")

 tomorrowSearch = (msg, query , isKindle) ->


retrieve title text sans markup

retrieve title text sans markup

--- client/lib/refresh.coffee
+++ client/lib/refresh.coffee
@@ -147,7 +147,7 @@
     initAddButton pageElement

   createPage = ->
-    title = $("""a[href="/#{slug}.html"]""").html()
+    title = $("""a[href="/#{slug}.html"]""").text()
     title or= slug
     pageHandler.put $(pageElement), {type: 'create', id: util.randomBytes(8), item: {title}}
     buildPage( {title} )


avoid double negation

avoid double negation

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -141,7 +141,7 @@

   moveCursor: (cursor, count=1) ->
     _.times count, =>
-      cursor.moveLeft() unless cursor.isAtBeginningOfLine() and not atom.config.get('vim-mode.wrapLeftRightMotion')
+      cursor.moveLeft() if not cursor.isAtBeginningOfLine() or atom.config.get('vim-mode.wrapLeftRightMotion')
       @ensureCursorIsWithinLine(cursor)

 class MoveRight extends Motion


Style.

Style.

--- client/publication.coffee
+++ client/publication.coffee
@@ -1312,14 +1312,14 @@
   'mouseenter .access .selection': (e, template) ->
     accessHover = $(e.currentTarget).find('input').val()
     $(template.findAll '.access .displayed.description').removeClass('displayed')
-    $(template.findAll ".access .description.#{accessHover}").addClass('displayed')
+    $(template.findAll ".access .description.#{ accessHover }").addClass('displayed')

     return # Make sure CoffeeScript does not return anything

   'mouseleave .access .selections': (e, template) ->
     accessHover = $(template.findAll '.access input:radio:checked').val()
     $(template.findAll '.access .displayed.description').removeClass('displayed')
-    $(template.findAll ".access .description.#{accessHover}").addClass('displayed')
+    $(template.findAll ".access .description.#{ accessHover }").addClass('displayed')

     return # Make sure CoffeeScript does not return anything


fixed small bug

fixed small bug

--- app/assets/javascripts/model.coffee
+++ app/assets/javascripts/model.coffee
@@ -321,7 +321,6 @@

 				console.error("Color (#{colors.length}) and vertices (#{vertices.length / 3}) count doesn't match.", matrix) if vertices.length != colors.length * 3

-				console.error("Hashes don't match.", hash, hash2) if hash != hash2
 				# Then we'll just put the point in to our data structure.
 				for i in [0...colors.length]
 					x = vertices[i * 3]


Set Monday first day on datetime popup

Set Monday first day on datetime popup

Fixes #567

--- app/assets/javascripts/ui.js.coffee
+++ app/assets/javascripts/ui.js.coffee
@@ -18,6 +18,7 @@
   dateTimeOpts = {
     dateFormat: "dd-mm-yy"
     stepMinute: 15
+    firstDay: 1
     showButtonPanel: false
     minDateTime: new Date((new(Date)).setMinutes(0))
   }


Fix jasmine specs

Fix jasmine specs

--- app/assets/javascripts/votes.coffee
+++ app/assets/javascripts/votes.coffee
@@ -15,8 +15,8 @@
   $(this).attr('data-role','delete')
   $(this).attr('action',"/votes/#{data.vote.id}")
   $(this).append($('<input name="_method" type="hidden" value="delete" />'))
-  $(this).find('input.vote').one 'mouseout', ->
-    $(this).removeClass("vote").addClass("unvote")
+  $(this).one 'mouseout', ->
+    $(this).find('input.vote').removeClass("vote").addClass("unvote")
   id = $(this).parent().data('id')
   $(".vote-count[data-id=#{id}]").text(parseInt($(".vote-count[data-id=#{id}]").text()) + 1 )

@@ -26,7 +26,7 @@
   $(this).attr('data-role','create')
   $(this).attr('action',"/votes")
   $(this).find('input[value=delete]').remove()
-  $(this).find('input.vote').one 'mouseout', ->
+  $(this).one 'mouseout', ->
     $(this).find('input.unvote').removeClass("unvote").addClass("vote")
   id = $(this).parent().data('id')
   $(".vote-count[data-id=#{id}]").text(parseInt($(".vote-count[data-id=#{id}]").text()) - 1 )


Remove clipboard class

Remove clipboard class

Position relative and CSS columns don't seem to get along

--- app/assets/js/components/asg.coffee
+++ app/assets/js/components/asg.coffee
@@ -188,7 +188,6 @@
           (button {
             id: this.props.name + "-copy"
             'data-clipboard-text': this.props.name
-            className: "clipboard"
             title: "Copy ASG name to clipboard"
           }, [
             (img {


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -57,7 +57,7 @@
   # the player object. From here you can use the direction and
   # calculate a velocity.

-  enemy.I.v
+  enemy.I.velocity = enemy.I.

   </pre></code>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -3,7 +3,7 @@
     velocity: Point(0, 0)

   follow: (obj) ->
-    I.velocity = obj.I.position().subtract(self.position()).n
+    I.velocity = obj.I.position().subtract(self.position()).norm().scla

   self.bind "update", ->
     I.velocity = player.position().subtract(self.position()).norm().scale(5)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -3,7 +3,7 @@
     velocity: Point(0, 0)

   follow: (obj) ->
-    I.velocity = obj.I.position().su
+    I.velocity = obj.I.position().subtract(

   self.bind "update", ->
     I.velocity = player.position().subtract(self.position()).norm().scale(5)


Handle duplicate key error code 11001

Handle duplicate key error code 11001

--- scripts/sanitize-email/main.coffee
+++ scripts/sanitize-email/main.coffee
@@ -57,7 +57,7 @@
 handleError = (user, err) ->

   switch err.code
-    when 11000 # duplicate key error
+    when 11000, 11001 # duplicate key error
       handleAbuse user
     else
       console.error "error: #{JSON.stringify err}"


Looking for competition_id, not _id

Looking for competition_id, not _id

--- api.coffee
+++ api.coffee
@@ -71,7 +71,7 @@
   listCompetitionMemberships: (competitionId) ->
     console.log "[API] list competition memberships (competitionId=#{competitionId})"
     if competitionId?
-      query = _id: ObjectID(competitionId)
+      query = competition_id: ObjectID(competitionId)
     Q(CompetitionMembership.find(query).exec())

   joinCompetition: (userId, competitionId) ->


Really fix sorting versions

Really fix sorting versions

--- app.coffee
+++ app.coffee
@@ -336,7 +336,7 @@
 								fileCallback err
 								return

-							versions.sort (a, b) -> a.replace(/(\d\.\d\.\d)\./, '$1_') > b.replace(/(\d\.\d\.\d)\./, '$1_')
+							versions.sort (a, b) -> a.replace(/(\d\.\d\.\d)\./, '$1_').localeCompare b.replace(/(\d\.\d\.\d)\./, '$1_')
 							async.eachSeries versions, (versionFile, versionsCallback) ->
 								if versionFile is 'latest'
 									versionsCallback null


add release handler method

add release handler method

--- app.coffee
+++ app.coffee
@@ -1,6 +1,7 @@
 express = require 'express'
 stylus = require 'stylus'
 assets = require 'connect-assets'
+soap = require 'soap'

 app = express.createServer()
 app.use assets()
@@ -10,4 +11,7 @@

 app.get '/', (req, resp) -> resp.render 'index'

+app.get '/releases', (req, resp) ->
+  resp.send 'hi'
+
 app.listen process.env.VMC_APP_PORT or 3000, -> console.log 'Listening...'


Cleanup.

Cleanup.

--- app.coffee
+++ app.coffee
@@ -120,15 +120,15 @@
     if fs.existsSync(process.cwd() + '/lol.launcher.exe')
       GLOBAL.lolInstallPath = process.cwd() + '/Config/Champions/'
       cb null
-
-    # Also check League of Legends.exe in case it's a Garena installation.
-    else if fs.existsSync(process.cwd() + '/League of Legends.exe')
-      GLOBAL.lolInstallPath = process.cwd() + '/Config/Champions/'
-      cb null

     # Garena Installation Check 1
     else if fs.existsSync(process.cwd() + '/LoLLauncher.exe')
       GLOBAL.lolInstallPath = process.cwd() + '/GameData/Apps/LoL/Game/Config/Champions'
+      cb null
+
+    # Garena Installation Check 2
+    else if fs.existsSync(process.cwd() + '/League of Legends.exe')
+      GLOBAL.lolInstallPath = process.cwd() + '/Config/Champions/'
       cb null

     # Default Install


if host has 1 cpu (staging) then set availableWorkingCpus to 1

--- app.coffee
+++ app.coffee
@@ -176,7 +176,12 @@
 		socket.destroy()

 	currentLoad = os.loadavg()[0]
-	availableWorkingCpus = os.cpus().length - 1
+
+	if os.cpus().length == 1
+		availableWorkingCpus = 1
+	else
+		availableWorkingCpus = os.cpus().length - 1
+
 	freeLoad = availableWorkingCpus - currentLoad
 	freeLoadPercentage = Math.round((freeLoad / availableWorkingCpus) * 100)
 	if freeLoadPercentage <= 0


Warn users before leaving with pending uploads

--- scripts/home.coffee
+++ scripts/home.coffee
@@ -97,8 +97,9 @@
     return container

 window.onbeforeunload = ->
-    if false
-        return 'If you leave this page, your uploads will be cancelled.'
+    for f of uploadedFiles
+        if uploadedFiles[f].status not in ['done', 'error', 'ready']
+            return 'If you leave this page, your uploads will be cancelled.'

 handleWorkerMessage = (e) ->
     console.log(JSON.stringify(e.data))


add suggestions if kerb isn't trading

add suggestions if kerb isn't trading

--- scripts/kerb.coffee
+++ scripts/kerb.coffee
@@ -16,6 +16,7 @@
 jsdom = require "jsdom"

 days_of_week = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]
+kerb_alternatives = ["Pret", "Wasabi", "Benito's Hat", "Burrito Café", "Kiosk"]

 getDate = (next_week, day_of_week_str) ->
   today = new Date()
@@ -52,7 +53,8 @@
       message_str += "  " + trader_desc + "\n"

     if message_str is "Traders for " + formatted_date + "\n"
-      message_str = 'No traders found for ' + formatted_date + ', you might have to risk the canteen :('
+      kerb_alternative = kerb_alternatives[Math.floor(Math.random()*kerb_alternatives.length)]
+      message_str = 'No traders found for ' + formatted_date + ', how about heading to ' + kerb_alternative + '?'

     msg.send message_str


fat arrow detach

fat arrow detach

--- src/packages/editor-stats/src/editor-stats-view.coffee
+++ src/packages/editor-stats/src/editor-stats-view.coffee
@@ -58,7 +58,7 @@
     @rootView.append @
     @focus()

-  detach: ->
+  detach: =>
     super()
     @rootView.focus()


Namespace events to vtexjs, not vtex.

Namespace events to vtexjs, not vtex.

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -73,6 +73,7 @@

 	broadcastOrderForm = (orderForm) ->
 		$(window).trigger('vtex.checkout.orderform.update', orderForm)
+		$(window).trigger('vtexjs.checkout.orderform.update', orderForm)

 	orderFormHasExpectedSections = (sections) ->
 		if not @orderForm or not @orderForm instanceof Object


Temporary disable user info subpage

Temporary disable user info subpage

--- coffee/views/posts.coffee
+++ coffee/views/posts.coffee
@@ -42,15 +42,15 @@

       @show = options.show
       @pageUser = options.pageUser
-      @templateData = pageUser: @pageUser
+      #@templateData = pageUser: @pageUser
       # We should manually call render because we don't know about
       # page's user before initialize.
       @render()
-      if @pageUser?
-        # FIXME: Horrible and confused name choice for different
-        # purposes: 'postUser', 'pageUser', 'user', 'getUser'.
-        userInfo = new UserInfoView user: @pageUser, show: @show
-        @subview "user-info", userInfo
+      #if @pageUser?
+      #  # FIXME: Horrible and confused name choice for different
+      #  # purposes: 'postUser', 'pageUser', 'user', 'getUser'.
+      #  userInfo = new UserInfoView user: @pageUser, show: @show
+      #  @subview "user-info", userInfo

       @fetch(true)?.done =>
         @subscribeEvent "!ws:new_message", @onNewPost


Calendar dummy locale

Calendar dummy locale

--- src/calendar.coffee
+++ src/calendar.coffee
@@ -5,9 +5,9 @@
   replace: true
   require: '?ngModel'
   scope: {}
-  controller: ['$scope', ($scope)->
-    $scope.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
-    $scope.weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
+  controller: ['$scope', '$locale', ($scope, $locale)->
+    $scope.months = $locale.DATETIME_FORMATS.SHORTMONTH
+    $scope.weekDays = $locale.DATETIME_FORMATS.SHORTDAY
     currentTime = new Date()
     $scope.currentDate = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate())
     $scope.selectedYear = $scope.currentDate.getFullYear()


Send shutdownServer when configure fails

--- server/serversystem/manager.coffee
+++ server/serversystem/manager.coffee
@@ -109,6 +109,7 @@

 handleFailConfigure = (serv, lobby, instance)->
   console.log "failed to configure server, re-queuing lobby and disabling server"
+  sockets[serv._id].send "shutdownServer|"+instance.id
   removeServerFromPool serv._id
   pendingInstances.remove {id: instance.id}
   lobbyQueue.remove {lobby: lobby._id}


make all tests for hinoki.parseDependencies pass

--- src/hinoki.coffee
+++ src/hinoki.coffee
@@ -3,3 +3,11 @@
     parseDependencies: (fun) ->
         unless 'function' is typeof fun
             throw new Error 'argument must be a function'
+
+        string = fun.toString()
+
+        argumentPart = string.slice(string.indexOf('(') + 1, string.indexOf(')'))
+
+        dependencies = argumentPart.split(/,\s/)
+
+        return if dependencies[0] is '' then [] else dependencies


[AtomReason] Slightly better syntax highlighting.

Summary:

Test Plan:

Reviewers:

CC:

--- editorSupport/language-reason/grammars/reason.cson
+++ editorSupport/language-reason/grammars/reason.cson
@@ -273,12 +273,13 @@
   }
   # let binding.
   {
-    'comment': 'let binding'
-    'match': '\\b(and|(let(\\s+module)?(\\s+rec)?))\\s+([a-zA-Z_][a-zA-Z0-9_]*)?'
+    'comment': 'binding'
+    'match': '\\b(val(\\s+virtual)?|class(\\s+virtual)?|and|method|(let(\\s+module)?(\\s+rec)?))(\\s+mutable)?\\s+([a-zA-Z_][a-zA-Z0-9_]*)?'
     'captures': {
       '1': { 'name': 'keyword.other.fun.reason' }
       '2': { 'name': 'keyword.other.fun.reason' }
       '3': { 'name': 'keyword.other.fun.reason' }
+      '4': { 'name': 'keyword.other.fun.reason' }
       '4': { 'name': 'keyword.other.fun.reason' }
       '5': { 'name': 'entity.name.reason' }
     }


Init metrics at top of app.coffee

Init metrics at top of app.coffee

--- services/clsi/app.coffee
+++ services/clsi/app.coffee
@@ -1,3 +1,6 @@
+Metrics = require "metrics-sharelatex"
+Metrics.initialize("clsi")
+
 CompileController = require "./app/js/CompileController"
 Settings = require "settings-sharelatex"
 logger = require "logger-sharelatex"
@@ -12,8 +15,7 @@
 Path = require "path"
 fs = require "fs"

-Metrics = require "metrics-sharelatex"
-Metrics.initialize("clsi")
+
 Metrics.open_sockets.monitor(logger)
 Metrics.memory.monitor(logger)


Remove some jQuery

Remove some jQuery

--- src/chocolate.coffee
+++ src/chocolate.coffee
@@ -24,6 +24,7 @@
       throw "You don't have defaultOptions or templates variables"
       return false

+    # jQuery
     @options = $.extend defaultOptions, options

     ###
@@ -146,6 +147,7 @@
         @thumbnails.innerHTML = ''
         $(@overlay.querySelector('.choco-thumbnails-toggle')).off 'click'

+      # jQuery
       $(window).off 'resize'

       @current = null
@@ -202,9 +204,7 @@

       @updateDimensions image.width, image.height

-      $(@container).css
-        'background-image': 'url(' + image.source + ')'
-        '-ms-filter': "\"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image.source + "',sizingMethod='scale')\""
+      setStyle @container, "background-image": "url(#{image.source})"

       @header.innerHTML = if image.title then templates['image-title'].replace '{{title}}', image.title else ''
     @
@@ -277,27 +277,27 @@
     top -= headerHeight / 2 if headerHeight > 0

     style =
-      'width':  toInt(innerWidth / 2 - left) + 'px'
-      'height': toInt(innerHeight) + 'px'
-
-    $(@leftside).css  style
-    $(@rightside).css style
+      'width':  "#{toInt(innerWidth / 2 - left)}px"
+      'height': "#{toInt(innerHeight)}px"
+
+    setStyle @leftside,  style
+    setStyle @rightside, style

     if title
       addClass @header, 'choco-show'
-      $(@header).css
-        'width':       toInt width
-        'margin-left': '-' + toInt(left) + 'px'
-        'margin-top':  '-' + toInt(top + headerHeight) + 'px'
+      setStyle @header,
+        'width':       "#{toInt width}px"
+        'margin-left': "-#{toInt left}px"
+        'margin-top':  "-#{toInt(top + headerHeight)}px"

     style =
-      'width':       toInt width
-      'height':      toInt height
-      'margin-left': '-' + toInt(left) + 'px'
-      'margin-top':  '-' + toInt(top) + 'px'
-
-    $(@container).css style
-    $(@spinner).css   style
+      'width':       "#{toInt width}px"
+      'height':      "#{toInt height}px"
+      'margin-left': "-#{toInt left}px"
+      'margin-top':  "-#{toInt top}px"
+
+    setStyle @container, style
+    setStyle @spinner, style
     @

   ###
@@ -317,15 +317,14 @@
       content += template.replace('{{selected}}', selected)
                          .replace('{{cid}}', cid)
                          .replace('{{image}}', image.thumbnail)
-                         .replace('{{title}}', if image.title then ' title="' + image.title + '"' else '')
+                         .replace('{{title}}', if image.title then " title=\"#{image.title}\"" else '')

     $(@thumbnails).html(content).find('.choco-thumbnail').each ->
       thumbnail = $ @
-      image = thumbnail.attr 'data-image'
+      image = @getAttribute 'data-image'

       thumbnail.on 'click', -> _this.open toInt thumbnail.attr 'data-cid'
-      thumbnail.css 'background-image': 'url(' + image + ')'
-      thumbnail.get(0).style['-ms-filter'] = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image + "',sizingMethod='scale')"
+      setStyle @, "background-image": "url(#{image})"

     addHandler @overlay, 'click', ->
       current = _this.images[_this.current]
@@ -470,7 +469,7 @@
    Private method
   ###
   _getInitialParams: ->
-    thumbnails = if not @options.thumbnails then false else $(@thumbnails).height()
+    thumbnails = if not @options.thumbnails then false else @thumbnails.offsetHeight

     css = getStyle @overlay
     horizontal = toInt(css 'padding-left') + toInt(css 'padding-right')
@@ -479,7 +478,7 @@
     header = toInt getStyle(@header)('height')
     if header is 0
       header = 40
-      $(@header).css 'height', header
+      setStyle @header, "height": "#{header}px"

     {horizontal, vertical, thumbnails, header}

@@ -528,29 +527,39 @@
     for ev in event
       target.addEventListener ev, listener, false

+hasClassList = (element, selector) ->
+  target = getTarget element, selector
+  if target? and target.hasOwnProperty 'classList'
+    return target.classList
+  else
+    return false
+
 addClass = (element, className, selector) ->
-  target = getTarget element, selector
-  if target.hasOwnProperty 'classList'
-    target.classList.add className
+  list = hasClassList element, selector
+  list.add className if list

 removeClass = (element, className, selector) ->
-  target = getTarget element, selector
-  if target.hasOwnProperty 'classList'
-    target.classList.remove className
+  list = hasClassList element, selector
+  list.remove className if list

 toggleClass = (element, className, selector) ->
-  target = getTarget element, selector
-  if target.hasOwnProperty 'classList'
-    target.classList.toggle className
+  list = hasClassList element, selector
+  list.toggle className if list

 hasClass = (element, className, selector) ->
-  target = getTarget element, selector
-  if target.hasOwnProperty 'classList'
-    target.classList.contains className
+  list = hasClassList element, selector
+  list.contains className if list

 getStyle = (element) ->
   style = getComputedStyle element
   return (property) -> style.getPropertyValue.call style, property
+
+setStyle = (element, styles) ->
+  properties = Object.keys styles
+  properties.forEach (property) ->
+    prop = property.replace /-([a-z])/g, (g) -> g[1].toUpperCase()
+    element.style[prop] = styles[property]
+

 toInt = (string) -> parseInt string, 10


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -11,7 +11,6 @@
   cooldown: (name, options={}) ->
     options.target ||= 0
     options.approachBy = 1 unless options.approachBy?
-    options

     unless I.cooldowns[name]
       I.cooldowns[name] = options


fixed this context of file watcher

fixed this context of file watcher

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -151,13 +151,13 @@
         @watchers = [callback]

         # this is the filesystem listen routine
-        watcher = (curr, prev) ->
+        watcher = (curr, prev) =>
             return if pending
             pending = yes
             if curr.mtime isnt prev.mtime
                 # modified, wait a little before reloading
                 # since modifications tend to come in waves
-                setTimeout ( ->
+                setTimeout ( =>
                     try
                         do reload for reload in @watchers
                     catch err


Dont reexport injected definitions unless exported

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -1552,12 +1552,15 @@
   definitions = (join (importImplicit ctx), (topLevel ctx, form))
   shouldBeExported = (name, declaration) ->
     # Exporting all to allow for private members to be imported by submodules
-    not declaration.virtual and declaration.final# and declaration.exported
-  isExported = (name, exported) ->
-    exported
-  nonVirtual = filterMap shouldBeExported, ctx._scope()
-  exported = concatSets (subtractSets nonVirtual, builtInDefinitions()),
-    (filterMap isExported, ctx.submodules) # TODO: separate exporting namespaces according to the separation of namespaces
+    #        and declaration.exported
+    not declaration.virtual and
+      (not declaration.injected or declaration.exported) and
+      declaration.final
+  # TODO: separate exporting namespaces according to the separation of namespaces
+  exported = filterMap shouldBeExported, ctx._scope()
+  # deprecated submodules:
+  #    concatSets exported, (filterMap isExported, ctx.submodules)
+  # isExported = (name, exported) -> exported
   exportList = map validIdentifier, (setToArray exported)
   exportDictionary = (jsDictionary exportList, exportList)
   {definitions, exportDictionary}
@@ -3109,6 +3112,7 @@
   macros = objectToMap ms
   for name, macro of values macros
     macro.final = yes
+    macro.injected = yes
   macros

@@ -4807,7 +4811,7 @@
   ]

 builtInDefinitions = ->
-  newMapWith 'True', (type: (quantifyAll toConstrained boolType), arity: []),
+  definitions = newMapWith 'True', (type: (quantifyAll toConstrained boolType), arity: []),
       'False', (type: (quantifyAll toConstrained boolType), arity: [])
       '==', (
         type: (quantifyAll toConstrained (typeFn (atomicType 'a', star), (atomicType 'a', star), boolType)),
@@ -4815,6 +4819,10 @@
       'is-null-or-undefined', (
         type: (quantifyAll toConstrained (typeFn (atomicType 'a', star), boolType)),
         arity: ['x'])
+  for name, definition of values definitions
+    definition.final = yes
+    definition.injected = yes
+  definitions

 # Set/Map implementation

@@ -6498,6 +6506,7 @@
       type: (type if shouldDeclare)
       tempType: type
       importable: isParent or exported
+      injected: true
       id: ctx.freshId()}
     if implicit
       if exported


* update attributes unit test

* update attributes unit test

--- test/attributes-test.coffee
+++ test/attributes-test.coffee
@@ -10,11 +10,14 @@
 Attributes      = require '../src/attributes/'
 setImmediate    = setImmediate || process.nextTick
 register        = TypeInfo.register
+attrs = null

 class TestType
   constructor: ->return super
-  $attributes: Attributes
+  $attributes: attrs = Attributes
+    haha: 'Boolean'
     min:
+      name: 'min'
       type: 'Number'
       value: 1
     max:
@@ -28,6 +31,7 @@
     result.should.be.true
   after ->
     TypeInfo.unregister 'Test'
+
   it "should get type info object directly", ->
     t = TestType()
     should.exist t
@@ -53,3 +57,51 @@
     t.should.have.property 'min', 1
     t.should.have.property 'max', 2
     t.should.have.property 'name', 'Test'
+  it "should change the type attributes name", ->
+    attrs.initialize
+      min:
+        name: '最小'
+    t = attrs.min.value
+    should.exist t
+    t.should.be.equal 1
+    t = attrs.min.type
+    should.exist t
+    t.should.be.equal 'Number'
+    t = TestType '最小':2
+    should.exist t
+    t.should.be.not.equal TypeInfo('Test')
+    t.should.have.property '最小', 2
+    t.should.not.have.property 'max'
+    t.should.have.property 'name', 'Test'
+    t = TestType 'min':6
+    should.exist t
+    t.should.be.not.equal TypeInfo('Test')
+    t.should.have.property '最小', 6
+    t.should.not.have.property 'max'
+    t.should.have.property 'name', 'Test'
+    # restore the old name
+    attrs.initialize
+      min:
+        name: 'min'
+  it "should get proper $attributes", ->
+    result = {}
+    for k, v of attrs
+      result[k] = v if attrs.hasOwnProperty k
+    result.should.be.deep.equal
+      name:
+        name: 'name'
+        "required": true
+        "type": "String"
+      required:
+        "name": "required"
+        "type": "Boolean"
+      haha:
+        type: 'Boolean'
+      min:
+        name: 'min'
+        type: 'Number'
+        value: 1
+      max:
+        type:
+          name: 'Number'
+          max: 3


not great, but JS doesn't like names with . in them

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -302,7 +302,7 @@
 }

 root.compile = (class_file) ->
-  class_name = class_file.this_class.toExternalString()
+  class_name = class_file.this_class.toExternalString().replace /\./g, '_'
   methods =
     for sig, m of class_file.methods
       unless m.access_flags.native or m.access_flags.abstract


Add lifecycle

Add lifecycle

--- views/services/plugin-manager.cjsx
+++ views/services/plugin-manager.cjsx
@@ -83,6 +83,9 @@
   readPlugins: async (opt_notifyFailed) ->
     pluginPaths = yield Promise.promisify(globAsync)(path.join @pluginPath, 'node_modules', 'poi-plugin-*')
     @plugins_ = pluginPaths.map @readPlugin_
+    for plugin_ in @plugins_
+      if config.get "plugin.#{plugin_.name}.enable", true
+        @loadPlugin(plugin_)
     if opt_notifyFailed
       @notifyFailed_()
     @plugins_ = _.sortBy @plugins_, 'priority'
@@ -299,9 +302,10 @@
     plugin.isUpdating = true
     try
       yield Promise.promisify(npm.commands.install)(["#{plugin.packageName}@#{plugin.lastestVersion}"])
-      plugin.isUpdating = false
-      plugin.isOutdated = false
-      plugin.version = plugin.lastestVersion
+      #plugin.isUpdating = false
+      #plugin.isOutdated = false
+      #plugin.version = plugin.lastestVersion
+      @reloadPlugin(plugin)
     catch error
       plugin.isUpdating = false
       throw error
@@ -322,12 +326,11 @@
         plugin = readPlugins.find (plugin_) -> packName == plugin_.packageName
         if plugin?
           # If the installed plugin is one of the existing plugins
-          plugin.version = packVersion
-          plugin.isOutdated = semver.gt plugin.lastestVersion, packVersion
+          if plugin.version != packVersion
+            @reloadPlugin(plugin)
         else
           # If the installed plugin is a new plugin
-          plugin = @readPlugin_ path.join @pluginPath, 'node_modules', packName
-          @plugins_.push plugin
+          @addPlugin(path.join @pluginPath, 'node_modules', packName)
           break
       @plugins_ = _.sortBy @plugins_, 'priority'
     catch error
@@ -342,10 +345,8 @@
     yield @getMirrors()
     try
       yield Promise.promisify(npm.commands.uninstall)([plugin.packageName])
-      for plugin_, index in @plugins_
-        if plugin.packageName is plugin_.packageName
-          @plugins_.splice(index, 1)
-          break
+      @unloadPlugin(plugin)
+      @removePlugin(plugin)
     catch error
       console.log "uninstallPlugin error: #{error}"
       console.log error.stack
@@ -355,11 +356,50 @@
   # @param {Plugin} plugin
   enablePlugin: (plugin) ->
     config.set "plugin.#{plugin.name}.enable", true
+    @loadPlugin(plugin)

   # disable one plugin
   # @param {Plugin} plugin
   disablePlugin: (plugin) ->
     config.set "plugin.#{plugin.name}.enable", false
+    @unloadPlugin(plugin)
+
+  # load one plugin
+  # @param {Plugin} plugin
+  loadPlugin: (plugin) ->
+    if plugin?.pluginDidLoad? then plugin.pluginDidLoad()
+
+  # unload one plugin
+  # @param {Plugin} plugin
+  unloadPlugin: (plugin) ->
+    if plugin.pluginShouldUnload? then plugin.pluginShouldUnload()
+
+  removePlugin: (plugin) ->
+    delete require.cache[require.resolve plugin.pluginPath]
+    for plugin_, index in @plugins_
+      if plugin.packageName == plugin_.packageName
+        @plugins_.splice(index, 1)
+        break
+
+  addPlugin: (pluginPath) ->
+    plugin = @readPlugin_ pluginPath
+    @plugins_.push plugin
+    @plugins_ = _.sortBy @plugins_, 'priority'
+    if config.get "plugin.#{plugin.name}.enable", true
+      @loadPlugin(plugin)
+
+  reloadPlugin: (plugin) ->
+    @unloadPlugin(plugin)
+    newPlugin = {}
+    delete require.cache[require.resolve plugin.pluginPath]
+    for plugin_, index in @plugins_
+      if plugin.packageName == plugin_.packageName
+        newPlugin = @readPlugin_ plugin.pluginPath
+        @plugins_[index] = newPlugin
+        break
+    if config.get "plugin.#{newPlugin.name}.enable", true
+      @loadPlugin(newPlugin)
+    @plugins_ = _.sortBy @plugins_, 'priority'

   # read a plugin from file system
   # @param {string} pluginPath path to a plugin directory
@@ -379,6 +419,8 @@
     catch error
       plugin = isRead: false
       plugin.version = '0.0.0'
+
+    plugin.pluginPath = pluginPath

     try
       plugin.packageData = fs.readJsonSync path.join pluginPath, 'package.json'


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -9,6 +9,7 @@
       I[name] = I[name].approach(target, approachBy)

   cooldown: (name, options={}) ->
+
     options.target ||= 0
     options.approachBy = 1 unless options.approachBy?


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -6,8 +6,8 @@
     player = GameObject
       health: 50

-    # health will approach 100
-    # by 1 each update
+    # health will approach
+    # 100 by 1 each update
     player.cooldown "health",
       target: 100


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -12,7 +12,7 @@
     player.update(1)

     player.I.health
-    # =>
+    # => 51

     # Shoot Timeout
     player = GameObject()


fixed bug with map route view being initialized on each leg click

--- web/js/views/map_route_view.coffee
+++ web/js/views/map_route_view.coffee
@@ -2,9 +2,9 @@

   class MapRouteView

-    constructor: ({routes, routeParams, index, map}) ->
-      @legViews = for leg, legIndex in routes.at(index).get('legs')
-        new MapRouteLegView(routes: routes, routeParams: routeParams, routeIndex: index, index: legIndex, map: map)
+    constructor: ({routes, routeParams, @index, map}) ->
+      @legViews = for leg, legIndex in routes.at(@index).get('legs')
+        new MapRouteLegView(routes: routes, routeParams: routeParams, routeIndex: @index, index: legIndex, map: map)

     dispose: ->
       legView.dispose() for legView in @legViews


Fix error in comment

Fix error in comment

--- src/lambda.coffee
+++ src/lambda.coffee
@@ -123,7 +123,7 @@
       # if y is free in S and x is free in E, then must α-convert λy.E to avoid
       # name conflicts.
       if (freeIn t.varName, s) and (freeIn x, t.body)
-        # (λy.E)[x := S] = λy'.(E[y := y'][y' := S])
+        # (λy.E)[x := S] = λy'.(E[y := y'][x := S])
         # TODO This is alpha-conversion. Multiple of them can happen inside the same beta-reduction.
         substitute (renameAbstractionVar t, s), x, s
       else


making minor script changes

making minor script changes

--- src/incident.coffee
+++ src/incident.coffee
@@ -1,24 +1,24 @@
 # Description:
-# #  OPrderly incident coordination through hubot
-# #
-# # Dependencies:
-# #   "<module name>": "<module version>"
-# #
-# # Configuration:
-# #   HUBOT_GITHUB_TOKEN (required)
-# #   HUBOT_GITHUB_ORG (required)
-# #
-# # Commands:
-# #   hubot incident start - Initiate incident
-# #   hubot incident ack - Aknowledge started incident
-# #   hubot incident end - Resolve incident
-# #
-# # Notes:
-# #   <optional notes required for the script>
-# #
-# # Authors:
-# #   Aaron Blythe
-# #   Levi Smith
+#  Orderly incident coordination through hubot
+#
+# Dependencies:
+#   "<module name>": "<module version>"
+#
+# Configuration:
+#   HUBOT_GITHUB_TOKEN (required)
+#   HUBOT_GITHUB_ORG (required)
+#
+# Commands:
+#   hubot incident start - Initiate incident
+#   hubot incident ack - Aknowledge started incident
+#   hubot incident end - Resolve incident
+#
+# Notes:
+#   <optional notes required for the script>
+#
+# Authors:
+#   Aaron Blythe
+#   Levi Smith

 #GitHubApi = require "github"
@@ -29,15 +29,15 @@

   robot.respond /incident (start|initiate)/i, (msg) ->
     # Probably set to the brain via the notify portion of the bot in non-hacky version
-    robot.brain.set 'incidentNumber', @_randomNum(10,5)
+    robot.brain.set 'incidentNumber', 8675309
     robot.brain.get('incidentNumber')
-    msg.reply "INCIDENT NOTIFY: Incident #{incidentNumber} has been started"
+    msg.send "INCIDENT NOTIFY: Incident #{incidentNumber} has been started"

   robot.respond /incident (ack|acknowledge)/i, (msg) ->
     robot.brain.get('incidentNumber')
-    msg.reply "INCIDENT NOTIFY: Acknowledged incident #{incidentNumber}"
+    msg.send "INCIDENT NOTIFY: Acknowledged incident #{incidentNumber}"

   robot.respond /incident (end|resolve)/i, (msg) ->
     robot.brain.get('incidentNumber')
-    msg.reply "INCIDENT NOTIFY: Resolved incident #{incidentNumber}"
+    msg.send "INCIDENT NOTIFY: Resolved incident #{incidentNumber}"


comment fix

comment fix

--- src/listener.iced
+++ src/listener.iced
@@ -76,7 +76,7 @@
   ##-----------------------------------------

   got_new_connection : (x) ->
-    throw new Error "@got_new_connection() is abstract; please implement!"
+    throw new Error "@got_new_connection() is pure virtual; please implement!"

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


Clean up

Clean up

--- src/logger.coffee
+++ src/logger.coffee
@@ -11,10 +11,6 @@
 require 'sugar'

 exports.Logger = {
-  log: []
-  init: ()->
-    @log = []
-    return this
   completed: ()->
     return @log.join("\n")
   append: (type, msg)->
@@ -41,4 +37,4 @@
   todo: (msg)->
     @append chalk.bgRed.black(" TODO "), msg

-}.init()+}


This goes with the previous commit about moving getNonOccupyingLength

--- src/logger.coffee
+++ src/logger.coffee
@@ -1,11 +1,9 @@
 _ = require 'lodash'
 { clearLine, cursorTo } = require 'readline'
-{ stripColor } = require 'chalk'
 { inspect } = require 'util'

+{ getNonOcuppyingLength } = require './utils'
 styler = require './styler'
-
-module.exports = @

 # Alias
 { stdout, stderr } = process
@@ -19,7 +17,6 @@
 MINIMUM_COLS = 15

 # Private methods
-getNonOcuppyingLength = (str) -> str.length - stripColor(str).length

 tag = (txt, style, tagString) -> style(_.padEnd(txt, LEFT_MARGIN)) + tagString

@@ -64,7 +61,7 @@
 # Exposed methods
 @write = (str, stream = stdout) => stream.write str; @

-@endLine = (stream = stdout) => stream.write "\n"; @
+@endLine = (stream = stdout) => stream.write "\n"; @openUpdate = no; @

 @eraseLine = (stream = stdout) => clearLine(stream); cursorTo(stream, 0); @


Performance fix from https://github.com/atom/tree-view/pull/525

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -222,6 +222,7 @@

   onDragStart: (event) ->
     return unless matches(event.target, '.sortable')
+    @lastDropTargetIndex = null

     event.dataTransfer.setData 'atom-event', 'true'

@@ -264,7 +265,7 @@
       false

   onDragLeave: (event) ->
-    @removePlaceholder()
+    @removePlaceholder() if e.target is e.currentTarget

   onDragEnd: (event) ->
     return unless matches(event.target, '.sortable')
@@ -280,6 +281,8 @@
     event.preventDefault()
     newDropTargetIndex = @getDropTargetIndex(event)
     return unless newDropTargetIndex?
+    return if @lastDropTargetIndex is newDropTargetIndex
+    @lastDropTargetIndex = newDropTargetIndex

     @removeDropTargetClasses()


Change themes icons and placeholder text

--- lib/themes-panel.coffee
+++ lib/themes-panel.coffee
@@ -36,28 +36,28 @@

       @section class: 'section', =>
         @div class: 'section-container', =>
-          @div class: 'section-heading icon icon-package', =>
+          @div class: 'section-heading icon icon-paintcan', =>
             @text 'Installed Themes'
             @span outlet: 'totalPackages', class:'section-heading-count', ' (…)'
           @div class: 'editor-container', =>
-            @subview 'filterEditor', new TextEditorView(mini: true, placeholderText: 'Filter packages by name')
+            @subview 'filterEditor', new TextEditorView(mini: true, placeholderText: 'Filter themes by name')

           @section class: 'sub-section installed-packages', =>
-            @h3 class: 'sub-section-heading icon icon-package', =>
+            @h3 class: 'sub-section-heading icon icon-paintcan', =>
               @text 'Community Themes'
               @span outlet: 'communityCount', class:'section-heading-count', ' (…)'
             @div outlet: 'communityPackages', class: 'container package-container', =>
               @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading themes…"

           @section class: 'sub-section core-packages', =>
-            @h3 class: 'sub-section-heading icon icon-package', =>
+            @h3 class: 'sub-section-heading icon icon-paintcan', =>
               @text 'Core Themes'
               @span outlet: 'coreCount', class:'section-heading-count', ' (…)'
             @div outlet: 'corePackages', class: 'container package-container', =>
               @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading themes…"

           @section class: 'sub-section dev-packages', =>
-            @h3 class: 'sub-section-heading icon icon-package', =>
+            @h3 class: 'sub-section-heading icon icon-paintcan', =>
               @text 'Development Themes'
               @span outlet: 'devCount', class:'section-heading-count', ' (…)'
             @div outlet: 'devPackages', class: 'container package-container', =>


Added public to functions

Added public to functions

--- snippets/php_fields.cson
+++ snippets/php_fields.cson
@@ -153,7 +153,7 @@
      * CMS Fields
      * @return FieldList
      */
-    function getCMSFields()
+    public function getCMSFields()
     {
     \t\\$fields = parent::getCMSFields();
     \t\\$fields->$0
@@ -167,7 +167,7 @@
      * Update Fields
      * @return FieldList
      */
-    function updateCMSFields(FieldList \\$fields)
+    public function updateCMSFields(FieldList \\$fields)
     {
     \t\\$fields->$0
     \treturn \\$fields;


Reading less frames per second avoids the issue of multiple faces detected when only one exists due to the same frame being updated while image detection processing is executed.

--- examples/coffeescript/camera-face-detection.coffee
+++ examples/coffeescript/camera-face-detection.coffee
@@ -41,9 +41,9 @@

     # Finally we get the key pressed on the window to terminate
     # execution of the program.
-    res = namedWindow.blockingWaitKey(0, 20)
+    res = namedWindow.blockingWaitKey(0, 50)

     # In this case I terminate the program if any key is pressed.
     if res >= 0 then clearInterval(intervalId)
   )
-, 50)
+, 150)


Disable Twilio validation because it keeps failing

--- server/index.coffee
+++ server/index.coffee
@@ -30,7 +30,9 @@
 .use("/api/bot", botService)
 # Public static content
 .use(feathers.static(publicPath))
-.post("/api/sms", twilio.webhook(config.twilio.authToken), twilioService.receive)
+.post("/api/sms", twilio.webhook(config.twilio.authToken, {
+  validate: false
+}), twilioService.receive)
 # Start server listening
 .listen config.server.port, ->
   console.log "Listening on http://localhost:" + config.server.port


Add log

Add log

--- app/src/storage/synced_store.coffee
+++ app/src/storage/synced_store.coffee
@@ -218,7 +218,7 @@
       if e is Errors.NoChanges
         @_clearChanges()
         return
-      l "Failed push ", e
+      $info "Failed push ", e
       @push() # Retry later
       throw e
     @_deferredPush.resolve(p)


fix a few small bugs

fix a few small bugs

--- src/math3d.coffee
+++ src/math3d.coffee
@@ -111,7 +111,7 @@
 math3d.fontSources = []

 math3d.loadThreejs = (callback) ->
-    if THREE? and _orbitControlsSetup and not math3d.fontsSources.length
+    if THREE? and _orbitControlsSetup and not math3d.fontSources.length
         return callback()

     _loadingThreejsCallbacks.push callback
@@ -734,6 +734,10 @@
         if @frameOpts.thickness isnt 0 and not @_bounded
             @_bounded = true

+            if not @boundingBox?
+                # no objects in the scene
+                return
+
             # set the color and linewidth of the bounding box
             @boundingBox.material.color = @frameColor
             @boundingBox.material.linewidth = @frameOpts.thickness
@@ -793,7 +797,6 @@
                             loc         : loc
                             text        : text
                             size        : textSize
-                            in_frame    : false
                             texture     :
                                     color   : frameColor
                                     opacity : 1


modified

modified

--- src/jsonsina.coffee
+++ src/jsonsina.coffee
@@ -43,14 +43,15 @@
         callback error, arr
         return

-      for each in arr?
-        each.day = new Date each.day
-        each.open = Number each.open
-        each.low = Number each.low
-        each.high = Number each.high
-        each.close = Number each.close
-        each.volume = Number each.volume
-        #console.log each
+      if arr # 必須這樣寫,不能簡化為: for each in arr?
+        for each in arr
+          each.day = new Date each.day
+          each.open = Number each.open
+          each.low = Number each.low
+          each.high = Number each.high
+          each.close = Number each.close
+          each.volume = Number each.volume
+          #console.log each

       callback err, arr


Remove dev-feed from level change messages

--- server/slack.coffee
+++ server/slack.coffee
@@ -8,7 +8,7 @@

 module.exports.sendChangedSlackMessage = (options) ->
   message = "#{options.creator.get('name')} saved a change to #{options.target.get('name')}: #{options.target.get('commitMessage') or '(no commit message)'} #{options.docLink}"
-  rooms = if /Diplomat submission/.test(message) then ['dev-feed'] else ['dev-feed', 'artisans']
+  rooms = if /Diplomat submission/.test(message) then ['dev-feed'] else ['artisans']
   @sendSlackMessage message, rooms

 module.exports.sendSlackMessage = (message, rooms=['tower'], options={}) ->


Hubot service status shows correct colors (#109)

--- vagrant/provision/roles/hubot/files/ninjabot/scripts/service.coffee
+++ vagrant/provision/roles/hubot/files/ninjabot/scripts/service.coffee
@@ -72,10 +72,10 @@
     if stdout? && stdout != ''
       stdout = stdout.replace /-classpath.*\n/, ""
       status = ""
-      if (stdout.match("/Active: failed/") || stdout.match("/inactive/"))
+      if stdout.match(/Active: active \(running\)/gi)
+        status = ":white_check_mark:"
+      else
         status = ":red_circle:"
-      else
-        status = ":white_check_mark:"
       respond msg, stdout, status
     if stderr? && stderr != ''
       respond msg, stderr, ":red_circle:"


Add speed as an option.

Add speed as an option.

--- js/flight.coffee
+++ js/flight.coffee
@@ -6,15 +6,19 @@
   fadeAnimation: true
   currentPanel: ''
   targetPanel: ''
+  speed: '0.4s'

   constructor: (options) ->
     # set options
     if options?
       if options["transitionAnimation"]?
-        @["transitionAnimation"] = options["transitionAnimation"]
+        @transitionAnimation = options["transitionAnimation"]

       if options["fadeAnimation"]?
-        @["fadeAnimation"] = options["fadeAnimation"]
+        @fadeAnimation = options["fadeAnimation"]
+
+      if options["speed"]?
+        @speed = options["speed"]

     @currentPanel = document.getElementsByClassName('visible')[0]


Add log points

Add log points

--- scripts/shoot.coffee
+++ scripts/shoot.coffee
@@ -9,6 +9,8 @@

     return if data.user.screen_name isnt from
     return if data.entities.user_mentions.length
+
+    console.log "Checking tweet: \"#{text}\""

     switch

@@ -29,3 +31,5 @@

     if Math.random() < rate
       msg.send text
+
+      console.log "Updated status: \"#{text}\""


Patch from 207 merge request

Patch from 207 merge request

--- app/assets/javascripts/backend/purchase_orders.js.coffee
+++ app/assets/javascripts/backend/purchase_orders.js.coffee
@@ -35,7 +35,7 @@

     $(document).on 'click', '.nested-fields .edit-item[data-edit="item-form"]', (event) ->
       vatSelectedValue = $(event.target).closest('.nested-fields').find('.item-display .vat-rate').attr('data-selected-value')
-      $(event.target).closest('.nested-fields').find('.nested-item-form:visible .order-vat-total').val(vatSelectedValue)
+      $(event.target).closest('.nested-fields').find('.nested-item-form:visible .order-vat-total').val(vatSelectedValue) unless vatSelectedValue == undefined

     $(document).on 'selector:change', 'input#purchase_invoice_supplier_id', ->
       supplier_id = $(this).parent().find('.selector-value').val()


remove debug statement

remove debug statement

--- app/assets/javascripts/behaviors/toggler_behavior.coffee
+++ app/assets/javascripts/behaviors/toggler_behavior.coffee
@@ -7,7 +7,6 @@
   #   %div.js-toggle-content
   #
   $("body").on "click", ".js-toggle-button", (e) ->
-    console.log(e);
     $(@).find('i').
       toggleClass('fa fa-chevron-down').
       toggleClass('fa fa-chevron-up')


JCredential::fetchByIdentifier: remove re-setting the options since we've fixed the issue in the first place

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -164,14 +164,6 @@
       JGroup.one { slug: group }, (err, group) ->
         return callback err  if err?

-        # FIXME Dunno why but `options` variable is overriding by fetcher
-        # in bongo above, so I had to re-set it here. When we fix the
-        # fetcher issue in bongo this won't be needed anymore, thanks JS ~ GG
-        options =
-          limit         : 1
-          targetOptions :
-            selector    : { identifier }
-
         group.fetchCredential {}, options, (err, res) ->
           callback err, res


Fix increaseIndentPattern

Fix increaseIndentPattern

--- generators/template/gherkin_settings_template.cson
+++ generators/template/gherkin_settings_template.cson
@@ -12,7 +12,5 @@
 __BUT__
 __AND__
     ]
-    'increaseIndentPattern': [
-__INCREASEINDENTPATTERNKEY__
-    ]
+    'increaseIndentPattern': __INCREASEINDENTPATTERNKEY__
     'commentStart': '# '


Consolidate callWithScope calls in Player to callWithMagicScope.

--- application/chrome/content/wesabe/download/Player.coffee
+++ application/chrome/content/wesabe/download/Player.coffee
@@ -201,20 +201,7 @@

         wesabe.info 'History is ', (hi.name for hi in @history).join(' -> ')

-        wesabe.lang.func.callWithScope fn, this, wesabe.lang.extend(
-          browser: browser
-          page: page
-          e: @constructor.elements
-          answers: @answers
-          options: @job.options
-          log: log
-          tmp: @tmp
-          action: @getActionProxy(browser, page)
-          job: @getJobProxy()
-          skipAccount: @skipAccount
-          reload: => @triggerDispatch(browser, page)
-          download: (args...) => @download(args...)
-        , scope or {})
+        @callWithMagicScope fn, browser, page, wesabe.lang.extend({log}, scope or {})

     @job.timer.start 'Navigate', overlap: off

@@ -462,21 +449,7 @@
           callbacks = @["#{type}ReceivedCallbacks"]
           if callbacks
             for callback in callbacks
-              wesabe.lang.func.callWithScope callback, this,
-                message: message
-                browser: browser
-                page: page
-                e: @constructor.elements
-                answers: @answers
-                options: @job.options
-                log: wesabe
-                tmp: @tmp
-                action: @getActionProxy browser, page
-                job: @getJobProxy()
-                reload: => @triggerDispatch browser, page
-                skipAccount: @skipAccount
-                download: (args...) => @download args...
-              , [message]
+              @callWithMagicScope callback, browser, page, wesabe.lang.extend({message, log: wesabe}), message

     unless @shouldDispatch browser, page
       wesabe.info 'skipping document load'
@@ -510,20 +483,7 @@
       for dispatch in @dispatches
         result = wesabe.tryThrow "#{module}#dispatch(#{dispatch.name})", (log) =>
           @job.timer.start 'Dispatch', overlap: off
-
-          wesabe.lang.func.callWithScope dispatch.callback, this,
-            browser: browser
-            page: page
-            e: @constructor.elements
-            answers: @answers
-            options: @job.options
-            log: log
-            tmp: @tmp
-            action: @getActionProxy browser, page
-            job: @getJobProxy()
-            reload: => @triggerDispatch browser, page
-            skipAccount: @skipAccount
-            download: (args...) => @download args...
+          @callWithMagicScope dispatch.callback, browser, page, {log}

         if result is false
           wesabe.info "dispatch chain halted"
@@ -537,19 +497,11 @@
   shouldDispatch: (browser, page) ->
     for filter in @filters
       result = wesabe.tryCatch "#{@constructor.fid}#filter(#{filter.name})", (log) =>
-        r = wesabe.lang.func.callWithScope filter.test, this,
-          browser: browser
-          page: page
-          e: @constructor.elements
-          log: log
-          tmp: @tmp
-          job: @getJobProxy()
-          skipAccount: @skipAccount
-
-        if r is true
-          log.debug "forcing dispatch"
-        else if r is false
-          log.debug "aborting dispatch"
+        switch r = @callWithMagicScope filter.test, browser, page, {log}
+          when true
+            log.debug "forcing dispatch"
+          when false
+            log.debug "aborting dispatch"

         return r

@@ -558,6 +510,22 @@

     wesabe.debug "no filter voted to force or abort dispatch, so forcing dispatch by default"
     return true
+
+  callWithMagicScope: (fn, browser, page, scope, args...) ->
+    wesabe.lang.func.callWithScope fn, this, wesabe.lang.extend({
+      browser
+      page
+      e: @constructor.elements
+      answers: @answers
+      options: @job.options
+      tmp: @tmp
+      action: @getActionProxy browser, page
+      job: @getJobProxy()
+      skipAccount: @skipAccount
+      reload: => @triggerDispatch browser, page
+      download: (args...) => @download args...
+    }, scope or {}), args...
+

   @::__defineGetter__ 'history', ->
     @_history ||= []


remove unused 'renderModule' method in room

--- src/core/room.coffee
+++ src/core/room.coffee
@@ -76,15 +76,6 @@
             else
                 res.send 200

-    renderModule    : (req, res) =>
-        user        = req.currentUser
-        options     =
-            title   : 'Voicious'
-            login   : user.name
-            room    : req.params.roomid
-            trans   : Translator.getTrans(req.host, 'room')
-        res.render "modules/#{req.body.module}.jade", options
-
     reportBug       : (req, res) =>
         @transport.sendMail({
             from    : "Voicious bugs<no-reply@voicious.com>"
@@ -117,4 +108,3 @@
         '/room/:roomid' : (Session.ifUser.curry exports.Room.roomPage, exports.Room.redirectRoom)
     post :
         '/report'       : exports.Room.reportBug
-        '/renderModule' : exports.Room.renderModule


cascade: test undefined and null

cascade: test undefined and null

--- test/options/cascade.coffee
+++ test/options/cascade.coffee
@@ -178,3 +178,26 @@
         @call ({options}) ->
           (options.an_option is undefined).should.be.true()
       .promise()
+
+  describe 'values', ->
+
+    it 'discard undefined values', ->
+      nikita
+        cascade:
+          an_option: true
+      .call
+        an_option: 'is preserved'
+      , ({options}) ->
+        @call an_option: undefined, ({options}) ->
+          options.an_option.should.eql 'is preserved'
+      .promise()
+
+    it 'cascade null values', ->
+      nikita
+        cascade: ['an_option']
+      .call
+        an_option: 'is overwritten by null'
+      , ({options}) ->
+        @call an_option: null, ({options}) ->
+          (options.an_option is null).should.be.true()
+      .promise()


Added doc comment and replaceAll default value

--- .metalsmith/plugins/autodate.coffee
+++ .metalsmith/plugins/autodate.coffee
@@ -6,7 +6,16 @@
 #

+###
+# @param {Object} opts
+# @param {Boolean} opts.replaceAll If true, Replace all of the `date`
+#   values. If false, add `date` values only if it is null.
+# @param {Boolean} opts.trimTime Remove the hours/minutes/etc from time
+#   values if they exist. This is useful to avoid the exact modified time,
+#   which might be just seconds ago.
+###
 module.exports = (opts={}) ->
+  opts.replaceAll ?= false
   opts.trimTime   ?= true
   (files, metalsmith, done) ->
     for _, file of files


Change directory to package server home on start

--- app.coffee
+++ app.coffee
@@ -18,6 +18,7 @@
 panic = -> throw new Error "Cowardly refusing to keep the process alive as root"
 panic() if process.getuid?() is 0 or process.getgid?() is 0

+process.chdir __dirname
 serverVersion = (require './package.json').version
 (require 'child_process').exec 'git describe --always', (err, stdout, stderr) -> serverVersion = stdout.trim() unless err?


Load static files from /var/www/html

Load static files from /var/www/html

--- app.coffee
+++ app.coffee
@@ -12,7 +12,7 @@

 # Serve up static files in the public folder
 app.configure ->
-  app.use express.static(__dirname + '/public')
+  app.use express.static('/var/www/html/nitro/public')
   app.use express.bodyParser()

   # Allow CORS


downloadRawHTML abstracted into its own method

--- app.coffee
+++ app.coffee
@@ -16,6 +16,35 @@
     row.find('.name').text(filename)
     row.find('.size').text(filesize)
     row.find('.error').text(error) if error
+    row
+
+  downloadRowHTML = (file) ->
+    row = $(
+      '
' +
+      '' +
+      '' +
+      '' +
+      '' +
+      '	' +
+      '	' +
+      '' +
+      '' +
+      '	' +
+      "
"
+    )
+
+    row.find('.name').text(file.name)
+    row.find('.size').text('-' + file.size) if file.size
+    # update the icon
+    $(row.find("img.s_page_white_get")[0]).addClass('s_' + file.icon).removeClass("s_page_white_get") if file.icon
+    if file.error
+      console.log "there are errors in the file: "
+      console.log file.error_message
+      console.log file.error
+      row.find('.info-col').addClass('error').text(file.error)
+      row.find('.status_image').removeClass('s_synced').addClass('s_error')
+      row.find('.image_icon').removeClass('s_page_white_get').addClass('s_cross')
+      console.log file.error_class
     row

   $('#upload').fileupload({
@@ -47,38 +76,14 @@

         console.log("filename = " + file.name)
         console.log("size = " + file.bytes)
-        row = $(
-          '
' +
-          '' +
-          '' +
-          '' +
-          '' +
-          '	' +
-          '	' +
-          '' +
-          '' +
-          '	' +
-          "
"
-        )
-
-        row.find('.name').text(file.name)
-        row.find('.size').text('-' + o.formatFileSize(file.size)) if file.size
-        # update the icon
-        $(row.find("img.s_page_white_get")[0]).addClass('s_' + file.icon).removeClass("s_page_white_get") if file.icon
-        if file.error
-          console.log "there are errors in the file: "
-          console.log file.error_message
-          console.log file.error
-          row.find('.info-col').addClass('error').text(file.error)
-          row.find('.status_image').removeClass('s_synced').addClass('s_error')
-          row.find('.image_icon').removeClass('s_page_white_get').addClass('s_cross')
-          console.log file.error_class
-          if file.error_class == 'DropboxAuthError'
-            console.log "it's an authentication error!"
-            $('#re-authenticate').show()
-            $('#upload_button').addClass('disabled')
-            $('#upload_button input').prop("disabled", true)
-        rows = rows.add(row)
+
+        if file.error && file.error_class == 'DropboxAuthError'
+          console.log "it's an authentication error!"
+          $('#re-authenticate').show()
+          $('#upload_button').addClass('disabled')
+          $('#upload_button input').prop("disabled", true)
+        # file.size = o.formatFileSize(file.size) if file.size
+        rows = rows.add(downloadRowHTML(file))
       return rows
     # done: (e,data) ->
     #   console.log e


Updating with correct loggers.

Updating with correct loggers.

--- app.coffee
+++ app.coffee
@@ -29,8 +29,8 @@
 )
 app.post("/incoming", (request, response) ->
   im = new IncomingMessage(request.body)
-  console.log "from: #{im.from}"
-  console.log "body: #{im.body}"
+  console.log "from: #{im.from()}"
+  console.log "body: #{im.body()}"
 )

 port = nconf.get("PORT") || 5000


Try anonymous.

Try anonymous.

--- app.coffee
+++ app.coffee
@@ -37,18 +37,20 @@
 class App

     # from dev.json
-    dev = $blab.resource "dev"
-        #token: dev.token
-        #gistId: dev.gistId
-        username: "anonymous"
-        password:
-        auth: "basic"
+    #dev = $blab.resource "dev"
+    #token: dev.token
+    #gistId: dev.gistId

     constructor: ->

+        #github = new Github
+        #    token: @token
+        #    auth: "oauth"
+
         github = new Github
-            token: @token
-            auth: "oauth"
+            username: "anonymous"
+            password:
+            auth: "basic"

         @gist = github.getGist(@gistId)


support for operations isnt

support for operations isnt

--- sexprs_logic.coffee
+++ sexprs_logic.coffee
@@ -150,6 +150,12 @@
   if @val instanceof Date and kval instanceof Date
    return @val.getTime() is kval.getTime()
   return kval is @val
+
+ oper_isnt = (obj) ->
+  kval = cast @type, keyVal obj, @keys
+  if @val instanceof Date and kval instanceof Date
+   return @val.getTime() isnt kval.getTime()
+  return kval isnt @val

  oper_gte = (obj) ->
   kval = cast @type, keyVal obj, @keys
@@ -239,6 +245,8 @@

     if comp is 'is'
      context.funcs.push oper_is.bind c
+    else if comp is 'isnt'
+     context.funcs.push oper_isnt.bind c
     else if comp is 'gte'
      context.funcs.push oper_gte.bind c
     else if comp is 'gt'


update tank models

update tank models

--- tank.coffee
+++ tank.coffee
@@ -1,146 +1,206 @@
-class Map
-  constructor: (@name) ->
-  draw: ->
-    grid: ->
-
-class Grid
-  cells: [][]
-  xcell_size: 13
-  ycell_size: 13
-
-class Cell
-  xpixers: 16 * 5
-  ypixers: 9 * 5
-
-class Pixer
-  terrain: ->
-  x_offset:
-
-class Terrain
-  constructor: (@type) ->
-  type: -> @type
-
-class Layer
-  objects: []
-  priority: 0
-  add_object: (o) -> @objects.push(o)
-  each_object: (fun) ->
-    fun.call(this, o) for o in @objects
-
-class FrontendLayer
-  constructor: ->
-    this.priority = 3
-
-class MainLayer
-  constructor: ->
-    this.priority = 2
-
-class BackgroundLayer
-  constructor: ->
-    this.priority = 1
-
-
-class Game
-  start: ->
-  over: ->
-  pause: ->
+class Rect
+  # x axis towards right
+  # y axis towards down
+  # x1, y1 stands for the top left point
+  # x2, y2 stands for the right bottom point
+  constructor: (@x1, @y1, @x2, @y2) ->
+  contains: (rect) ->
+    @x1 <= rect.x1 and @y1 <= rect.y1 and @x2 >= rect.x2 and @y2 >= rect.y2
+  intersection: (rect) ->
+    [max_x1, max_y1, min_x2, min_y2] = [@x1, @y1, @x2, @y2]
+    max_x1 = rect.x1 if rect.x1 > max_x1
+    max_y1 = rect.y1 if rect.y1 > max_y1
+    min_x2 = rect.x2 if rect.x2 < min_x2
+    min_y2 = rect.y2 if rect.y2 < min_y2
+    new Rect(max_x1, max_y1, min_x2, min_y2)
+  range_with: (rect) ->
+    [min_x1, min_y1, max_x2, max_y2] = [@x1, @y1, @x2, @y2]
+    min_x1 = rect.x1 if rect.x1 < min_x1
+    min_y1 = rect.y1 if rect.y1 < min_y1
+    max_x2 = rect.x2 if rect.x2 > max_x2
+    max_y2 = rect.y2 if rect.y2 > max_y2
+    new Rect(min_x1, min_y1, max_x2, max_y2)
+  toString: -> @x1 + ":" + @y1 + ":" + @x2 + ":" + @y2
+  width: -> @x2 - @x1
+  height: -> @y2 - @y1
+
+class World
+  max_x: 520
+  max_y: 520
+  tanks: []
+  terrains: []
+  all_units: -> @tanks.concat(@terrains)
+  add_tank: (tank) ->
+    @tanks.push(tank)
+  add_terrain: (terrain) ->
+    @terrains.push(terrain)

 class Observable
   observers: []
   add_observer: (o) -> @observers.push(o)
-  notify_changed: (from, args) ->
-    o.on_changed(from, args) for o in @observers
-
-class Observer
-  on_changed: (from, args) -> alert args
-
-class Tank extends Observable
-  constructor: (@env) ->
-  life: 0
+  notify_changed: (args) ->
+    o.on_changed(this, args) for o in @observers
+
+class WorldUnit extends Observable
+  constructor: (@world, positions) ->
+    @rect = new Rect(positions[0], positions[1], positions[2], positions[3])
+  id: -> @layer.toString + @rect.toString
+  layer: "main"
+
+class Graph
+  unit_rect_hash: {}
+  constructor: (@world) ->
+    @paper = Raphael("holder", @world.max_x, @world.max_y)
+  on_changed: (unit, args) ->
+    old_rect = @unit_rect_hash[unit.id]
+    new_rect = unit.rect
+    this.paint_all_layer(old_rect.range_with(new_rect))
+    @unit_rect_hash[unit.id] = new_rect
+  on_initialized: ->
+    this.paint_all_layer(new Rect(0, 0, @world.max_x, @world.max_y))
+  paint_all_layer: (rect) ->
+    rect_units = (unit for unit in @world.all_units() when rect.contains(unit.rect))
+    this.clear_rect(rect)
+    for layer in ["main", "foreground"]
+      this.paint_single_layer(unit, rect) for unit in rect_units when unit.layer == layer
+  clear_rect: (rect) ->
+    e = @paper.rect(rect.x1, rect.y1, rect.width(), rect.height())
+    e.attr({ fill: '#000' })
+  color_mapping: -> {
+    "brick" : "#f00",
+    "grass" : "#0f0",
+    "iron" : "#999",
+    "ice" : "#fff",
+    "water" : "#00f",
+    "user_primary" : "#0ff",
+    "user_secondary" : "#f5f",
+    "fool" : "#135",
+    "fish" : "#246",
+    "strong" : "#369"
+  }
+  paint_single_layer: (unit, rect) ->
+    console.log "paingting single"
+    need_paint_rect = rect.intersection(unit.rect)
+    if unit instanceof Terrain
+      e = @paper.rect(need_paint_rect.x1, need_paint_rect.y1, need_paint_rect.width(), need_paint_rect.height())
+      e.attr({ fill: this.color_mapping()[unit.type()] })
+    else
+      e = @paper.rect(need_paint_rect.x1, need_paint_rect.y1, need_paint_rect.width(), need_paint_rect.height())
+      e.attr({ fill: this.color_mapping()[unit.type()] })
+
+class Terrain extends WorldUnit
+  passable: (tank) -> true
+  destroyable: (missile) -> false
+  type: ->
+
+class BrickTerrain extends Terrain
+  passable: (tank) -> false
+  destroyable: (missile) -> missile.power >= 1
+  type: -> "brick"
+
+class IronTerrain extends Terrain
+  passable: (tank) -> false
+  destroyable: (missile) -> missile.power >= 2
+  type: -> "iron"
+
+class WaterTerrain extends Terrain
+  passable: (tank) -> tank.on_ship
+  type: -> "water"
+
+class IceTerrain extends Terrain
+  passable: (tank) -> true
+  type: -> "ice"
+
+class GrassTerrain extends Terrain
+  passable: (tank) -> true
+  destroyable: (missile) -> missile.power >= 3
+  type: -> "grass"
+  layer: "foreground"
+
+class Tank extends WorldUnit
+  constructor: (world, positions, @life) -> super world, positions
   decrease_life: (x) ->
     @life -= x
-    this.notify_changed(this, 'life')
+    this.notify_changed('life')
   increase_life: (x) ->
     @life += x
-    this.notify_changed(this, 'life')
+    this.notify_changed('life')
   die: ->
     @life = 0
-    this.notify_changed(this, 'life')
+    this.notify_changed('life')
   is_dead: ->
     @life <= 0
   power: 0
   decrease_power: (x) ->
     @power -= x
-    notify_changed(this, 'power')
+    this.notify_changed('power')
   increase_power: (x) ->
     @power += x
-    notify_changed(this, 'power')
+    this.notify_changed('power')
   speed: 0
   ship: false
   guard: false
-  gift: 0
   increase_gift: (x) ->
-  with_ship: ->
+  on_ship: ->
     @ship == true
-  with_guard: ->
+  on_guard: ->
     @guard == true
-  set_ship: (@ship) -> notify_changed(this, 'ship')
-  set_guard: (@guard) -> notify_changed(this, 'guard')
+  set_on_ship: (@ship) -> this.notify_changed('ship')
+  set_on_guard: (@guard) -> this.notify_changed('guard')
   direction: 'N'
-  turn: (@direction) -> notify_changed(this, 'direction')
+  turn: (@direction) -> this.notify_changed('direction')
   pos_x: 0
   pos_y: 0
   move: (offset_x, offset_y) ->
-    # consider @env.map
-    @pos_x += offset_x
-    @pos_x = 0 if @pos_x < 0
-    @pos_x = @env.max_x if @pos_x > @env.max_x
-    @pos_y += offset_y
-    @pos_y = 0 if @pos_y < 0
-    @pos_y = @env.max_y if @pos_y > @env.max_y
-    notify_changed(this, 'position')
-  x_target: (origin, offset) ->
-    tx = @pos_x + offset
-    tx = 0 if tx < 0
-    tx = @env.max_x if tx > @env.max_x
-  y_target: (origin, offset) ->
-    ty = @pos_y + offset
-    ty = 0 if ty < 0
-    ty = @env.max_y if ty > @envmax_y.
+    this.notify_changed('position')
   fire: ->
-    @env.fire
+  type: ->

 class UserTank extends Tank
-  constructor: (@game) ->
-    @life = 1
-    @power = 1
-    @speed = 2
-    super @game
+  constructor: (world, positions, @life, @primary) -> super world, positions
+  power: 1
+  speed: 2
+  type: -> 'user_' + (if @primary then 'primary' else 'secondary')

 class EnemyTank extends Tank
+  constructor: (world, positions, life, @gift) -> super world, positions, life
   cruise: ->
-    move_next
-    fire
-    # auto move
-
-
-class EnemySlowTank extends Tank
-  constructor: (@game) ->
-    @life = 1
-    @power = 1
-    @speed = 1
-
-
-
-
-
-
-class Missile
-
-class Gift
-
-
-paper = Raphael("holder", 1024, 520)
-
-circle = paper.circle(50, 40, 10)
-circle.attr("fill", "#f00")
+
+class FoolTank extends EnemyTank
+  power: 1
+  speed: 1
+  type: -> 'fool'
+
+class FishTank extends EnemyTank
+  power: 1
+  speed: 3
+  type: -> 'fish'
+
+class StrongTank extends EnemyTank
+  power: 2
+  speed: 1
+  type: -> 'strong'
+
+init = ->
+  console.log "init start"
+  world = new World
+  world.add_tank(new UserTank(world, [220, 10, 260, 50], 1, true))
+  world.add_tank(new UserTank(world, [220, 370, 260, 410], 1, false))
+  world.add_tank(new FoolTank(world, [220, 90, 260, 130], 1, 0))
+  world.add_tank(new FishTank(world, [220, 210, 260, 250], 2, 1))
+  world.add_tank(new StrongTank(world, [220, 290, 260, 330], 3, 2))
+
+  console.log tank.type() for tank in world.tanks
+
+  world.add_terrain(new BrickTerrain(world, [100, 100, 140, 500]))
+  world.add_terrain(new GrassTerrain(world, [180, 100, 220, 500]))
+  world.add_terrain(new IronTerrain(world, [260, 100, 300, 500]))
+  world.add_terrain(new WaterTerrain(world, [340, 100, 380, 500]))
+  world.add_terrain(new IceTerrain(world, [420, 100, 460, 500]))
+
+  graph = new Graph(world)
+  graph.on_initialized()
+
+  console.log "init done"
+
+$(document).ready init


clean up key maps

clean up key maps

--- editor.coffee
+++ editor.coffee
@@ -75,49 +75,23 @@
         from: CodeMirror.Pos cur.line, token.start
         to: CodeMirror.Pos cur.line, cur.ch

-CodeMirror.commands.run = (cm) ->
-  cm.lead_cell.run()
-  add_context()
-
-CodeMirror.commands.contextHelp = (cm) ->
-  cur = cm.getCursor()
-  token = cm.getTokenAt(cur)
-  if lead.graphite.has_docs token.string
-    run_in_info_context cm.lead_cell, "docs '#{token.string}'"
-  else if lead.ops[token.string]?
-    run_in_info_context cm.lead_cell, "help #{token.string}"
-
-CodeMirror.commands.suggest = (cm) ->
-  CodeMirror.showHint cm, suggest, async: true
-
-CodeMirror.keyMap.lead =
-  Tab: (cm) ->
-    if cm.somethingSelected()
-      cm.indentSelection 'add'
-    else
-      spaces = Array(cm.getOption("indentUnit") + 1).join(" ")
-      cm.replaceSelection(spaces, "end", "+input")
-  Up: (cm) ->
+lead_editor_commands =
+  run: (cm) ->
+    cm.lead_cell.run()
+    add_context()
+
+  contextHelp: (cm) ->
     cur = cm.getCursor()
-    if cur.line is 0
-      previous_context = input_cell_at_offset cm.lead_cell, -1
-      if previous_context?
-        previous_context.editor.focus()
-      else
-        CodeMirror.Pass
-    else
-      CodeMirror.Pass
-  Down: (cm) ->
-    cur = cm.getCursor()
-    if cur.line is cm.lineCount() - 1
-      next_context = input_cell_at_offset cm.lead_cell, 1
-      if next_context?
-        next_context.editor.focus()
-      else
-        CodeMirror.Pass
-    else
-      CodeMirror.Pass
-  'Shift-Up': (cm) ->
+    token = cm.getTokenAt(cur)
+    if lead.graphite.has_docs token.string
+      run_in_info_context cm.lead_cell, "docs '#{token.string}'"
+    else if lead.ops[token.string]?
+      run_in_info_context cm.lead_cell, "help #{token.string}"
+
+  suggest: (cm) ->
+    CodeMirror.showHint cm, suggest, async: true
+
+  fill_with_last_value: (cm) ->
     previous_context = input_cell_at_offset cm.lead_cell, -1
     if previous_context?
       cm.setValue previous_context.editor.getValue()
@@ -125,7 +99,53 @@
     else
       CodeMirror.Pass

+  next_cell: (cm) ->
+    cell = input_cell_at_offset cm.lead_cell, 1
+    if cell?
+      cell.editor.focus()
+    else
+      CodeMirror.Pass
+
+  previous_cell: (cm) ->
+    cell = input_cell_at_offset cm.lead_cell, -1
+    if cell?
+      cell.editor.focus()
+    else
+      CodeMirror.Pass
+
+  maybe_next_cell: (cm) ->
+    cur = cm.getCursor()
+    if cur.line is cm.lineCount() - 1
+      lead_editor_commands.next_cell cm
+    else
+      CodeMirror.Pass
+
+  maybe_previous_cell: (cm) ->
+    cur = cm.getCursor()
+    if cur.line is 0
+      lead_editor_commands.previous_cell cm
+    else
+      CodeMirror.Pass
+
+lead_key_map =
+  Tab: (cm) ->
+    if cm.somethingSelected()
+      cm.indentSelection 'add'
+    else
+      spaces = Array(cm.getOption("indentUnit") + 1).join(" ")
+      cm.replaceSelection(spaces, "end", "+input")
+  Up: 'maybe_previous_cell'
+  Down: 'maybe_next_cell'
+  'Shift-Up': 'fill_with_last_value'
+  'Shift-Enter': 'run'
+  'F1': 'contextHelp'
+  'Ctrl-Space': 'suggest'
+
   fallthrough: ['default']
+
+init_codemirror = ->
+  CodeMirror.keyMap.lead = lead_key_map
+  $.extend CodeMirror.commands, lead_editor_commands

 contexts = []

@@ -204,10 +224,6 @@
     tabSize: 2
     viewportMargin: Infinity
     gutters: ['error']
-    extraKeys:
-      'Shift-Enter': 'run'
-      'F1': 'contextHelp'
-      'Ctrl-Space': 'suggest'

   context =
     used: false
@@ -436,6 +452,7 @@
     reader.readAsText file

 window.init_editor = ->
+  init_codemirror()
   $document = $ '#document'
   $file_picker = $ '#file'
   $file_picker.on 'change', (e) ->


also rename loading message in sidebar

also rename loading message in sidebar

--- tutor/src/components/course-calendar/past-assignments.cjsx
+++ tutor/src/components/course-calendar/past-assignments.cjsx
@@ -16,7 +16,7 @@
 PastAssignmentsLoading = ({className}) ->

-      Loading past assignments...
+      Loading copied assignments...


fallback to small image if b64 thumbnail isn't defined

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -137,7 +137,7 @@
   else
     current_user = fetch('/current_user')
     if current_user.user == user.key
-      thumbnail = current_user.b64_thumbnail
+      thumbnail = current_user.b64_thumbnail or avatarUrl(user,'small')
       if thumbnail? && img_size == 'small'
         props.src = thumbnail
     else


fix out str

fix out str

--- index.coffee
+++ index.coffee
@@ -15,7 +15,7 @@
     if data.text
       out = validate data.text
       console.log "Speak:", out
-      spawn 'bash', ['./speak.sh', data.text]
+      spawn 'bash', ['./speak.sh', out]

 validate = (text) ->
   text


debugging

debugging

--- index.coffee
+++ index.coffee
@@ -73,11 +73,13 @@
  		app_secret: configs.clef.app_secret
  		logout_token: req.body.logout_token

+ 	console.log "1"
  	request.post
  		url: 'https://clef.io/api/v1/logout'
  		form: form
  	, (err, resp, body) ->
  		clefResponse = JSON.parse body
+ 		console.log clefResponse
  		if err or not clefResponse.access_token?
  			console.log "Error getting CLEF access token", err, clefResponse
  			v0.responses.notAuth res
@@ -87,10 +89,13 @@
 	 		request.get "https://clef.io/api/v1/info?access_token=#{req.$session.clefAccessToken}", (err, resp, body) ->
 	 			userInfo = JSON.parse body

+	 			console.log userInfo
+
 	 			if err or !userInfo.success? or !userInfo.success
 	 				console.log "Error getting clef user info", err
 	 				v0.responses.notAuth res
 	 			else
+	 				console.log session.sessions
 	 				for sess in session.sessions
 	 					if sess.user.identifier == userInfo.info.id
 	 						sess.user = false
@@ -119,8 +124,6 @@
  		app_id: configs.clef.app_id
  		app_secret: configs.clef.app_secret
  		code: code
-
- 	console.log form

  	request.post
  		url: 'https://clef.io/api/v1/authorize'


add option to merge same splits

add option to merge same splits

--- index.coffee
+++ index.coffee
@@ -5,12 +5,12 @@
 module.exports =

     termViews: []
-    # splits: {}
-    # splitCounts: {up: 0, down: 0, left: 0, right: 0}
+    splits: {}

     configDefaults:
       autoRunCommand: null
       shellArguments: '--init-file ~/.bash_profile'
+      openPanesInSameSplit: no

     activate: (@state)->

@@ -31,15 +31,19 @@
       termView

     splitTerm: (direction)->
+      openPanesInSameSplit = atom.config.get 'term2.openPanesInSameSplit'
       termView = @createTermView()
       direction = capitalize direction
-      # if @splits[direction] and @splits[direction].items.length > 0
-      #   pane = @splits[direction]
-      #   item = pane.addItem termView
-      #   pane.activateItem item
-      # else
-      #   @splits[direction] = atom.workspace.getActivePane()["split#{direction}"] items: [termView]
-      atom.workspace.getActivePane()["split#{direction}"] items: [termView]
+
+      if openPanesInSameSplit
+        if @splits[direction] and @splits[direction].items.length > 0
+          pane = @splits[direction]
+          item = pane.addItem termView
+          pane.activateItem item
+        else
+          @splits[direction] = atom.workspace.getActivePane()["split#{direction}"] items: [termView]
+      else
+        atom.workspace.getActivePane()["split#{direction}"] items: [termView]

     newTerm: ->
       termView = @createTermView()


add regexp

add regexp

--- index.coffee
+++ index.coffee
@@ -15,9 +15,10 @@
     if data.text
       out = validate data.text
       console.log "Speak:", out
-      spawn 'bash', ['./speak.sh', out]
+      #spawn 'bash', ['./speak.sh', out]

 validate = (text) ->
   text
-    .replace /https?:\/\/.+\s/gi, ""
-    .replace /RT:\s@\d+\s/gi, ""
+    .replace /https?:\/\/.+(\s|$)/gi, ""
+    .replace /RT\s@\d+:\s/gi, ""
+    .replace /#\d+(\s|$)/gi, ""


could try that

could try that

--- index.coffee
+++ index.coffee
@@ -16,6 +16,7 @@

 # Helper to cache an endpoint
 fetchAndCache = (key, url, callback) ->
+  console.log 'FETCH', key
   request
     .get(ARTSY_URL + url)
     .set('x-xapp-token': xappToken)
@@ -27,12 +28,12 @@
         body: sres.body
       db.cache.update { _id: key }, doc, { upsert: true }
       callback? null, doc
-debouncedFetchAndCache = _.throttle fetchAndCache, parseInt THROTTLE_TIME

 # CORS support
 app.use cors()

 # Cache configured routes
+debounced = {}
 paths = PATHS.split ','
 for path in paths
   app.get path, (req, res, next) ->
@@ -41,7 +42,11 @@
       return next err if err
       if cached
         res.set(cached.headers).send cached.body
-        debouncedFetchAndCache key, req.url
+        debounced[key] ?= _.debounce(
+          (-> fetchAndCache key, req.url)
+          parseInt(THROTTLE_TIME)
+        )
+        debounced[key]()
       else
         fetchAndCache key, req.url, (err, doc) ->
           return next err if err


setup app after artsyXapp is initialized

--- index.coffee
+++ index.coffee
@@ -20,10 +20,9 @@
 # Attempt to connect to Redis. If it fails, no worries, the app will move on
 # without caching.
 cache.setup ->
-  setup app
-
   # Get an xapp token
   artsyXapp.init { url: API_URL, id: ARTSY_ID, secret: ARTSY_SECRET }, ->
+    setup app

     # Start the server and send a message to IPC for the integration test helper
     # to hook into.


updating proxy url

updating proxy url

--- index.coffee
+++ index.coffee
@@ -51,7 +51,7 @@

 reddish_port = 8000
 reddish_monitor_port = 8001
-reddish_hostname = if process.env.NODE_ENV is 'production' then 'reddish.freeflow.io' else 'dev.freeflow.io'
+reddish_hostname = if process.env.NODE_ENV is 'production' then 'reddi.sh' else 'dev.freeflow.io'

 # setup handshaken and connected state


logging errors

logging errors

--- rest_controller.coffee
+++ rest_controller.coffee
@@ -4,10 +4,6 @@
 bbCallback = ORMUtils.bbCallback
 JSONUtils = require 'backbone-orm/lib/json_utils'
 JoinTableControllerSingleton = require './lib/join_table_controller_singleton'
-
-sendError = (res, err) ->
-  res.setHeader('Content-Type', 'text/plain')
-  return res.status(500).send(err.toString())

 module.exports = class RESTController

@@ -16,6 +12,7 @@
     @[key] = value for key, value of options
     @white_lists or= {}
     @templates or= {}
+    @logger or= console

     app.get "#{@route}/:id", @_call(@show)
     app.get @route, @_call(@index)
@@ -31,12 +28,16 @@

     JoinTableControllerSingleton.generateByOptions(app, options)

+  sendError: (res, err) ->
+    @logger.error("Error 500 from #{res.req.method} #{res.req.url}: #{err}")
+    res.header('content-type', 'text/plain').status(500).send(err.toString())
+
   index: (req, res) =>
     try
       cursor = @model_type.cursor(JSONUtils.parse(req.query))
       cursor = cursor.whiteList(@white_lists.index) if @white_lists.index
       cursor.toJSON (err, json) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         return res.json({result: json}) if cursor.hasCursorQuery('$count') or cursor.hasCursorQuery('$exists')
         unless json
           if cursor.hasCursorQuery('$one')
@@ -46,101 +47,101 @@

         if cursor.hasCursorQuery('$page')
           @render req, json.rows, (err, rendered_json) =>
-            return sendError(res, err) if err
+            return @sendError(res, err) if err
             json.rows = rendered_json
             res.json(json)
         else
           @render req, json, (err, rendered_json) =>
-            return sendError(res, err) if err
+            return @sendError(res, err) if err
             res.json(rendered_json)

     catch err
-      sendError(res, err)
+      @sendError(res, err)

   show: (req, res) =>
     try
       cursor = @model_type.cursor(req.params.id)
       cursor = cursor.whiteList(@white_lists.show) if @white_lists.show
       cursor.toJSON (err, json) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         return res.status(404).send() unless json
         json = _.pick(json, @white_lists.show) if @white_lists.show

         @render req, json, (err, json) =>
-          return sendError(res, err) if err
+          return @sendError(res, err) if err
           res.json(json)

     catch err
-      sendError(res, err)
+      @sendError(res, err)

   create: (req, res) =>
     try
       json = JSONUtils.parse(if @white_lists.create then _.pick(req.body, @white_lists.create) else req.body)
       model = new @model_type(@model_type::parse(json))
       model.save {}, bbCallback (err) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err

         json = if @white_lists.create then _.pick(model.toJSON(), @white_lists.create) else model.toJSON()
         @render req, json, (err, json) =>
-          return sendError(res, err) if err
+          return @sendError(res, err) if err
           res.json(json)

     catch err
-      sendError(res, err)
+      @sendError(res, err)

   update: (req, res) =>
     try
       json = JSONUtils.parse(if @white_lists.update then _.pick(req.body, @white_lists.update) else req.body)
       @model_type.find req.params.id, (err, model) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         return res.status(404).send() unless model
         model.save model.parse(json), bbCallback (err) =>
-          return sendError(res, err) if err
+          return @sendError(res, err) if err

           json = if @white_lists.update then _.pick(model.toJSON(), @white_lists.update) else model.toJSON()
           @render req, json, (err, json) =>
-            return sendError(res, err) if err
+            return @sendError(res, err) if err
             res.json(json)

     catch err
-      sendError(res, err)
+      @sendError(res, err)

   destroy: (req, res) =>
     try
       @model_type.exists req.params.id, (err, exists) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         return res.status(404).send() unless exists

         @model_type.destroy {id: req.params.id}, (err) ->
-          return sendError(res, err) if err
+          return @sendError(res, err) if err
           res.status(200).send()

     catch err
-      sendError(res, err)
+      @sendError(res, err)

   destroyByQuery: (req, res) =>
     try
       @model_type.destroy JSONUtils.parse(req.query), (err) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         res.send(200)
     catch err
-      sendError(res, err)
+      @sendError(res, err)

   head: (req, res) =>
     try
       @model_type.exists req.params.id, (err, exists) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         res.send(if exists then 200 else 404)
     catch err
-      sendError(res, err)
+      @sendError(res, err)

   headByQuery: (req, res) =>
     try
       @model_type.exists JSONUtils.parse(req.query), (err, exists) =>
-        return sendError(res, err) if err
+        return @sendError(res, err) if err
         res.send(if exists then 200 else 404)
     catch err
-      sendError(res, err)
+      @sendError(res, err)

   preRender: (req, json, callback) -> callback()


Correct path to spawner.js

Correct path to spawner.js

Resolve issue xolvio/Letterpress#2

--- lib/LongRunningChildProcess.coffee
+++ lib/LongRunningChildProcess.coffee
@@ -92,7 +92,7 @@

   _getSpawnScriptPath: ->
     path.join(@_getMeteorLocalPath(),
-      'build/programs/server/assets/packages/xolvio_webdriver/libs/spawner.js'
+      'build/programs/server/assets/packages/xolvio_webdriver/lib/spawner.js'
     )

@@ -176,4 +176,4 @@
         @pid = null
         fs.removeSync(@_getPidFilePath())
       catch err
-        log.warn "Error: While killing process:\n", err+        log.warn "Error: While killing process:\n", err


[master] no fall through

[master] no fall through

--- scraper/guba.coffee
+++ scraper/guba.coffee
@@ -19,9 +19,12 @@
 requestAsync = (url) ->
   deferred = Q.defer()
   request url, (err, response, body) ->
-    deferred.reject err if err
-    deferred.reject new Error("#{url} returned status code #{response.statusCode}") if response.statusCode != 200
-    deferred.resolve body
+    if err
+      deferred.reject err
+    else if response.statusCode != 200
+      deferred.reject new Error("#{url} returned status code #{response.statusCode}")
+    else
+      deferred.resolve body
   deferred.promise

 makeUrl = (symbol, page) ->


Add animation system

Add animation system

--- entity.coffee
+++ entity.coffee
@@ -33,6 +33,8 @@
   setInterval tickAll, 1000 / 60

 class BaseEntity
+  setTexture: ->
+  setAnimation: ->
   getBody: -> null
   doJump: ->
   doMoveLeft: ->
@@ -68,6 +70,8 @@
   hasCargo: -> @next.hasCargo()
   getFacing: -> @next.getFacing()
   doHitWall: -> @next.doHitWall()
+  setAnimation: (anim) -> @next.setAnimation(anim)
+  setTexture: (tex) -> @next.setTexture(tex)

   seppuku: -> World.del(this)

@@ -264,6 +268,9 @@
 class SpriteAdapter extends EntityAdapter
   constructor: (@stage, @sprite, @next) ->

+  setTexture: (tex) ->
+    @sprite.setTexture tex
+
   doTick: ->
     pos = @getPosition()
     @sprite.position = new PIXI.Point(pos.x * PIXELS_PER_METER,
@@ -278,6 +285,21 @@
   doDetach: ->
     @stage.removeChild @sprite
     @next.doDetach()
+
+class AnimationAdapter extends EntityAdapter
+  constructor: (@period, @animations, @next) ->
+    @currentAnimation = null
+    @phase = Math.random() * 60*60*24*365
+
+  setAnimation: (anim) ->
+    @currentAnimation = anim
+
+  doTick: ->
+    if @currentAnimation?
+      frames = @animations[@currentAnimation]
+      frame = Math.floor((@phase + unixTime()) / @period) % frames.length
+      @setTexture frames[frame]
+    @next.doTick()

 class DebugLateralMovementAdapter extends EntityAdapter
   doMoveLeft: (en) ->
@@ -310,4 +332,5 @@
 @DebugLateralMovementAdapter = DebugLateralMovementAdapter
 @FixedLocationAdapter = FixedLocationAdapter
 @LimitedLifespanAdapter = LimitedLifespanAdapter
-
+@AnimationAdapter = AnimationAdapter
+


Pass logger to the urlhandler

Pass logger to the urlhandler

--- src/parser.coffee
+++ src/parser.coffee
@@ -31,7 +31,7 @@
         parentURLs.push url

         start = new Date().getTime()
-        URLHandler.get url, headers, timeout, (err, xml) =>
+        URLHandler.get url, headers, timeout, logger, (err, xml) =>
             return cb(err) if err?
             took = new Date().getTime() - start
             logger.log("Vast download took " + took/1000 + " seconds, URL [" + url + "]")


an attempt at fixing tp #1050 by moving the event attacher out of the render function into the initializer / constructor

--- jsapp/xlform_model_view/mv.skipLogicHelpers.coffee
+++ jsapp/xlform_model_view/mv.skipLogicHelpers.coffee
@@ -72,19 +72,27 @@
     constructor: (@model, @view, @builder) ->
       @view.presenter = @
       @question = @model._get_question()
-    render: (destination) ->
+      if @builder.survey
+        @builder.survey.on 'choice-list-update', (row, key) =>
+          if @destination
+            @render(@destination)
+
+        @builder.survey.on 'row-detail-change', (row, key) =>
+          if @destination
+            if key == 'label'
+              @render(@destination)
+      else
+        console.error "this.builder.survey is not yet available"
+
+
+    render: (@destination) ->
       @view.render()
       @view.question_picker_view.fill_value(@model.get('question_cid'))
       @view.operator_picker_view.fill_value(@model.get('operator').get_value())
       @view.response_value_view.fill_value(@model.get('response_value')?.get('value'))
       @view.attach_to(destination)

-      @builder.survey.on 'row-detail-change', (row, key) =>
-        if key == 'label'
-          @render(destination)
-
-      @builder.survey.on 'choice-list-update', (row, key) =>
-        @render(destination)
+

       @determine_add_new_criterion_visibility()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -87,7 +87,6 @@
     hflip: false
     vflip: false
     scale: 1
-    spriteName: null
     zIndex: 0

   setSizeCallback = (sprite) ->
@@ -99,8 +98,6 @@
       I.sprite = Sprite.fromURL(I.sprite, setSizeCallback)
     else
       I.sprite = Sprite.loadByName(I.sprite, setSizeCallback)
-  else if I.spriteName
-    I.sprite = Sprite.loadByName(I.spriteName, setSizeCallback)

   self.bind 'draw', (canvas) ->
     if I.alpha? and I.alpha != 1


Source docs added to option attribute definition

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -138,7 +138,11 @@
     previewsContainer: null

     # If null, no capture type will be specified
-    # If camera, .
+    # If camera, mobile devices will skip the file selection and choose camera
+    # If microphone, mobile devices will skip the file selection and choose the microphone
+    # If camcorder, mobile devices will skip the file selection and choose the camera in video mode
+    # On apple devices multiple must be set to false.  AcceptedFiles may need to
+    # be set to an appropriate mime type (e.g. "image/*", "audio/*", or "video/*").
     capture: null

     # Dictionary


Corrected the default 'File too big' message.

#364

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -147,7 +147,7 @@
     dictFallbackText: "Please use the fallback form below to upload your files like in the olden days."

     # If the filesize is too big.
-    dictFileTooBig: "File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB."
+    dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB."

     # If the file doesn't match the file type.
     dictInvalidFileType: "You can't upload files of this type."


No-Data mask

No-Data mask

--- muleview/app/view/MuleChart.coffee
+++ muleview/app/view/MuleChart.coffee
@@ -28,6 +28,20 @@
     @fireEvent "topkeyclick"

   renderChart: () ->
+    if not @hasData
+      @add Ext.create "Ext.container.Container",
+        layout:
+          type: "vbox"
+          pack: "center"
+          align: "center"
+
+        items: [
+          xtype: "label"
+          text: "No Data"
+        ]
+      @setDisabled(true)
+
+      return

     # Remove old components and create container:
     @removeAll()
@@ -188,6 +202,7 @@
       total = record.get(@topKeys[0])
       for key in keys
         value = record.get(key)
+        @hasData ||= value
         percent = 100 * value / total
         ans[key].push
           x: record.get("timestamp")
@@ -202,6 +217,7 @@
     keys = Ext.Array.pluck(@alerts || [], "name")
     keys = keys.concat(@topKeys || [])
     keys = keys.concat(@subKeys || [])
+
     seriesData = @prepareSeriesData(keys)

     series = []
@@ -232,7 +248,6 @@
         type: "subkey"
         renderer: "stack"

-
     series

   keyLegendName: (key) ->


Add test for code parameters.

Add test for code parameters.

--- test.coffee
+++ test.coffee
@@ -148,5 +148,35 @@
     [2, /\[/]
     [2, /{/]

+test 'code parameters'
+    config:
+        displayCodeParameters: true
+    code: """
+        f = (a, b, c) ->
+        g = (a='default', b=a) ->
+        h = ([a, b], {c:d, e}) ->
+        i = ([[a, [b]]], {c:[d], e:{f}}) ->"""
+    tree: """
+            <root>
+             ├─ f(a, b, c) [0..0]
+             ├─ g(a, b) [1..1]
+             ├─ h([a, b], {c, e}) [2..2]
+             └─ i([[a, [b]]], {c, e}) [3..3]"""
+
+test 'illegal code parameters'
+    config:
+        displayCodeParameters: true
+    code: """
+        f = (eval, arguments=-1) ->"""
+    tree: """
+            <root>
+             └─ f(eval, arguments) [0..0]"""
+    [0, /eval/]
+    [0, /arguments/]
+    # NOTE
+    # Illegal parameter names in destructuring assignments are processed at
+    # compile time and will not produce errors while parsing. Hence no test as
+    #       g = ([eval], {arguments}) ->
+
 if any_test_failed
     process.stdout.on 'drain', -> process.exit(1)


Fix flaky test re: adding new file

Fix flaky test re: adding new file

Wait for the tree view selection to *fully* update before checking the
selected item in the tree view.

As far as I can tell, when a file is added, the tree view is updated in
a few steps:

1. Add the new file to the directory
2. Select the new file

Prior to this change, we were waiting for Step 1 to happen, and then
asserting that Step 2 had also already happened. Between Step 1 and Step
2, it appears that the *directory* is selected, and that was causing the
failure seen in
https://github.com/atom/tree-view/issues/1155#issue-247146435.

--- spec/tree-view-package-spec.coffee
+++ spec/tree-view-package-spec.coffee
@@ -1809,9 +1809,11 @@
                 expect(atom.workspace.getModalPanels().length).toBe 0
                 expect(atom.workspace.getCenter().getActivePaneItem().getPath()).toBe newPath

-              waitsFor "tree view to be updated", ->
+              waitsFor "file to be added to tree view", ->
                 dirView.entries.querySelectorAll(".file").length > 1

+              waitsFor "tree view selection to be updated", ->
+                treeView.element.querySelector('.file.selected')
               runs ->
                 expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath)
                 expect(callback).toHaveBeenCalledWith({path: newPath})


PrivateMessage: CycleChannel is required for successful removeParticipants function call

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -166,34 +166,35 @@

 removeParticipants = (data, callback)->
   url = "/channel/#{data.channelId}/participants/remove"
-  doChannelParticipantOperation data, url, (err, response) ->
-    return callback err if err
-    cycleChannelHelper data, response
-    callback null, response
-
-cycleChannelHelper = (data, removeResult)->
-  return unless removeResult?.length
-
-  isUserRemoved = (removeResult) ->
+  doChannelParticipantOperation data, url, (err, response)->
+    return callback err if err
+    cycleChannelHelper data, response, callback
+
+# TODO we can move this to realtime worker later on
+cycleChannelHelper = (data, removeResult, callback)->
+  return callback { message: "user(s) could not leave the channel" } unless removeResult?.length
+
+  isUserRemoved = (removeResult)->
     for accountResult in removeResult
       return yes  if accountResult.statusConstant is 'left'

     return no

-  return unless isUserRemoved removeResult
+  return callback { message: "user(s) could not leave the channel" } unless isUserRemoved removeResult

   {channelId, accountId} = data
-  channelById {id: channelId, accountId}, (err, channel) ->
-    return console.error err if err
-    return console.error 'Channel not found' unless channel?.channel
+  channelById {id: channelId, accountId}, (err, channel)->
+    return callback err if err
+    return callback { message: 'Channel not found' } unless channel?.channel
     {channel} = channel
     options =
       groupSlug     : channel.groupName
       apiChannelType: channel.typeConstant
       apiChannelName: channel.name
     SocialChannel = require './channel'
-    SocialChannel.cycleChannel options, (err) ->
-      return console.error err if err
+    SocialChannel.cycleChannel options, (err)->
+      return callback err if err
+      callback null, removeResult

 doChannelParticipantOperation = (data, url, callback)->


We need user's _id for currently logged in user.

But to make sure objectify of sub-documents work we add _id to catalog
fields as well.

--- server/person.coffee
+++ server/person.coffee
@@ -41,6 +41,7 @@
   # A subset of public fields used for automatic publishing
   @PUBLISH_AUTO_FIELDS: ->
     fields: _.pick @PUBLISH_FIELDS().fields, [
+      'user._id'
       'user.username'
       'slug'
       'displayName'
@@ -52,6 +53,7 @@
   # A subset of public fields used for catalog results
   @PUBLISH_CATALOG_FIELDS: ->
     fields: _.pick @PUBLISH_FIELDS().fields, [
+      'user._id'
       'user.username'
       'slug'
       'displayName'


fix tests that require on globals

fix tests that require on globals

--- server/server.coffee
+++ server/server.coffee
@@ -14,6 +14,10 @@
   if (config && config.paths)
     global.rendr.entryPath = config.paths.entryPath
     global.rendr.manifestDir = config.paths.publicDir
+  else
+    # if we don't have entry path, guess
+    global.rendr.entryPath = process.env.PWD + '/app'
+    global.rendr.manifestDir = global.rendr.entryPath + '/../public'

 # ===== CONFIG =====