use console.log not logger.log

use console.log not logger.log

we don't habe verbiouse lossing enabled that early in the apps lifecycle
also best not to require modules before enabling profiler

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -27,20 +27,19 @@
 		appname = _name
 		collectDefaultMetrics({ timeout: 5000, prefix: Metrics.buildPromKey()})

-		logger = require("logger-sharelatex")

-		logger.log("ENABLE_TRACE_AGENT set to #{process.env['ENABLE_TRACE_AGENT']}")
+		console.log("ENABLE_TRACE_AGENT set to #{process.env['ENABLE_TRACE_AGENT']}")
 		if process.env['ENABLE_TRACE_AGENT'] == "true"
-			logger.log("starting google trace agent")
+			console.log("starting google trace agent")
 			traceAgent = require('@google-cloud/trace-agent')

 			traceOpts =
 				ignoreUrls: [/^\/status/, /^\/health_check/]
 			traceAgent.start(traceOpts)

-		logger.log("ENABLE_DEBUG_AGENT set to #{process.env['ENABLE_DEBUG_AGENT']}")
+		console.log("ENABLE_DEBUG_AGENT set to #{process.env['ENABLE_DEBUG_AGENT']}")
 		if process.env['ENABLE_DEBUG_AGENT'] == "true"
-			logger.log("starting google debug agent")
+			console.log("starting google debug agent")
 			debugAgent = require('@google-cloud/debug-agent')
 			debugAgent.start({
 				allowExpressions: true,
@@ -50,9 +49,9 @@
 				}
 			})

-		logger.log("ENABLE_PROFILE_AGENT set to #{process.env['ENABLE_PROFILE_AGENT']}")
+		console.log("ENABLE_PROFILE_AGENT set to #{process.env['ENABLE_PROFILE_AGENT']}")
 		if process.env['ENABLE_PROFILE_AGENT'] == "true"
-			logger.log("starting google profile agent")
+			console.log("starting google profile agent")
 			profiler = require('@google-cloud/profiler')
 			profiler.start({
 				serviceContext: {


Minor change

Minor change

--- library/membrane/describe.coffee
+++ library/membrane/describe.coffee
@@ -61,8 +61,8 @@
     @DELETE = -> @describe @prototype.DELETE, arguments...
     @PATCH = -> @describe @prototype.PATCH, arguments...
     @POST = -> @describe @prototype.POST, arguments...
+    @PUT = -> @describe @prototype.PUT, arguments...
     @GET = -> @describe @prototype.GET, arguments...
-    @PUT = -> @describe @prototype.PUT, arguments...

     # Traverse all of the services that are registered with the router
     # and collect the documentation for each method, then given this
@@ -76,7 +76,7 @@
         conformant = (s) -> s.objectOf Descriptor
         services = _.filter services, conformant
         _.map services, (service) => do (service) =>
-            unsupported = service.unsupported
+            assert unsupported = service.unsupported
             supported = service.constructor.SUPPORTED
             implemented = (m) -> service[m] isnt unsupported
             fix = (m) => @constructor.describe service[m], ->


Revert some of the import changes

Revert some of the import changes

--- library/membrane/skeleton.coffee
+++ library/membrane/skeleton.coffee
@@ -31,8 +31,11 @@
 colors = require "colors"
 logger = require "winston"

+tools = require "./../nucleus/tools"
+extendz = require "./../nucleus/extends"
+compose = require "./../nucleus/compose"
+{WithHooks} = require "./../nucleus/stubs"
 {Descriptor} = require "./describe"
-{WithHooks} = require "./../nucleus/stubs"

 # This is an abstract base class for every service in the system
 # and in the end user application that provides a REST interface


Slight changes in the skeleton

Slight changes in the skeleton

--- library/membrane/skeleton.coffee
+++ library/membrane/skeleton.coffee
@@ -65,10 +65,10 @@
         knowns = @constructor.SUPPORTED
         doesJson = response.accepts /json/
         pathname = try url.parse(request.url).pathname
-        checkIfSupported = (method) => @[method] isnt @unsupported
+        checkIfSupported = (m) => @[m] isnt @unsupported
         supported = _.filter knowns, checkIfSupported
         descriptor = methods: supported, resource: pathname
-        return response.send descriptor if doesJson
+        return @push response, descriptor if doesJson
         formatted = supported.join(", ") + "\r\n"
         response.send formatted; this


check in the messages impl

check in the messages impl

--- library/semantic/formular.coffee
+++ library/semantic/formular.coffee
@@ -60,6 +60,27 @@
         @container.appendTo(hosting); scoped @container
         @hosting = hosting; @reference = reference; @

+    # This method is intended for rendering error messages attached
+    # to the fields. It traverses all of the fields and see if any
+    # of the fields have warning metadata attached to it. If so, a
+    # warning is added to the list of messages and the field marked
+    # with an error tag, which makes its validity visually distinct.
+    messages: (heading) ->
+        assert @container.removeClass "warning error"
+        @warnings.empty(); list = $ "<ul>", class: "list"
+        h = $("<div>", class: "header").appendTo @warnings
+        h.text heading.toString(); list.appendTo @warnings
+        assert fields = @container.find(".field") or []
+        sieve = (seq) -> _.filter seq, (value) -> value
+        sieve _.map fields, (value, index, iteratee) =>
+            assert _.isObject value = $(value) or null
+            warning = value.data("warning") or undefined
+            value.removeClass "error" if value.is ".error"
+            return if not warning? or _.isEmpty warning
+            value.addClass "error"; notice = $ "<li>"
+            notice.appendTo(list).text "#{warning}"
+            @container.addClass "warning"; value
+
     # This is a part of the formular protocol. This method allows
     # you to upload all the fields from a vector of objects, each
     # of whom describes each field in the formular; its value and


Node modules koding: export NodejsProfiler

--- node_modules_koding/koding-datadog/lib/index.coffee
+++ node_modules_koding/koding-datadog/lib/index.coffee
@@ -1,8 +1,10 @@
-DogStatsD   = require './dogstatsd'
-MetricsBase = require './metricsbase'
+DogStatsD      = require './dogstatsd'
+MetricsBase    = require './metricsbase'
+NodejsProfiler = require './nodejsprofiler'

 module.exports = {
   DogStatsD
   MetricsBase
+  NodejsProfiler
 }


Handled single value requests

Handled single value requests

--- mocks/cursor.coffee
+++ mocks/cursor.coffee
@@ -34,7 +34,11 @@
     # TODO: OPTIMIZE TO REMOVE 'id' and '_rev' if needed
     if @_cursor.$values
       $values = if @_cursor.$white_list then _.intersection(@_cursor.$values, @_cursor.$white_list) else @_cursor.$values
-      json = (((item[key] for key in $values when item.hasOwnProperty(key))) for item in json)
+      if @_cursor.$values.length is 1
+        key = @_cursor.$values[0]
+        json = if $values.length then ((if item.hasOwnProperty(key) then item[key] else null) for item in json) else _.map(json, -> null)
+      else
+        json = (((item[key] for key in $values when item.hasOwnProperty(key))) for item in json)
     else if @_cursor.$select
       $select = if @_cursor.$white_list then _.intersection(@_cursor.$select, @_cursor.$white_list) else @_cursor.$select
       json = _.map(json, (item) => _.pick(item, $select))


Rename atom shell tasks

Rename atom shell tasks

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -23,7 +23,7 @@
   grunt.loadNpmTasks('grunt-contrib-coffee')
   grunt.loadNpmTasks('grunt-contrib-less')
   grunt.loadNpmTasks('grunt-shell')
-  grunt.loadNpmTasks('grunt-download-atom-shell')
+  grunt.loadNpmTasks('grunt-download-electron')
   grunt.loadNpmTasks('grunt-electron-installer')
   grunt.loadNpmTasks('grunt-peg')
   grunt.loadTasks('tasks')
@@ -254,7 +254,7 @@
   grunt.registerTask('lint', ['standard', 'coffeelint', 'csslint', 'lesslint'])
   grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])

-  ciTasks = ['output-disk-space', 'download-atom-shell', 'download-atom-shell-chromedriver', 'build']
+  ciTasks = ['output-disk-space', 'download-electron', 'download-electron-chromedriver', 'build']
   ciTasks.push('dump-symbols') if process.platform isnt 'win32'
   ciTasks.push('set-version', 'check-licenses', 'lint', 'generate-asar')
   ciTasks.push('mkdeb') if process.platform is 'linux'
@@ -266,6 +266,6 @@
   ciTasks.push('publish-build') unless process.env.TRAVIS
   grunt.registerTask('ci', ciTasks)

-  defaultTasks = ['download-atom-shell', 'download-atom-shell-chromedriver', 'build', 'set-version', 'generate-asar']
+  defaultTasks = ['download-electron', 'download-electron-chromedriver', 'build', 'set-version', 'generate-asar']
   defaultTasks.push 'install' unless process.platform is 'linux'
   grunt.registerTask('default', defaultTasks)


Fix the version pin

Fix the version pin

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -163,7 +163,7 @@
       ]

     'build-atom-shell':
-      tag: "13c887d"
+      tag: "6dac8da"
       nodeVersion: '0.21.0'
       remoteUrl: "https://github.com/atom/atom-shell"
       buildDir: buildDir


Hack to fix visibility

Hack to fix visibility

--- src/popout_for_youtube.coffee
+++ src/popout_for_youtube.coffee
@@ -43,14 +43,22 @@
 # this is where you want to look.
 #
 fix_visibility = ->
-  $([
-    # Flash and WebM player
-    '#watch7-player'
-
-    # channel page
-    '#branded-page-body'
-    '.tab-content-body'
-  ].join(',')).css {overflow: 'visible'}
+
+  #
+  # FIXME I don't like using an interval here, but something is happening on the
+  # page after the button is loaded and the visibility set that unsets the
+  # visibility.
+  #
+  setInterval ->
+    $([
+      # Flash and WebM player
+      '#watch7-player'
+
+      # channel page
+      '#branded-page-body'
+      '.tab-content-body'
+    ].join(',')).css {overflow: 'visible'}
+  , 500

 #


Improve menu entries

Improve menu entries

Capitalize package name to match other package names.
Add other available commands to the menu.

--- menus/paredit.cson
+++ menus/paredit.cson
@@ -7,9 +7,14 @@
   {
     'label': 'Packages'
     'submenu': [
-      'label': 'paredit'
+      'label': 'Paredit'
       'submenu': [
+        { 'label': 'Forward Barf', 'command': 'paredit:forward-barf' }
         { 'label': 'Forward Slurp', 'command': 'paredit:forward-slurp' }
+        { 'label': 'Backward Barf', 'command': 'paredit:backward-barf' }
+        { 'label': 'Backward Slurp', 'command': 'paredit:backward-slurp' }
+        { 'label': 'Kill Line', 'command': 'paredit:kill-line' }
+        { 'label': 'Raise Expression', 'command': 'paredit:raise-sexp' }
       ]
     ]
   }


More meaningful messages, cosmetic changes.

--- client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -82,7 +82,9 @@
           createButton.hideLoader()
           return warn err if err
           result = "Sorry, #{domainName} is taken,"
-          if suggestions.length > 0
+          if suggestions.length is 1
+            result = "#{result}
but we found an alternative:"
+          else if suggestions.length > 1
             result = "#{result}
but we found following alternatives:"
           else
             result = "#{result}
and we couldn't find any alternative."
@@ -181,7 +183,7 @@
 class CommonDomainCreationForm extends KDFormViewWithFields
   constructor:(options = {}, data)->
     super
-      cssClass              : KD.utils.curry "new-domain-form", options.cssClass
+      cssClass              : KD.utils.curry "new-domain-form",options.cssClass
       fields                :
         domainName          :
           name              : "domainInput"
@@ -256,16 +258,19 @@

     super options, data

+    price = (parseFloat @getData().price).toFixed 2
+    selectOptions = \
+      ({title: "#{i} year for $ #{(price * i).toFixed 2}", \
+        value: i} for i in [1..5])
+
+    @yearBox = new KDSelectBox {name:'year', selectOptions}
+
     @buyButton = new KDButtonView
       title    : "Buy"
       style    : "clean-gray"
       callback : ->
         alert 'Buy it'

-    price = (parseFloat @getData().price).toFixed 2
-    selectOptions = \
-      ({title: "#{i} year for $ #{(price * i).toFixed 2}" , value: i} for i in [1..5])
-    @yearBox = new KDSelectBox {name:'year', selectOptions}

   pistachio:->
     """


Navigation bug with Sammy

Navigation bug with Sammy

--- js/outer.coffee
+++ js/outer.coffee
@@ -43,4 +43,4 @@
   )

 # Run the application
-circle.run window.location.pathname
+$ -> circle.run window.location.pathname


fixed comments not showing up

fixed comments not showing up

--- modules/comment._coffee
+++ modules/comment._coffee
@@ -39,14 +39,14 @@

 		@queryAndFormatCommentResults query, params, _

-	getAllCommentsOfKnownodeID: (knownodeId, _) ->
-		@logger.logDebug @currentModule, "getAllCommentsOfKnownodeID #{knownodeId}"
-		queryParams = 'KN_ID' : knownodeId
+	getAllCommentsOfKnownodeID: (connectionId, _) ->
+		@logger.logDebug @currentModule, "getAllCommentsOfKnownodeID #{connectionId}"
+		queryParams = 'KN_ID' : connectionId

 		query = [
-			'START root=node(*) ',
-			'MATCH (root) <-[r:COMMENT_OF*]- (comment) -[u:CREATED_BY]-> (commentUser)',
-			'WHERE has(root.KN_ID) AND root.KN_ID = {KN_ID} '
+			'START connection=node(*) ',
+			'MATCH (connection) <-[r:COMMENT_OF*]- (comment) -[u:CREATED_BY]-> (commentUser)',
+			'WHERE has(connection.KN_ID) AND connection.KN_ID = {KN_ID} '
 			'RETURN comment, r, commentUser'
 		].join('\n');


Remove external route

Remove external route

--- server/controllers/routes.coffee
+++ server/controllers/routes.coffee
@@ -42,8 +42,8 @@
     'apps/:name/*': all: [utils.isAuthenticated, apps.app]
     'apps/:name*': all: [utils.isAuthenticated, apps.appWithSlash]

-    #'cozy/*': all: devices.replication
-    'cozy/*': all: devices.external #access couchdb directly
+    'cozy/*': all: devices.replication
+    #'cozy/*': all: devices.external #access couchdb directly

     '.well-known/host-meta.?:ext': get: experiment.webfingerHostMeta
     '.well-known/:module': all: experiment.webfingerAccount


Fixed: the report must be sent AFTER the accounts are polled

--- server/lib/report-manager.coffee
+++ server/lib/report-manager.coffee
@@ -17,8 +17,9 @@
         @prepareNextCheck()

     prepareNextCheck: ->
-        # day after between 00:00am and 04:00am
-        delta =  Math.floor(Math.random() * 180)
+        # day after between 04:00am and 08:00am
+        # this must be triggered AFTER accounts were polled
+        delta =  Math.floor(Math.random() * 180 + 4)
         now = moment()
         nextUpdate = now.clone().add(1, 'days')
                             .hours(1)


add more aliases

add more aliases

--- src/scripts/api-status.coffee
+++ src/scripts/api-status.coffee
@@ -18,14 +18,14 @@
     'preprod-web1': 'http://10.21.6.26/review/service-status/'
     'preprod-web2': 'http://10.21.4.101/review/service-status/'
   promotedoffer:
-    'prod-web1': 'http://192.168.220.181/promotedoffer/service-status/'
-    'prod-web2': 'http://192.168.220.182/promotedoffer/service-status/'
-    'preprod-web1': 'http://10.21.6.26/promotedoffer/service-status/'
-    'preprod-web2': 'http://10.21.4.101/promotedoffer/service-status/'
+    'prod-web1': 'http://192.168.220.181/promoted-offer/service-status/'
+    'prod-web2': 'http://192.168.220.182/promoted-offer/service-status/'
+    'preprod-web1': 'http://10.21.6.26/promoted-offer/service-status/'
+    'preprod-web2': 'http://10.21.4.101/promoted-offer/service-status/'
 }

 module.exports = (robot) ->
-  robot.respond /status (.*) (.*) (.*)?$/i, (msg) ->
+  robot.respond /status (.*) (.*) (.*)$/i, (msg) ->
     status msg

 # NOTE: messages contains new lines for some reason.
@@ -38,11 +38,9 @@
     monitor: msg.match[2]
     server: msg.match[3]

-  validateRequest bits, msg, () ->
-    msg.http('http://192.168.220.181/review/service-status/ProductFeedMonitor')
-      .get() (err, res, body) ->
-        json = JSON.parse(body)
-        msg.send "Current Status: #{json.Status || json.status}"
+  validateRequest(bits, msg, () ->
+    sendRequest buildUrl(bits), msg
+  )

 validateRequest = (bits, msg, callback) ->
   if not aliases[bits.app]


Change container to inline-block

Change container to inline-block

--- src/scripts/coffee/app.coffee
+++ src/scripts/coffee/app.coffee
@@ -68,7 +68,7 @@

       canvas = @canvas
       canvas.css('overflow', 'hidden')
-      canvas.css('display', 'block')
+      canvas.css('display', 'inline-block')
       canvas.css('border', '1px solid black')

       canvas = @canvas


one simple change to make console dramatically more usable; another on the backend that takes the min of recent resizes would then do it.

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -110,6 +110,7 @@
         @path = @opts.path

         @mark_file_use = debounce(@mark_file_use, 3000)
+        @resize        = debounce(@resize, 500)

         @_project_actions = redux.getProjectActions(@project_id)

@@ -117,6 +118,8 @@
         # editor is focused.  This impacts the cursor, and also whether
         # messages such as open_file or open_directory are handled (see @init_mesg).
         @is_focused = false
+
+        @allow_resize = true

         # Create the DOM element that realizes this console, from an HTML template.
         @element = console_template.clone()
@@ -292,7 +295,7 @@
                 @render(data)

             if @_needs_resize
-                @resize()
+                @resize(true)

         @session.on 'reconnecting', () =>
             #console.log('terminal: reconnecting')
@@ -338,7 +341,7 @@
             @append_to_value(@session.init_history)

         @terminal.showCursor()
-        @resize()
+        @resize(true)

     render: (data) =>
         #console.log "render '#{data}'"
@@ -461,6 +464,7 @@

     client_keydown: (ev) =>
         #console.log("client_keydown")
+        @allow_resize = true

         if @_ignore
             # no matter what cancel ignore if the user starts typing, since we absolutely must not loose anything they type.
@@ -503,7 +507,7 @@
         $(@terminal.element).css('font-size':"#{@opts.font.size}px")
         @element.find(".webapp-console-font-indicator-size").text(@opts.font.size)
         @element.find(".webapp-console-font-indicator").stop().show().animate(opacity:1).fadeOut(duration:8000)
-        @resize()
+        @resize(true)

     _init_font_make_default: () =>
         @element.find("a[href=\"#font-make-default\"]").click () =>
@@ -598,18 +602,22 @@
         @element.find("a").tooltip(delay:{ show: 500, hide: 100 })

         @element.find("a[href=\"#increase-font\"]").click () =>
+            @allow_resize = true
             @_increase_font_size()
             return false

         @element.find("a[href=\"#decrease-font\"]").click () =>
+            @allow_resize = true
             @_decrease_font_size()
             return false

         @element.find("a[href=\"#refresh\"]").click () =>
+            @allow_resize = true
             @session?.reconnect()
             return false

         @element.find("a[href=\"#paste\"]").click () =>
+            @allow_resize = true
             id = uuid()
             s = "
 Terminal Copy and Paste
Copy and paste in terminals works as usual: to copy, highlight text then press ctrl+c (or command+c); press ctrl+v (or command+v) to paste.

NOTE: When no text is highlighted, ctrl+c sends the usual interrupt signal.

You can copy the terminal history from here:

<textarea readonly style='font-family: monospace;cursor: auto;width: 97%' id='#{id}' rows=10></textarea>"
             bootbox.alert(s)
@@ -814,7 +822,7 @@
             top       : "3.5em"
             bottom    : 1

-        @resize()
+        @resize(false)

     # exit fullscreen mode
     exit_fullscreen: () =>
@@ -823,7 +831,7 @@
                 position : 'relative'
                 top      : 0
                 width    : "100%"
-        @resize()
+        @resize(false)

     refresh: () =>
         @terminal.refresh(0, @opts.rows-1)
@@ -833,7 +841,11 @@
     # Determine the current size (rows and columns) of the DOM
     # element for the editor, then resize the renderer and the
     # remote PTY.
-    resize: () =>
+    resize: (internal) =>
+        if internal and not @allow_resize
+            return
+        @allow_resize = false
+
         if not @session?
             # don't bother if we don't even have a remote connection
             # FUTURE: could queue this up to send


Making model more like the docs

Making model more like the docs

--- models/user.coffee
+++ models/user.coffee
@@ -1,6 +1,9 @@
 mongoose = require 'mongoose'
+Schema = mongoose.Schema
+mongo = mongoose.mongo
+ObjectID = mongo.ObjectID

-userSchema = new mongoose.Schema
+userSchema = new Schema
   name: String

 User = mongoose.model('User', userSchema)


nicer text

nicer text

--- src/coffee/models/markdown.coffee
+++ src/coffee/models/markdown.coffee
@@ -1,6 +1,22 @@
 class Monolith.Models.Markdown extends Backbone.Model
   defaults:
-    text: '#runner @ kit\n3 credits\ninstalled\n\tdatasucker\ngrip\n\tsure gamble\n\tparasite'
+    text: """
+#runner @ kit
+3 credits
+installed
+	cache
+		7 virus counters
+	datasucker
+		2 virus
+	dinosaurus
+		yog.0
+grip
+	sure gamble
+	parasite
+
+#current
+net celebrity
+"""
     game: null
     viewModel: null


pubsub_server: lower logging

pubsub_server: lower logging

--- src/xmpp/pubsub_server.coffee
+++ src/xmpp/pubsub_server.coffee
@@ -180,7 +180,7 @@
         @discoItemsEl?

     reply: (results) ->
-        logger.log 'DiscoItemsRequest.reply': results
+        logger.debug 'DiscoItemsRequest.reply': results
         queryEl = new xmpp.Element("query", xmlns: NS.DISCO_ITEMS)
         if results?.node
             queryEl.attrs.node = results.node
@@ -780,7 +780,7 @@
         for r in REQUESTS
             result = new r(stanza)
             if result.matches()
-                logger.debug 'found subrequest', r.name
+                logger.trace 'found subrequest', r.name
                 break
             else
                 result = null


Fix random element max

Fix random element max

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -397,7 +397,7 @@
   $(".random-pill").each((idx, el) ->
     page = $(this).data("page")
     if page == "engagement"
-      chosen = $($(this).children('.segment-pill')[Math.floor(Math.random() * 4)])
+      chosen = $($(this).children('.segment-pill')[Math.floor(Math.random() * $(this).children('.segment-pill').length)])
       chosen.addClass("selected")
       metricSlug = chosen.attr("data-metric-slug")
       segmentType = chosen.attr("data-segment-type")


onChange -> onFileChange

onChange -> onFileChange

--- client/app/MainApp/fs/fswatcher.coffee
+++ client/app/MainApp/fs/fswatcher.coffee
@@ -33,7 +33,7 @@
       method     : 'fs.readDirectory'
       vmName     : @vmName
       withArgs   :
-        onChange : (change)=> @onChange @path, change
+        onChange : (change)=> @onFileChange @path, change
         path     : FSHelper.plainPath @path
         watchSubdirectories : @getOption 'recursive'
     , (err, response)=>
@@ -54,7 +54,7 @@
   onFileRemoved:(change)->
     # warn "File removed:", change.file.fullPath

-  onChange:(path, change)->
+  onFileChange:(path, change)->

     if @getOption 'ignoreTempChanges'
       return  if /^\.|\~$/.test change.file.name


Account Settings: Credentials: Remove Engine Yard from providers config and add Azure instead of it.

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -85,18 +85,18 @@
       #   placeholder        : "google zone"
       #   defaultValue       : "us-central1-a"

-  engineyard               :
-    name                   : "EngineYard"
-    link                   : "https://www.engineyard.com/"
-    title                  : "EngineYard"
-    description            : "EngineYard"
+  azure                    :
+    name                   : "Azure"
+    link                   : "https://azure.microsoft.com/"
+    title                  : "Azure"
+    description            : "Azure"
     credentialFields       :
       accountId            :
         label              : "Account Id"
-        placeholder        : "account id in engineyard"
+        placeholder        : "account id in azure"
       secret               :
         label              : "Secret"
-        placeholder        : "engineyard secret"
+        placeholder        : "azure secret"
         type               : "password"

   digitalocean             :


provider/vagrant: minor text changes

provider/vagrant: minor text changes

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -176,5 +176,5 @@
     description            : 'Local provisioning with Vagrant'
     credentialFields       :
       queryString          :
-        label              : 'Kite Query Path'
-        placeholder        : 'querystring for local machine kite'
+        label              : 'Kite ID'
+        placeholder        : 'Querystring for local machine kite'


Fixed: when a task is put at the end of a sub-selection, reorder shouldn't happen

--- client/app/views/task_list_view.coffee
+++ client/app/views/task_list_view.coffee
@@ -212,7 +212,8 @@
         nextOfNextModel = @baseCollection.at(nextIndex + 1) or null

         # if not last item of the collection
-        if nextIndex isnt @baseCollection.length
+        if nextIndex isnt @baseCollection.length and \
+           nextIndexInSubCollection isnt @collection.length
             newOrder = @baseCollection.getNewOrder next, nextOfNextModel
             {order, step} = newOrder
             currentModel.set 'order', order


Set loading while query is fetching records

--- client/lanes/components/grid/Grid.cjsx
+++ client/lanes/components/grid/Grid.cjsx
@@ -114,7 +114,9 @@
         <Lanes.Components.Grid.Toolbar key="toolbar" {...props} />

     dataSource: (q) ->
-        @query.results.allRows(page: q.page, visibleOnly: true).then (rows) ->
+        @setState(is_loading: true)
+        @query.results.allRows(page: q.page, visibleOnly: true).then (rows) =>
+            @setState(is_loading: false)
             count: rows.length, data: rows

     render: ->
@@ -136,7 +138,7 @@
                     onSortChange={@onSortChange}
                     onSelectionChange={@onRowClick}
                     idProperty='0'
-                    loading={true}
+                    loading={@state.is_loading}
                     pagination={true}
                     onColumnResize={@onColumnResize}
                     paginationToolbarProps = { position: 'top' }


fixed ternary expression for coffee

fixed ternary expression for coffee

--- client/src/views/text_edit_view.coffee
+++ client/src/views/text_edit_view.coffee
@@ -17,7 +17,7 @@
     @render()

   render: (options = {}) =>
-    template = options.edit ? @editTemplate : @template
+    template = if options.edit then @editTemplate else @template

     @$el.html(template(content: @report.get(@type), type: @type))


tests: fixed koding-logo id usage

tests: fixed koding-logo id usage

deprecated on teams product

--- client/test/lib/helpers/helpers.coffee
+++ client/test/lib/helpers/helpers.coffee
@@ -115,7 +115,7 @@

   assertMainHeader: (browser, assertLoginLink = yes) ->

-    logoSelector = '[testpath=main-header] a#koding-logo'
+    logoSelector = '[testpath=main-header] a.koding-header-logo'
     loginLinkSelector = '[testpath=main-header] [testpath=login-link]'

     browser.waitForElementVisible logoSelector, 25000


Switch submit button from "Register" to "Enroll"

--- src/course/invite-code-input.cjsx
+++ src/course/invite-code-input.cjsx
@@ -35,7 +35,7 @@
         waitingText={'Registering…'}
         onClick={@startRegistration}
       >
-        Register
+        Enroll
       </AsyncButton>

     <div className="form-group">


#3 экспорт констант внутри класса

#3 экспорт констант внутри класса

--- copy-files.coffee
+++ copy-files.coffee
@@ -5,14 +5,6 @@
 path = require 'path'

 {normalize, basename, dirname, extname, join, existsSync} = path
-
-REPLACE       = 0
-SKIP          = 1
-REPLACE_OLDER = 2
-
-STATUS_SUCCESS    = 0
-STATUS_PENDING    = 1
-STATUS_FAIL       = -1

 class CopyFiles
   defaults:
@@ -22,6 +14,25 @@
     on_complete: null
   numOfOpenFiles: 0
   maxNumOfOpenFiles: 100
+
+  # for internal use
+  STATUS_SUCCESS = 0
+  STATUS_PENDING = 1
+  STATUS_FAIL    = -1
+
+  # for export
+  STATUS_SUCCESS: STATUS_SUCCESS
+  STATUS_FAIL :   STATUS_FAIL
+
+  # for internal use
+  REPLACE       = 0
+  SKIP          = 1
+  REPLACE_OLDER = 2
+
+  # for export
+  REPLACE:       REPLACE
+  SKIP:          SKIP
+  REPLACE_OLDER: REPLACE_OLDER

   constructor: (src, dst, options) ->
     throw "Error: #{src} not found!" unless existsSync(src)


Utils: Added applyGradient method.

Utils: Added applyGradient method.

--- core/utils.coffee
+++ core/utils.coffee
@@ -686,6 +686,14 @@
         i += 3
     string

+  applyGradient: (kdViewInstance, color1, color2) ->
+    rules = [
+      "-moz-linear-gradient(100% 100% 90deg, #{color2}, #{color1})"
+      "-webkit-gradient(linear, 0% 0%, 0% 100%, from(#{color1}), to(#{color2}))"
+      "-o-linear-gradient(#{color1}, #{color2})"
+    ]
+    kdViewInstance.setCss "backgroundImage", rule for rule in rules
+
   # Return true x% of time based on argument.
   #
   # Example:


Ace: Refactored saveAs dialog to make it reusable.

--- core/utils.coffee
+++ core/utils.coffee
@@ -791,6 +791,57 @@
     if provider then mainController.emit "ForeignAuthCompleted", provider
     else mainController.emit "ForeignAuthFailed"

+  showSaveDialog: (container, callback = noop, options = {}) ->
+    container.addSubView dialog = new KDDialogView
+      cssClass      : KD.utils.curryCssClass "save-as-dialog", options.cssClass
+      duration      : 200
+      topOffset     : 0
+      overlay       : yes
+      height        : "auto"
+      buttons       :
+        Save        :
+          style     : "modal-clean-gray"
+          callback  : => callback input, finderController, dialog
+        Cancel      :
+          style     : "modal-cancel"
+          callback  : =>
+            finderController.stopAllWatchers()
+            delete finderController
+            finderController.destroy()
+            dialog.destroy()
+
+    dialog.addSubView wrapper = new KDView
+      cssClass : "kddialog-wrapper"
+
+    wrapper.addSubView form = new KDFormView
+
+    form.addSubView label = new KDLabelView
+      title : options.inputLabelTitle or "Filename:"
+
+    form.addSubView input = new KDInputView
+      label        : label
+      defaultValue : options.inputDefaultValue or ""
+
+    form.addSubView labelFinder = new KDLabelView
+      title : "Select a folder:"
+
+    dialog.show()
+    input.setFocus()
+
+    finderController    = new NFinderController
+      nodeIdPath        : "path"
+      nodeParentIdPath  : "parentPath"
+      foldersOnly       : yes
+      contextMenu       : no
+      loadFilesOnInit   : yes
+
+    finder = finderController.getView()
+    finderController.reset()
+
+    form.addSubView finderWrapper = new KDView cssClass : "save-as-dialog save-file-container", null
+    finderWrapper.addSubView finder
+    finderWrapper.setHeight 200
+
   # deprecated ends

 ###


Use `Icon` component instead of `i`

Use `Icon` component instead of `i`

--- resources/assets/coffee/react/profile-page/user-page.coffee
+++ resources/assets/coffee/react/profile-page/user-page.coffee
@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
 ###
-{a, button, div, i, p} = React.DOM
+{a, button, div, p} = React.DOM
 el = React.createElement

 class ProfilePage.UserPage extends React.Component
@@ -30,7 +30,7 @@
               className: 'forum-post-actions__action'
               href: '#'
               onClick: @editStart
-              i className: 'fa fa-edit'
+              el Icon, name: 'edit'

       if @props.userPage.editing
         el ProfilePage.UserPageEditor, userPage: @props.userPage
@@ -54,7 +54,7 @@
         Lang.get('users.show.page.edit_big')

       p className: 'profile-page-new-content profile-page-new-icon',
-        i className: 'fa fa-pencil-square-o'
+        el Icon, name: 'pencil-square-o'

       p
         className: 'profile-page-new-content'


Expose Acho.defaults

Expose Acho.defaults

--- lib/Acho.coffee
+++ lib/Acho.coffee
@@ -37,4 +37,6 @@
     defaults(params, skin)
     Acho(params)

+Acho.defaults = DEFAULT
+
 module.exports = Acho


extract_instruction

--- src/layout/mixins/programs.coffee
+++ src/layout/mixins/programs.coffee
@@ -17,6 +17,12 @@
 {my} = require '../../my'
 {make} = require '../../render/make' # TODO: find better path-ing

+# method, key, integer
+extract_instruction = (contents) ->
+  instruction = contents.split " "
+  instruction[2] = parseInt instruction[2]
+  instruction
+
 exports.programs = (sprite) ->

   get_next_index = (current_index, count) ->
@@ -47,12 +53,12 @@
       action = world.get 'next_action'
       world.call 'perform', action.get('value') if action

-    perform: (world, action) ->
-      contents = sprite.get('actions').get(action)
-      instruction = contents.split " "
-      instruction[2] = parseInt instruction[2]
+    perform: (world, key) ->
+      contents = sprite.get('actions').get(key)
+      world.call 'perform_instruction', contents

-      [method, key, value] = instruction
+    perform_instruction : (world, contents) ->
+      [method, key, value] = extract_instruction contents
       my.assert sprite[method], "#{sprite.label()}: no '#{method}' property"
       sprite[method](key, value)


Add tests for multiple opening and closing parens.

--- spec/bracket-matcher-spec.coffee
+++ spec/bracket-matcher-spec.coffee
@@ -50,6 +50,25 @@
         editor.moveLeft()
         editor.moveLeft()
         expectHighlights([12, 0], [0, 28])
+
+    describe "when closing multiple pairs", ->
+      it "always highlights the inner pair", ->
+        editor.setCursorBufferPosition([8, 53])
+        expectHighlights([8, 53], [8, 47])
+        editor.moveRight()
+        expectHighlights([8, 53], [8, 47])
+        editor.moveRight()
+        expectHighlights([8, 54], [8, 42])
+
+    describe "when opening multiple pairs", ->
+      it "always highlights the inner pair", ->
+        editor.setText '((1 + 1) * 2)'
+        editor.setCursorBufferPosition([0, 0])
+        expectHighlights([0, 0], [0, 12])
+        editor.moveRight()
+        expectHighlights([0, 1], [0, 7])
+        editor.moveRight()
+        expectHighlights([0, 1], [0, 7])

     describe "when the cursor is after an ending pair", ->
       it "highlights the starting pair and ending pair", ->


Fix upgrade message. Closes #644.

Fix upgrade message. Closes #644.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -104,7 +104,7 @@

   requestHandlers =
     hideUpgradeNotification: -> HUD.hideUpgradeNotification()
-    showUpgradeNotification: -> HUD.showUpgradeNotification()
+    showUpgradeNotification: (request) -> HUD.showUpgradeNotification(request.version)
     toggleHelpDialog: (request) -> toggleHelpDialog(request.dialogHtml, request.frameId)
     focusFrame: (request) -> if (frameId == request.frameId) then focusThisFrame(request.highlight)
     refreshCompletionKeys: refreshCompletionKeys
@@ -118,6 +118,7 @@
     # in the options page, we will receive requests from both content and background scripts. ignore those
     # from the former.
     return unless sender.tab?.url.startsWith 'chrome-extension://'
+    return unless isEnabledForUrl or request.name == 'getActiveState'
     sendResponse requestHandlers[request.name](request, sender)
     # Ensure the sendResponse callback is freed.
     false


Adds a "staticRoutes" property on the server config

This lets a user define something like this:

```
    server:
      staticRoutes:
        '/javascripts': 'js'
        '/stylesheets': 'css'
        '/fonts': '<%= files.webfonts.root %>'
        '/images': '<%= files.img.root %>'
        '/bin': '/Users/justin/bin'
```

This is a ninja feature that I'm sneaking in to work on lineman-rails.

--- tasks/server.coffee
+++ tasks/server.coffee
@@ -19,6 +19,7 @@
   httpProxy = require("http-proxy")
   fileUtils = require("./../lib/file-utils")
   watchr = require('watch_r-structr-lock')
+  fs = require("fs")

   grunt.registerTask "server", "static file & api proxy development server", ->
     apiPort = process.env.API_PORT || grunt.config.get("server.apiProxy.port") || 3000
@@ -28,6 +29,7 @@
     apiProxyChangeOrigin = grunt.config.get("server.apiProxy.changeOrigin") || undefined
     webPort = process.env.WEB_PORT || grunt.config.get("server.web.port") || 8000
     webRoot = grunt.config.get("server.base") || "generated"
+    staticRoutes = grunt.config.get("server.staticRoutes")
     userConfig = fileUtils.loadConfigurationFile("server")
     pushStateEnabled = grunt.config.get("server.pushState")
     @requiresConfig("server.apiProxy.prefix") if pushStateEnabled and apiProxyEnabled
@@ -39,6 +41,7 @@
     app.configure ->
       app.use(express.compress())
       app.use(express.static("#{process.cwd()}/#{webRoot}"))
+      mountUserStaticRoutes(app, webRoot, staticRoutes)

       userConfig.drawRoutes?(app)
       addBodyParserCallbackToRoutes(app)
@@ -61,6 +64,12 @@

     server.listen webPort, ->
       resetRoutesOnServerConfigChange(app)
+
+  mountUserStaticRoutes = (app, webRoot, staticRoutes) ->
+    _(staticRoutes).each (src, dest) ->
+      path = if fs.existsSync(src) then src else "#{process.cwd()}/#{webRoot}/#{src}"
+      grunt.log.writeln("Mounting static assets found in `#{path}` to route `#{dest}`")
+      app.use(dest, express.static(path))

   pushStateSimulator = (cwd, webRoot) ->
     (req, res, next) ->


--- spec/notice/notifier_spec.coffee
+++ spec/notice/notifier_spec.coffee
@@ -165,11 +165,45 @@
                 fn: (msg, next) -> next()

             mmm = _notifier().middleware['Assembly Line 6']
-
+
             mmm['arrange into single file'] {}, ->
             mmm['squirt the product in']    {}, ->
             mmm['put a lid on it']          {}, done
-
+
+
+        it 'sequence is preserved when replacing middleware', (done) ->
+
+            {sequence, deferred} = require 'also'
+            five = notifier().create 'Assembly Line 5'
+            five.use (msg, next) ->
+                msg.array.push 1
+                next()
+            five.use
+                title: 'REPLACE ME'
+                fn: (msg, next) ->
+                    msg.array.push 2
+                    next()
+            five.use
+                title: 'x'
+                fn: (msg, next) ->
+                    msg.array.push 3
+                    next()
+            five.use
+                title: 'REPLACE ME'
+                fn: (msg, next) ->
+                    msg.array.push 'new 2'
+                    next()
+
+            mmm = _notifier().middleware['Assembly Line 5']
+            msg = array: []
+            sequence( for key of mmm
+                do (key) -> deferred ({resolve}) ->
+                    mmm[key] msg, resolve
+            ).then ->
+
+                msg.array.should.eql [1, 'new 2', 3]
+                done()
+

 return
 os       = require 'os'


Add failing spec to demonstrate problem

Add failing spec to demonstrate problem

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -178,8 +178,14 @@
       atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'))
       expect(packageManager.packageHasSettings('package-with-config')).toBe true

-    it "returns false when the pacakge does not have config", ->
+    it "returns false when the pacakge does not have config and doesn't define language grammars", ->
       expect(packageManager.packageHasSettings('random-package')).toBe false

-      atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'language-test'))
-      expect(packageManager.packageHasSettings('language-test')).toBe false
+    it "returns true when the pacakge does not have config, but does define language grammars", ->
+      packageName = 'language-test'
+
+      waitsForPromise ->
+        atom.packages.activatePackage(path.join(__dirname, 'fixtures', packageName))
+
+      runs ->
+        expect(packageManager.packageHasSettings(packageName)).toBe true


change shortkey

change shortkey

--- keymaps/atom-lupa.cson
+++ keymaps/atom-lupa.cson
@@ -8,7 +8,7 @@
 # For more detailed documentation see
 # https://atom.io/docs/latest/behind-atom-keymaps-in-depth
 'atom-workspace':
-  'cmd-alt-l': 'atom-lupa:toggle'
+  'cmd-.': 'atom-lupa:toggle'

 '.lupa-structure .lupa-entity':
   'enter': 'atom-lupa:lupa-structure-enter'


Add toggle binding

Add toggle binding

--- keymaps/tree-view.cson
+++ keymaps/tree-view.cson
@@ -1,5 +1,6 @@
 'body':
   'meta-\\': 'tree-view:toggle'
+  'meta-k meta-b': 'tree-view:toggle'
   'meta-|': 'tree-view:reveal-active-file'

 '.tree-view':


separate qunit test modules with an extra blank line

--- app/coffee/tests/tests.coffee
+++ app/coffee/tests/tests.coffee
@@ -35,6 +35,7 @@
   equal logout_called, false
   $('#logout').click()
   equal logout_called, true, 'logout callback called correctly'
+

 module 'Basic game page',
   setup: ->


add docs command

add docs command

--- lib/dobi.coffee
+++ lib/dobi.coffee
@@ -19,6 +19,7 @@
 where <command> [command-specific-options] is one of:
   create <my-package> <type=app>    create a new package
   deploy <my-app>                   deploy an app (COMING SOON)
+  docs                              open the dobi docs
   init                              initialize a workspace
   install <my-app> <site-slug>      create a site using your app
   login                             authenticate your user
@@ -167,6 +168,11 @@
   when 'deploy'
     exit 'not available yet'

+  # open the dobi docs
+  when 'docs'
+    open 'http://www.dobi.io'
+    exit()
+
   # initialize a workspace
   when 'init'
     getWorkspacePath (workspace) ->


Fixed a broken hack

Fixed a broken hack

--- static/js/models.coffee
+++ static/js/models.coffee
@@ -55,7 +55,7 @@
         return @get('posts').ministry.length > 0

     get_party: ->
-        return PARTY_LIST_HACK @get('party')
+        return PARTY_LIST_HACK[@get('party')]

 class @MemberList extends Backbone.Tastypie.Collection
     urlRoot: URL_CONFIG['api_member']


remove comment

remove comment

--- lib/init.coffee
+++ lib/init.coffee
@@ -1,15 +1,3 @@
-# Your init script
-#
-# Atom will evaluate this file each time a new window is opened. It is run
-# after packages are loaded/activated and after the previous editor state
-# has been restored.
-#
-# An example hack to log to the console when each text editor is saved.
-#
-# atom.workspace.observeTextEditors (editor) ->
-#   editor.onDidSave ->
-#     console.log "Saved! #{editor.getPath()}"
-

 #plugin = require './plugin'
 debug = false


Fix typo

Fix typo

--- lib/init.coffee
+++ lib/init.coffee
@@ -20,6 +20,6 @@
   activate: ->
     console.log 'activate linter-package-json-validator' if atom.inDevMode()

-    packageDeps.install 'linter-node markdownlint'
+    packageDeps.install 'package-json-validator'

   provideLinter: -> PJVProvider


Add link to panoptes aggregation code (#5023)

This link has been added to the data exports page along side the
Data-digging link.

--- app/pages/lab/data-dumps.cjsx
+++ app/pages/lab/data-dumps.cjsx
@@ -42,6 +42,9 @@
         For examples of how to work with the data exports, see our&nbsp

           Data Digging code repository
+         or use our&nbsp
+
+          Panoptes Aggregation python package
         .


Removed a console.log debug

Removed a console.log debug

--- lib/main.coffee
+++ lib/main.coffee
@@ -63,7 +63,6 @@
             config = JSON.parse(fs.readFileSync(jslintrcPath, "utf-8"))
           catch err
             console.log "Error reading config file \"" + jslintrcPath + "\": " + err  if err.code isnt "ENOENT"
-        console.log config

         text = textEditor.getText()


Export entire 'errors' submodule

Export entire 'errors' submodule

--- lib/main.coffee
+++ lib/main.coffee
@@ -42,5 +42,5 @@
   # the 'services' dependencyType.
   graph: require './graph'

-  # Export LawError base type for extensions to depend upon.
-  LawError: require('./errors').LawError+  # Export 'errors' module for extensions to depend upon.
+  errors: = require './errors'


Shift instead of pop

Shift instead of pop

--- lib/main.coffee
+++ lib/main.coffee
@@ -26,7 +26,7 @@
     _ = require 'underscore-plus'

     @paneSubscription = atom.workspace.observePanes (pane) =>
-      tabBarView = new TabBarView(pane, state.pop())
+      tabBarView = new TabBarView(pane, state.shift())

       paneElement = atom.views.getView(pane)
       paneElement.insertBefore(tabBarView.element, paneElement.firstChild)


use spawn over exec

use spawn over exec

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,5 +1,5 @@
 # Dependencies
-{exec} = require 'child_process'
+{spawn} = require 'child_process'
 os = require 'os'

 module.exports = NslCore =
@@ -44,14 +44,14 @@
     if script? and scope.startsWith 'source.nsl'
       editor.save() if editor.isModified()

-      @getPath (stdout) ->
+      @getPath (data) ->
         nslJar  = atom.config.get('language-nsl.pathToJar')

         if not nslJar
           atom.notifications.addError("**language-nsl**: no valid `nsL.jar` was specified in your config", dismissable: false)
           return

-        defaultArguments = ["java", "-jar", nslJar]
+        defaultArguments = ["-jar", nslJar]
         customArguments = atom.config.get('language-nsl.customArguments').trim().split(" ")

         if os.platform() is 'win32'
@@ -59,28 +59,31 @@
         else
           customArguments.push(script)

-        nslCmd = defaultArguments.concat(customArguments)
+        args = defaultArguments.concat(customArguments)
+        nslCmd = spawn('java', args);

-        exec nslCmd.join(" "), (error, stdout, stderr) ->
-          if error isnt null
-            # nslJar error from stdout, not error!
-            atom.notifications.addError("**#{script}**", detail: error, dismissable: true)
-          else
-            atom.notifications.addSuccess("Compiled successfully", detail: stdout, dismissable: false)
+        nslCmd.stderr.on 'data', (data) ->
+          atom.notifications.addError("Compilation error", detail: data, dismissable: true)
+          return
+
+        nslCmd.stdout.on 'data', (data) ->
+          atom.notifications.addSuccess("Compiled successfully", detail: data, dismissable: false)
+          return
     else
       # Something went wrong
       atom.beep()

   getPath: (callback) ->
     if os.platform() is 'win32'
-      which  = "where"
+      whichJava = spawn('where', ['java']);
     else
-      which  = "which"
+      whichJava = spawn('which', ['java']);

     # Find Java
-    exec "\"#{which}\" java", (error, stdout, stderr) ->
-      if error isnt null
-        atom.notifications.addError("**language-nsl**: Java is not in your `PATH` [environmental variable](http://superuser.com/a/284351/195953)", dismissable: true)
-      else
-        callback stdout
+    whichJava.stderr.on 'data', (data) ->
+      atom.notifications.addError("**language-nsl**: Java is not in your `PATH` [environmental variable](http://superuser.com/a/284351/195953)", dismissable: true)
       return
+
+    whichJava.stdout.on 'data', (data) ->
+      callback data
+      return


update moveMethods CSS class to all editorElement

--- lib/main.coffee
+++ lib/main.coffee
@@ -16,14 +16,19 @@
       default: 'insert'
       enum: ['insert', 'overwrite']

+  eachEditorElement: (fn) ->
+    atom.workspace.getTextEditors().forEach (editor) ->
+      editorElement = getView(editor)
+      fn(editorElement)
+
   activate: ->
     @subscriptions = new CompositeDisposable

-    @subscribe atom.config.observe MoveMethodConfig, (newValue) ->
-      editorElement = getView(getEditor())
-      editorElement.classList.remove OverwriteClass
-      if newValue is 'overwrite'
-        editorElement.classList.add(OverwriteClass)
+    @subscribe atom.config.observe MoveMethodConfig, (newValue) =>
+      @eachEditorElement (editorElement) =>
+        editorElement.classList.remove(OverwriteClass)
+        if newValue is 'overwrite'
+          editorElement.classList.add(OverwriteClass)

     @subscribe atom.commands.add 'atom-text-editor',
       'vim-mode-plus-user:toggle-move-method': ->
@@ -35,8 +40,8 @@
     @subscriptions?.dispose()
     @subscriptions = {}

-    atom.workspace.getTextEditors().forEach (editor) ->
-      getView(editor).classList.remove(OverwriteClass)
+    @eachEditorElement (editorElement) ->
+      editorElement.classList.remove(OverwriteClass)

   subscribe: (args...) ->
     @subscriptions.add args...


:art: refactoring

:art: refactoring

--- lib/main.coffee
+++ lib/main.coffee
@@ -22,41 +22,47 @@
     @vimStates = new Map

     @registerViewProviders()
-    @subscriptions.add Base.init(@provideVimModePlus())
+    @subscribe Base.init(@provideVimModePlus())
     @registerCommands()

     if atom.inDevMode()
       developer = (new (require './developer'))
-      @subscriptions.add developer.init()
+      @subscribe developer.init()

-    @subscriptions.add atom.workspace.observeTextEditors (editor) =>
+    @subscribe atom.workspace.observeTextEditors (editor) =>
       return if editor.isMini() or @vimStates.has(editor)
       vimState = new VimState(editor, @statusBarManager)
       @vimStates.set(editor, vimState)
       vimState.onDidDestroy =>
         @vimStates.delete(editor)

-    @subscriptions.add new Disposable =>
+    @subscribe new Disposable =>
       @vimStates.forEach (vimState) -> vimState.destroy()

+  subscribe: (args...) ->
+    @subscriptions.add args...
+
   registerCommands: ->
+    # all commands here is executed with context where 'this' binded to 'vimState'
+    vimStateCommands =
+      'activate-normal-mode': -> @activate('normal')
+      'activate-linewise-visual-mode': -> @activate('visual', 'linewise')
+      'activate-characterwise-visual-mode': -> @activate('visual', 'characterwise')
+      'activate-blockwise-visual-mode': -> @activate('visual', 'blockwise')
+      'activate-previous-visual-mode': -> @activate('visual', 'previous')
+      'reset-normal-mode': -> @activate('reset')
+      'set-count': (e) -> @count.set(e) # 0-9
+      'set-register-name': -> @register.setName() # "
+      'replace-mode-backspace': -> @modeManager.replaceModeBackspace()
+
     getState = =>
       @getEditorState(atom.workspace.getActiveTextEditor())

-    commands =
-      'activate-normal-mode': -> getState().activate('normal')
-      'activate-linewise-visual-mode': -> getState().activate('visual', 'linewise')
-      'activate-characterwise-visual-mode': -> getState().activate('visual', 'characterwise')
-      'activate-blockwise-visual-mode': -> getState().activate('visual', 'blockwise')
-      'activate-previous-visual-mode': -> getState().activate('visual', 'previous')
-      'reset-normal-mode': -> getState().activate('reset')
-      'set-count': (e) -> getState().count.set(e) # 0-9
-      'set-register-name': -> getState().register.setName() # "
-      'replace-mode-backspace': -> getState().modeManager.replaceModeBackspace()
-
     scope = 'atom-text-editor'
-    for name, fn of commands
-      @subscriptions.add atom.commands.add(scope, "#{packageScope}:#{name}", fn)
+    for name, fn of vimStateCommands
+      do (fn) =>
+        @subscribe atom.commands.add scope, "#{packageScope}:#{name}", (event) ->
+          fn.bind(getState())(event)

   registerViewProviders: ->
     atom.views.addViewProvider Hover, (model) ->
@@ -78,7 +84,7 @@
   consumeStatusBar: (statusBar) ->
     @statusBarManager.initialize(statusBar)
     @statusBarManager.attach()
-    @subscriptions.add new Disposable =>
+    @subscribe new Disposable =>
       @statusBarManager.detach()

   provideVimModePlus: ->


Change order of params for easier access

--- lib/misc.coffee
+++ lib/misc.coffee
@@ -2,7 +2,7 @@
   constructor: () ->
     @is_locked = false

-  try: (fun, args...) ->
+  try: (args..., fun) ->
     if @is_locked
       return


require a URL for POST request

require a URL for POST request

--- lib/putt.coffee
+++ lib/putt.coffee
@@ -33,7 +33,10 @@
     this

   post: (query_hash, options = {}) =>
-    r_restler.post(options.url, {data: query_hash}).on('success', @on_done).on('error', @on_error)
+    if options.url?
+      r_restler.post(options.url, {data: query_hash}).on('success', @on_done).on('error', @on_error)
+    else
+      @on_error("Please specify a URL")
     this

   tweet: (text, options = {}) =>


BUG: a loading icon can overlay the previous inline-result

--- lib/repl.coffee
+++ lib/repl.coffee
@@ -158,10 +158,6 @@
   # be of the shape like the following.
   # ["text for display", {button options}, [childtree1, childtree2, ...]]
   displayInline: (editor, range, tree, error=false)->
-    end = range.end.row
-
-    # Remove the existing view if there is one
-    @ink.Result.removeLines(editor, end, end)

     # Defines a recursive function that can convert the tree of values to
     # display into an Atom Ink tree view. Sub-branches are expandable.
@@ -179,6 +175,7 @@
     view = recurseTree(tree)

     # Add new inline view
+    end = range.end.row
     r = new @ink.Result editor, [end, end],
           content: view, error: error, type: if error then 'block' else 'inline'

@@ -240,6 +237,8 @@
     if options.inlineOptions?
       editor = options.inlineOptions.editor
       range = options.inlineOptions.range
+      # Remove the existing view if there is one
+      @ink.Result.removeLines(editor, range.row.end, range.row.end)
       # use the id for asynchronous eval/result
       spinid = @loadingIndicator.startAt(editor, range)


npe-x500-m3.coffee: Set Yocto version to Warrior

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@balena.io>

--- npe-x500-m3.coffee
+++ npe-x500-m3.coffee
@@ -30,7 +30,7 @@
 		machine: 'npe-x500-m3'
 		image: 'resin-image'
 		fstype: 'resinos-img'
-		version: 'yocto-thud'
+		version: 'yocto-warrior'
 		deployArtifact: 'resin-image-npe-x500-m3.resinos-img'
 		compressed: true


Fix reserveds

Fix reserveds

--- lib/tree.coffee
+++ lib/tree.coffee
@@ -94,9 +94,9 @@
     tos = edge.subTo or ["t#{id}_#{edge.to}"]

     dot = ''
-    for from in froms
-      for to in tos
-        dot += "#{prefix}  #{from} -> #{to}"
+    for fromEdge in froms
+      for toEdge in tos
+        dot += "#{prefix}  #{fromEdge} -> #{toEdget}"
         attributes = {}
         attributes.label = edge.name or edge.type


Fix getSymbolAtPoint handling of symbol at start of line.

--- lib/util.coffee
+++ lib/util.coffee
@@ -99,7 +99,7 @@
     find = (test) ->
       start = end = point
       start_ = start.translate [0, -1]
-      while test(start_) and start_.isGreaterThan(line.start)
+      while test(start_) and start_.isGreaterThanOrEqual(line.start)
         start = start_
         start_ = start.translate [0, -1]
       while test(end) and end.isLessThan(line.end)


maybe make realtime sync slightly more robust in an edge case (*NOT* sure about this)

--- src/smc-webapp/syncdoc.coffee
+++ src/smc-webapp/syncdoc.coffee
@@ -46,6 +46,8 @@

 {IS_MOBILE} = require('./feature')

+syncstring = require('smc-util/syncstring')
+
 class AbstractSynchronizedDoc extends EventEmitter
     file_path: () =>
         if not @_file_path?
@@ -185,6 +187,7 @@
         @codemirror1 = @editor.codemirror1
         @element     = @editor.element

+        # window.w = @
         # replace undo/redo by sync-aware versions
         for cm in [@codemirror, @codemirror1]
             cm.undo = @undo
@@ -251,7 +254,7 @@
                     if @_closed
                         return
                     #dbg("got upstream syncstring change: '#{misc.trunc_middle(@_syncstring.get(),400)}'")
-                    @codemirror.setValueNoJump(@_syncstring.get())
+                    @_set_codemirror_to_syncstring()
                     @emit('sync')

                 @_syncstring.on 'metadata-change', =>
@@ -264,7 +267,7 @@
                     if @_closed
                         return
                     #console.log("syncstring before change")
-                    @_syncstring.set(@codemirror.getValue())
+                    @_set_syncstring_to_codemirror()

                 @_syncstring.on 'deleted', =>
                     if @_closed
@@ -295,6 +298,27 @@
                 @emit('connect')   # successful connection
                 cb?()  # done initializing document (this is used, e.g., in the SynchronizedWorksheet derived class).

+    _set_syncstring_to_codemirror: =>
+        val = @codemirror.getValue()
+        @_last_val = val
+        @_syncstring.set(val)
+
+    _set_codemirror_to_syncstring: =>
+        val = @_syncstring.get()
+        cur_val = @codemirror.getValue()
+        if @_last_val? and cur_val != @_last_val
+            # We *MERGE* the changes in since, we made changes between when we last set
+            # the syncstrind and now.  This can perhaps sometimes happen in rare cases
+            # due to async save debouncing.  (Honestly, I don't know how this case could
+            # possibly happen.)
+            patch = syncstring.dmp.patch_make(@_last_val, cur_val)
+            val = syncstring.dmp.patch_apply(patch, val)[0]
+        else
+            patch = undefined
+        @codemirror.setValueNoJump(val)
+        if patch?
+            @_set_syncstring_to_codemirror()
+
     has_unsaved_changes: =>
         if not @codemirror?
             return false
@@ -322,7 +346,7 @@

     _sync: (cb) =>
         if @codemirror?  # need not be defined, right when user closes the editor instance
-            @_syncstring.set(@codemirror.getValue())
+            @_set_syncstring_to_codemirror()
         @_syncstring.save(cb)

     sync: (cb) =>
@@ -334,7 +358,7 @@
             return
         cm = @focused_codemirror()  # see https://github.com/sagemathinc/smc/issues/1161
         if not @_syncstring.in_undo_mode()
-            @_syncstring.set(cm.getValue())
+            @_set_syncstring_to_codemirror()
         value = @_syncstring.undo()
         cm.setValueNoJump(value, true)
         @save_state_debounce()
@@ -359,7 +383,7 @@
         if not @codemirror? or not @_fully_loaded
             cb() # nothing to do -- not initialized/loaded yet...
             return
-        @_syncstring.set(@codemirror.getValue())
+        @_set_syncstring_to_codemirror()
         async.series [@_syncstring.save, @_syncstring.save_to_disk], (err) =>
             @_update_unsaved_uncommitted_changes()
             if err


Promise dependency

Promise dependency

--- src/adaptorMethods/update.coffee
+++ src/adaptorMethods/update.coffee
@@ -1,3 +1,4 @@
+Promise = require 'promise'
 _ = require 'lodash'
 sendAttributesForSaving = require './_redisObjectSave'


Allow window.settlers.show_menu_alert to set value of menu alert span

--- static/script/settlers.coffee
+++ static/script/settlers.coffee
@@ -25,7 +25,7 @@

   update:		() ->
     new window.hlib.Ajax
-      url:			'/pull_notify/'
+      url:			'/pull_notify'
       keep_focus:		true
       handlers:
         h200:			(response, ajax) ->
@@ -126,8 +126,11 @@
 window.settlers.render_board_piece = (attrs) ->
   return '<span ' + ((attr_name + '="' + attr_value + '"' for own attr_name, attr_value of attrs).join ' ') + '></span>'

-window.settlers.show_menu_alert = (item) ->
+window.settlers.show_menu_alert = (item, cnt) ->
   eid = '#' + item + ' .menu-alert'
+
+  if cnt
+    $(eid).html cnt

   $(eid).show()


fix merge conflicts with master

fix merge conflicts with master

--- SingularityUI/app/collections/LogLines.coffee
+++ SingularityUI/app/collections/LogLines.coffee
@@ -2,13 +2,10 @@

 LogLine = require '../models/LogLine'

-<<<<<<< HEAD
 moment = require 'moment'

 orZero = (value) => if value < 0 then 0 else value

-=======
->>>>>>> master
 class LogLines extends Collection

     model: LogLine


add validation for case when user input is not in the database

--- app/assets/javascripts/auto_suggest.js.coffee
+++ app/assets/javascripts/auto_suggest.js.coffee
@@ -1,27 +1,34 @@
-# Uses JQuery's autocomplete to suggest a crop name in lieu of a
+# Uses JQuery's autocomplete to make a suggestion in lieu of a
 # preposterously long select dropdown. To implement add code to
 # the view like this:
 #
 # = auto_suggest @resource, :auto_suggest_source
+#
+# You must also add a search method to the resource's controller.

 jQuery ->
   if el = $( '.auto-suggest' )

+    id = $( '.auto-suggest-id' )
+    submit = $( '[name="commit"]' )
+    errorContainer = $( '' ).insertAfter(el)
+
     el.autocomplete
       minLength: 1,
-      source: el.attr( "data-source-url" ),
+      source: el.attr( 'data-source-url' ),
       focus: ( event, ui ) ->
         el.val( ui.item.name )
-        $( ".auto-suggest-id" ).val( ui.item.id )
+        id.val( ui.item.id )
         false
       select: ( event, ui ) ->
         el.val( ui.item.name )
-        $( ".auto-suggest-id" ).val( ui.item.id )
+        id.val( ui.item.id )
         false
       response: ( event, ui ) ->
+        id.val( "" )
         for item in ui.content
           if item.name == el.val()
-            $( ".auto-suggest-id" ).val( item.id )
+            id.val( item.id )

     if el.data( 'uiAutocomplete' )
       el.data( 'uiAutocomplete' )._renderItem = ( ul, item ) ->
@@ -30,3 +37,17 @@
           .append( "#{item.name}" )
           .appendTo( ul )

+    submit.on( 'click', (e) ->
+      if el.val() != '' && id.val() == ''
+        e.preventDefault()
+        errorContainer.prepend( '
That\'s not in our database.
' )
+        el.css( 'background-color', '#ffe6e6' )
+        $( 'html, body' ).animate({
+          scrollTop: ( el.offset().top ) - 200
+        }, 200)
+    )
+
+    el.focus( ->
+      el.css( 'background-color', '#fff' )
+      errorContainer.find( 'p' ).remove()
+    )


Use XMLHttpRequest directly, and handle progress

--- app/assets/javascripts/photo_upload.js.coffee
+++ app/assets/javascripts/photo_upload.js.coffee
@@ -12,23 +12,26 @@
 getFormFieldsFor = (file) ->
   $.getJSON "/sign", { username: "demo", filename: file.name }

+handleUploadProgress = (file, event) ->
+  if event.lengthComputable
+    percent = Math.floor((event.loaded / event.total) * 100)
+    console.log "#{file.name}: #{percent}% uploaded"
+
 uploadFile = (file) ->
   getFormFieldsFor(file).success (json) ->
     fd = new FormData()
     fd.append(key, value) for key, value of json.fields
     fd.append('file', file)
     console.log "Uploading #{file.name}"
-    request = $.ajax(
-      url: json.url,
-      type: "POST",
-      data: fd,
-      processData: false,
-      contentType: false,
-      cache: false
-    )
-    request.done (data) ->
-      console.log request
-      console.log "#{json.url}#{json.fields.key}"
+    xhr = new XMLHttpRequest()
+    xhr.upload.addEventListener "progress", ((e) -> handleUploadProgress(file, e)), false
+    xhr.addEventListener "load", (e) ->
+      if xhr.status == 204
+        console.log "Successfully uploaded #{file.name}"
+      else
+        console.log "Failed to upload #{file.name}: #{xhr.status}"
+    xhr.open "POST", json.url, true
+    xhr.send(fd)

 uploadFiles = (files) ->
   uploadFile(file) for file in files when file.type == "image/jpeg"


Remove scroll

Remove scroll

--- app/assets/javascripts/static_pages.js.coffee
+++ app/assets/javascripts/static_pages.js.coffee
@@ -20,9 +20,3 @@
   if window.devicePixelRatio > 1
     $(".retina").each (i) ->
       replaceRetina this
-  $("#toc a").click (e) ->
-    e.preventDefault()
-    target = $(this).attr("href")
-    $("html, body").animate
-      scrollTop: $(target).offset().top
-    , 1000


The GitHub button will not be rendered if is not configured

--- app/coffee/modules/integrations/github.coffee
+++ app/coffee/modules/integrations/github.coffee
@@ -47,8 +47,8 @@
     """ #TODO: i18n

     link = ($scope, $el, $attrs) ->
-        redirectToUri = $location.absUrl()
         clientId = $config.get("gitHubClientId", null)
+        return if not clientId

         renderGitHubButton = ->
             $el.html(template) if clientId
@@ -93,6 +93,7 @@
         loginWithGitHubAccount()

         $el.on "click", ".button-github", (event) ->
+            redirectToUri = $location.absUrl()
             url = "#{AUTH_URL}?client_id=#{clientId}&redirect_uri=#{redirectToUri}&state=github&scope=user:email"
             $window.location.href = url


sort playbills on the groups route - closes #168

--- app/assets/javascripts/components/group-page.cjsx
+++ app/assets/javascripts/components/group-page.cjsx
@@ -5,6 +5,11 @@
 SelectDropdown     = require('./select-dropdown')
 ReactSlider        = require('react-slider')
 API                = require('../lib/api')
+
+
+sort_first_pages_json = (a, b) ->
+  (2 * a['retired_from_mark'] + a['retired_from_transcribe']) -
+  (2 * b['retired_from_mark'] + b['retired_from_transcribe'])

 GroupPage = React.createClass
   displayName: "GroupPage"
@@ -60,6 +65,9 @@
       min_year = 9999
       max_year = 0
       min_max_year_array = []
+
+      # sort the first_pages_json so transcribed + marked appear later
+      first_pages_json.sort(sort_first_pages_json)

       # iterate through the first pages and update the available institutions and playwright options
       for first_page_json in first_pages_json
@@ -241,23 +249,23 @@

-                  <ReactSlider
+                  <ReactSlider
                     onAfterChange={@updateYearSliderState}
                     ref="ReactYearSlider"
-                    min={@state.min_year}
-                    max={@state.max_year}
+                    min={@state.min_year}
+                    max={@state.max_year}
                     value={[@state.start_year, @state.end_year]} withBars />

-
+

-                  <SelectDropdown options_array={@state.all_institutions}
+                  <SelectDropdown options_array={@state.all_institutions}
                                   placeholder={@state.placeholder_institution}
                                   value={@state.selected_institution}
                                   onSelect={@updateSelectedInstitution} />

-                  <SelectDropdown options_array={@state.all_playwrights}
+                  <SelectDropdown options_array={@state.all_playwrights}
                                   placeholder={@state.placeholder_playwright}
                                   value={@state.selected_playwright}
                                   onSelect={@updateSelectedPlaywright} />
@@ -275,7 +283,7 @@

-              <div className="collection-progressbar"
+


added mode default to coffee

added mode default to coffee

--- bokehjs/src/coffee/source/ajax_data_source.coffee
+++ bokehjs/src/coffee/source/ajax_data_source.coffee
@@ -16,11 +16,11 @@

     setup : (plot_view, glyph) =>
       @pv = plot_view
-      @get_data()
+      @get_data(@get('mode'))
       if @get('polling_interval')
         @interval = setInterval( @get_data, @get('polling_interval'), @get('mode'), @get('max_size'), @get('if_modified'))

-    get_data : (mode="replace", max_size=0, if_modified=false) =>
+    get_data : (mode, max_size=0, if_modified=false) =>
       $.ajax(
         dataType: 'json'
         ifModified: if_modified
@@ -46,6 +46,11 @@
       )
       return null

+    defaults: =>
+      return _.extend {}, super(), {
+        mode: 'replace'
+      }
+
   class AjaxDataSources extends Backbone.Collection
     model: AjaxDataSource
     defaults:


bugfix buttons create more configuration options for buttons

--- modules/gui/lib/editor/Editor.coffee
+++ modules/gui/lib/editor/Editor.coffee
@@ -8,12 +8,16 @@
         ##
         configure: (config)->

-            super new floyd.Config
+            config = super new floyd.Config

                 template: ->
                     div class:'editor Buttons floyd-loading', style:'opacity: .35'

             , config
+
+            @__buttons = config.buttons
+
+            return config

         ##
         ##
@@ -22,7 +26,7 @@
             super (err)=>
                 return done(err) if err

-                @_buildButtons config.buttons
+                @_buildButtons @__buttons
                 @_wireMouse()

@@ -60,13 +64,24 @@
                             handler: conf

                     @_createButton action, conf, (err, button)=>
+
+                        if typeof conf is 'string'
+                            conf =
+                                text: conf
+
+                        if typeof conf is 'function'
+                            conf =
+                                handler: conf

-                        button.text conf.text || action
+                        if conf.text
+                            button.text conf.text

                         if conf.title
                             button.attr 'title', conf.title

+
                         @_append button.click (event)=>
+
                             if conf.handler
                                 conf.handler.apply @, [event, _popup]

@@ -74,6 +89,8 @@
                                 @_emit action,
                                     event: event
                                     open: _popup
+                                    root: @__root
+                                    parent: @__root.parent()

                             return false


Root scope variable name change to prevent collisions

--- templates/coffee.coffee
+++ templates/coffee.coffee
@@ -70,7 +70,7 @@
   else "    ") + "factory(#{['root'].concat(globals.map(browserRequire)).join(', ')})"

   s "  return"
-  s ")(this, (#{['root'].concat(args).join(', ')}) ->"
+  s ")(this, (#{['__root__'].concat(args).join(', ')}) ->"

   s options.indent + options.contents
   unless options.global


deployment/nginx: add createUserMachineLocation

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -44,6 +44,37 @@
         proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_next_upstream   error timeout   invalid_header http_500;
         proxy_connect_timeout 1;
+      }
+  \n"""
+
+createUserMachineLocation = (path) ->
+  return """\n
+      location ~ ^\/#{path}\/(?<ip>.+?)\/(?<rest>.*) {
+        # define our dynamically created backend
+        set $backend $ip:3000/$rest;
+
+        # proxy it to the backend
+        proxy_pass http://$backend;
+
+        # needed for websocket handshake
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection $connection_upgrade;
+
+        # be a good proxy :)
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header Host $host;
+        proxy_redirect off;
+
+        # Don't buffer WebSocket connections
+        proxy_buffering off;
+
+        # Default is 60 seconds, means nginx will close it after 60 seconds
+        # inactivity which is a bad thing for long standing connections
+        # like websocket. Make it 6 hours.
+        proxy_read_timeout 21600s;
+        proxy_send_timeout 21600s;
       }
   \n"""

@@ -109,6 +140,7 @@
         #access_log off;
       }
       #{createLocations(workers)}
+      #{createUserMachineLocation("userproxy")}
     # close server
     }
   # close http


Add hidden property in to Account

Add hidden property in to Account

--- app/src/models/account.coffee
+++ app/src/models/account.coffee
@@ -9,6 +9,7 @@
   @index 'index', sync: yes
   @sync 'name'
   @sync 'color'
+  @sync 'hidden'

   @fromWalletAccount: (hdAccount) ->
     return null unless hdAccount?


subDocs (arrays) supported

subDocs (arrays) supported

--- lib/applyOp.coffee
+++ lib/applyOp.coffee
@@ -44,12 +44,25 @@
           if operation in removers
             return
           else
-            node[part] = {} # TODO: maybe do []/{} depending on manifest?
+            node[part] = if arrayIndex then [] else {}

+        # walk to the next node
         node = node[part]
+
+        # if we have an array component, find the appropriate item and walk to it
         if arrayIndex
-          targetItem = _.find node, (item) -> item.id is arrayIndex
-          node = targetItem if targetItem
+          subDoc = _.find node, (item) -> item.id is arrayIndex
+
+          # if we can't find the subDoc, create it
+          unless subDoc?
+            if operation in removers
+              return
+            else
+              subDoc = {id: arrayIndex}
+              node.push subDoc
+
+          # walk to the subdoc
+          node = subDoc

     # apply the appropriate change
     switch operation


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/core_object.coffee
+++ test/core_object.coffee
@@ -51,10 +51,7 @@

     test2: "cool"

-  o.include M
-
-  equals o.test(), "my_val"
-  equals o.test2, "cool"
+  o.include M, M2

 module()


console log

console log

--- apps/artwork/client/feature-navigation.coffee
+++ apps/artwork/client/feature-navigation.coffee
@@ -15,7 +15,6 @@
       if @kind == 'feature'
         "/feature/#{@model.id}"
       else if @kind == 'fair'
-        console.log @model
         "/#{@model.get('organizer').profile_id}"
       else
         "/#{@model.id}"


Even lower now.

Even lower now.

--- avalonstar/assets/javascripts/notifier.coffee
+++ avalonstar/assets/javascripts/notifier.coffee
@@ -11,9 +11,9 @@

 # Sounds.
 soundDonation = new Audio('/static/audio/donation.ogg')
-soundDonation.volume = 0.4
+soundDonation.volume = 0.25
 soundSubscription = new Audio('/static/audio/subscription.ogg')
-soundSubscription.volume = 0.4
+soundSubscription.volume = 0.25

 subscribed = (data, added) ->
   if not running


implement the cleanup() (which matches requirements) and remove the remove method

--- app/assets/javascripts/cartilage/views/loading_indicator_view.js.coffee
+++ app/assets/javascripts/cartilage/views/loading_indicator_view.js.coffee
@@ -93,8 +93,9 @@
     @_clearFrame(@_canvasContext)
     @_isAnimating = false

-  remove: =>
-    @stop() and super()
+  cleanup: ->
+    @stop()
+    super()

   #
   # Draw a frame.


fix for scanning; #51

fix for scanning; #51

--- app/assets/javascripts/views/admin/active_repositories_item_view.coffee
+++ app/assets/javascripts/views/admin/active_repositories_item_view.coffee
@@ -16,7 +16,7 @@
       <% }) %>

-      <a id="scan" href="/api/repos/<%= id %>/scan <%= name %>/scan"> scan
+      <a id="scan" href="/api/repos/<%= id %>/scan"> scan
        remove

   """)


added Ti.App.setWin

added Ti.App.setWin

--- Resources/app.coffee
+++ Resources/app.coffee
@@ -4,20 +4,23 @@

 Ti.App.win = null

+Ti.App.setWin (type)->
+  win = Ti.App.win
+  win.close() if win
+  win = Ti.App.win = K.createWindow({
+    fullscreen: true
+  })
+  $({type: type}).appendTo win
+  win.open({
+    transition: Titanium.UI.iPhone && Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
+  })
+
 main = ->

   Ti.Facebook.requestWithGraphPath('me', {}, 'GET', (e) ->
       if e.success
         Ti.App.facebookdata = JSON.parse(e.result);
-        win = Ti.App.win
-        win.close() if win
-        win = Ti.App.win = K.createWindow({
-          fullscreen: true
-        })
-        $({type: 'profile'}).appendTo win
-        win.open({
-          transition: Titanium.UI.iPhone && Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
-        })
+        Ti.App.setWin('profile')
     );


Fix nesting of matches list

Fix nesting of matches list

--- lib/color-results-element.coffee
+++ lib/color-results-element.coffee
@@ -92,10 +92,10 @@

         #{pathName}
         (#{matches.length + 1})

-

-          #{matches.map((match) => @createMatchResult match).join('')}
-

+

+        #{matches.map((match) => @createMatchResult match).join('')}
+

"""

   createMatchResult: (match) ->


Remove 'request' module reference

Remove 'request' module reference

--- lib/ims-lti.coffee
+++ lib/ims-lti.coffee
@@ -1,7 +1,6 @@
 fs          = require 'fs'
 https       = require 'https'
 urlUtil     = require 'url'
-request     = require 'request'
 querystring = require 'querystring'

 # Export the main object


:memo: Add documentation for the generatePlugin method

--- lib/minimap.coffee
+++ lib/minimap.coffee
@@ -96,6 +96,7 @@
       @emit('activated')
       @emitter.emit('did-activate')

+  # Public: Opens the plugin generation view.
   generatePlugin: ->
     view = new MinimapPluginGeneratorView()


pick only start date from range for key generation (this is going to break keys... I hate this)

--- chrome/content/zotero-better-bibtex/BetterBibTeXPatternFormatter.coffee
+++ chrome/content/zotero-better-bibtex/BetterBibTeXPatternFormatter.coffee
@@ -12,7 +12,13 @@

   format: (item) ->
     @item = Zotero.BetterBibTeX.serialized.get(item)
+
     return {} if @item.itemType in ['attachment', 'note']
+
+    if item.date
+      dates = @item.date.split('/')
+      dates = @item.date.split('_') unless dates.length == 2
+      @date = if dates.length == 2 then dates[0] else @item.date

     for candidate in @patterns[0]
       delete @postfix
@@ -240,22 +246,22 @@
       words.slice(0, 1).join('')

     shortyear: ->
-      return '' unless @item.date
-      date = Zotero.Date.strToDate(@item.date)
+      return '' unless @date
+      date = Zotero.Date.strToDate(@date)
       return '' if typeof date.year == 'undefined'
       year = date.year % 100
       return "0#{year}"  if year < 10
       return '' + year

     year: ->
-      return '' unless @item.date
-      date = Zotero.Date.strToDate(@item.date)
-      return @item.date if typeof date.year == 'undefined'
+      return '' unless @date
+      date = Zotero.Date.strToDate(@date)
+      return @date if typeof date.year == 'undefined'
       return date.year

     month: ->
-      return '' unless @item.date
-      date = Zotero.Date.strToDate(@item.date)
+      return '' unless @date
+      date = Zotero.Date.strToDate(@date)
       return '' if typeof date.year == 'undefined'
       return @months[date.month] ? ''


Fix up motion to stop on the top line

Fix up motion to stop on the top line

--- lib/motions.coffee
+++ lib/motions.coffee
@@ -24,7 +24,8 @@

 class MoveUp extends Motion
   execute: ->
-    @editor.moveCursorUp()
+    {column, row} = @editor.getCursorScreenPosition()
+    @editor.moveCursorUp() if row > 0

 class MoveDown extends Motion
   execute: ->


Correct specs around checking whether or not all of the models have their associations loaded in the caching layer.

--- spec/loaders/abstract-loader-shared-behavior.coffee
+++ spec/loaders/abstract-loader-shared-behavior.coffee
@@ -269,17 +269,21 @@
       context 'there exists a cache with this cacheKey', ->
         beforeEach ->
           loader.storageManager.storage('tasks').add taskOne
-          loader.storageManager.getCollectionDetails('tasks').cache['updated_at:desc|foo:bar||||'] = [key: "tasks", id: taskOne.id]
+          loader.storageManager.getCollectionDetails('tasks').cache['updated_at:desc|||||'] = [key: "tasks", id: taskOne.id]

         context 'all of the cached models have their associations loaded', ->
+          beforeEach ->
+            taskOne.set('project_id', buildAndCacheProject().id)
+
           it 'calls #_onLoadSuccess with the models from the cache', ->
-            opts.filters = foo: 'bar'
+            opts.include = ['project']
             loader.setup(opts)
             loader._checkCacheForData()
             expect(loader._onLoadSuccess).toHaveBeenCalledWith([taskOne])

         context 'all of the cached models do not have their associations loaded', ->
           it 'returns false and does not call #_onLoadSuccess', ->
+            opts.include = ['project']
             loader.setup(opts)
             notFound(loader, opts)


remove certain security properties from the compose file

--- lib/compose.coffee
+++ lib/compose.coffee
@@ -57,6 +57,19 @@
       delete service.volumes unless service.volumes
       service.volumes = service.volumes.filter((s) -> s) if service.volumes

+    restrictCompose = (serviceName, service) ->
+      delete service.cap_add
+      delete service.cap_drop
+      delete service.cgroup_parent
+      delete service.devices
+      delete service.dns
+      delete service.dns_search
+      delete service.tmpfs
+      delete service.networks
+      delete service.privileged
+
+
+
     migrateLinksToDependsOn = (serviceName, service) ->
       if service.links
         service.depends_on = _.union (service.depends_on or []), service.links
@@ -72,6 +85,7 @@
       addVolumeMapping serviceName, service
       addDockerMapping serviceName, service
       migrateLinksToDependsOn serviceName, service
+      restrictCompose serviceName, service

     version: '2'
     services: doc


Type names fix.

Type names fix.

--- lib/ChatService.coffee
+++ lib/ChatService.coffee
@@ -268,7 +268,7 @@

   # Gets the latest room message id.
   # @param roomName [String] Room name.
-  # @param cb [Function<error, Integer>] Sends ack with an
+  # @param cb [Function<error, Number>] Sends ack with an
   #   error or the latest message id.
   # @see UserCommands#roomHistorySync
   roomHistoryLastId : (roomName, cb) ->
@@ -278,7 +278,7 @@
   # `historyMaxGetMessages` option. May be called several times to
   # fill gaps larger then the value of `historyMaxGetMessages`.
   # @param roomName [String] Room name.
-  # @param id [Integer] Message id,
+  # @param id [Number] Message id,
   # @param cb [Function<error, Array<Objects>>] Sends ack with an
   #   error or array of messages.
   # @see UserCommands#roomHistoryLastId
@@ -359,7 +359,7 @@
   # Crates an object and starts a new server instance.
   #
   #
-  # @option serviceOptions [Integer] closeTimeout Maximum time to wait
+  # @option serviceOptions [Number] closeTimeout Maximum time to wait
   #   before a server disconnects all clients in ms, default is
   #   `5000`.
   #
@@ -380,14 +380,14 @@
   #   {ServerMessages#roomUserJoined} and
   #   {ServerMessages#roomUserLeft} messages, default is `false`.
   #
-  # @option serviceOptions [Integer] historyMaxGetMessages Room
+  # @option serviceOptions [Number] historyMaxGetMessages Room
   #   history size available via {UserCommands#roomHistory} or
   #   {UserCommands#roomHistorySync}, default is `100`.
   #
-  # @option serviceOptions [Integer] historyMaxMessages Room history
+  # @option serviceOptions [Number] historyMaxMessages Room history
   #   DB size, default is `10000`.
   #
-  # @option serviceOptions [Integer] port Server port, default is
+  # @option serviceOptions [Number] port Server port, default is
   #   `8000`.
   #
   # @option serviceOptions [Boolean] useRawErrorObjects Send error
@@ -486,7 +486,7 @@
   # @option stateOptions [Boolean] useCluster Enable Redis cluster,
   #   default is `false`.
   #
-  # @option stateOptions [Integer] lockTTL Locks timeout in ms,
+  # @option stateOptions [Number] lockTTL Locks timeout in ms,
   #   default is `5000`.
   #
   # @option stateOptions [Object or Array<Object>] redisOptions


Pedantic apostrophe Lets -> Let's

Pedantic apostrophe Lets -> Let's

This is a minor grammar change.

--- docker_tutorial/static/js/steps.coffee
+++ docker_tutorial/static/js/steps.coffee
@@ -158,7 +158,7 @@

Run your new image

Now you have basically setup a complete, self contained environment with the 'ping' program installed.

Your image can now be run on any host that runs Docker.

-
Lets run this image on this machine.

+
Let's run this image on this machine.

       """
 assignment: """

Assignment


Don't over init

Don't over init

--- test/integration/login.coffee
+++ test/integration/login.coffee
@@ -205,10 +205,6 @@

 describe 'Whitelabel', ->
-  before (done) ->
-    wd40.init ->
-      browser.get LOGIN_URL, done
-
   corpProfile =
     shortName: 'evilcorp'
     displayName: 'Evil Corp'


test for expression for initializer

test for expression for initializer

--- test/parser/statements.coffee
+++ test/parser/statements.coffee
@@ -43,6 +43,7 @@
 assert parses('for( ;; ) ;'), 'simple for loops should parse'
 assert parses('for( ;; ) {}'), 'for blocks should parse'
 assert parses('for( Integer i = 5;; ) ;'), 'for initializers should parse'
+assert parses('for( true;; ) ;'), 'for expression initializers should parse'
 assert parses('for( ; true; ) ;'), 'for conditions should parse'
 assert parses('for( ;; false ) ;'), 'for increments should parse'


Run only checkUIStyleSheet checks on styles.less

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -26,11 +26,6 @@

   if fs.isFileSync(userStyleSheetPath)
     userStyleSheet = fs.readFileSync(userStyleSheetPath, 'utf8')
-    linter.checkSyntaxStylesheet(userStyleSheet, {
-      packageName: "your local #{path.basename(userStyleSheetPath)} file"
-      packagePath: ""
-      sourcePath: userStyleSheetPath
-    })
     linter.checkUIStylesheet(userStyleSheet, {
       packageName: "your local #{path.basename(userStyleSheetPath)} file"
       packagePath: ""


:new: Add throwOnStdErr option

:new: Add throwOnStdErr option

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -18,13 +18,14 @@

   _exec: (command, args = [], options = {}, isNodeExecutable = false) ->
     options.stream ?= 'stdout'
+    options.throwOnStdErr ?= false
     return new Promise (resolve, reject) ->
       data = stdout: [], stderr: []
       stdout = (output) -> data.stdout.push(output.toString())
       stderr = (output) -> data.stderr.push(output.toString())
       exit = ->
         if options.stream is 'stdout'
-          if data.stderr.length
+          if data.stderr.length and options.throwOnStdErr
             reject(data.stderr.join(''))
           else
             resolve(data.stdout.join(''))


"Port old plunks to new schema"

"Port old plunks to new schema"

--- lib/plunker/stores/memory.coffee
+++ lib/plunker/stores/memory.coffee
@@ -17,8 +17,18 @@

 delay = (timeout, callback) -> setTimeout(callback, timeout)

+class Model extends Backbone.Model
+
 class Collection extends Backbone.Collection
   comparator: (model) -> -Cromag.parse(model.get("updated_at") or model.get("created_at"))
+  parse: (data) ->
+    _.map json, (json) ->
+      if matches = json.http_url.match(/^(http:\/\/[^\/]+)(.+)$/)
+        json.raw_url = "#{matches[0]}/raw#{matches[1]}"
+
+        for filename, file of json.files
+          file.raw_url = json.raw_url + filename
+      json

 class Store


adding elapsedTime

adding elapsedTime

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -39,7 +39,7 @@
   gameObject.update()

 test "elapsedTime", ->
-
+  gameObject = GameObje

 test "[event] destroy", 2, ->
   GameObject


add id for accessibility

add id for accessibility

--- src/components/surety-guard.cjsx
+++ src/components/surety-guard.cjsx
@@ -26,6 +26,7 @@

   renderPopover: ->
     <BS.Popover
+      id="confirmation-alert"
       className="surety-guard"
       title={@props.title}
     >


removing stray logging statement

removing stray logging statement

Former-commit-id: 510fcdd41da899b7c326e035fbac6dca5f45c08c [formerly 7c3562e4b7057347ac7114c30b2ac4e1e6622f44] [formerly 774970cd330893a58d63d684e1fc54a8bb833d5e [formerly a7c2d42cbd285b9d228d53523512291cfb0a9f9f]] [formerly c74f17db15adc6a68e90577924a5b3ac90a3607d [formerly 8f86f028717ed6ecc11cc603de9e45940a69d43a] [formerly 728464935e0151897836e638d4ae207ef83119b3 [formerly 728464935e0151897836e638d4ae207ef83119b3 [formerly 91a8c20a5020379967ac31d0436de45c0e124d28]]]]
Former-commit-id: 813ba76ba282b65b4ef184246a6b3c8b739a082b [formerly a7b629cd43b96e47329f67d2c6e9489e2db5109b] [formerly 8407a8fcf2bae860327476376cd2f9c7e2cdacd9 [formerly 2942e8b2d46ff0a484c9eb919630411e78debe81]]
Former-commit-id: 92b4b98d145d1d7334127ca405744ddeb320b4a8 [formerly 545226be0521df16bb17c2d4857362503c3d485d]
Former-commit-id: 09e48090757fa5b5069f16c49f342ce563a68073
Former-commit-id: b36d2573a84c01dd419408d97dbf39ea07c10070

--- @client/modules/franklin/proposal/proposal_controller.coffee
+++ @client/modules/franklin/proposal/proposal_controller.coffee
@@ -40,8 +40,6 @@
           @setupLayout @layout
           @setState @state
           @region.show @layout
-
-          console.log 'showing again!', @state, @region, @layout

       App.reqres.setHandler "proposal_controller:#{@model.id}", => @


WindowController: browser focus/blur events improved see commit details

Existing implementation was only firing on browser tab changes, but not on os wide app focus changes. This new implementation covers the both.

--- src/core/windowcontroller.coffee
+++ src/core/windowcontroller.coffee
@@ -13,18 +13,8 @@

   @keyViewHistory = []
   superKey        = if navigator.userAgent.indexOf("Mac OS X") is -1 then "ctrl" else "command"
-  addListener     = (eventName, listener, capturePhase=yes)->
+  addListener     = (eventName, listener, capturePhase = yes) ->
     window.addEventListener eventName, listener, capturePhase
-
-  # Finding vendor prefixes for visibility
-  getVisibilityProperty = ->
-    prefixes = ["webkit", "moz", "o"]
-    return "hidden" if `"hidden" in document`
-    return "#{prefix}Hidden" for prefix in prefixes when `prefix + "Hidden" in document`
-    return ""
-
-  getVisibilityEventName = ->
-    return "#{getVisibilityProperty().replace(/[Hh]idden/, '')}visibilitychange"

   constructor:(options,data)->

@@ -122,20 +112,21 @@
             KD.getSingleton("router").handleRoute href
     , no)

-    window.addEventListener 'beforeunload', @bound "beforeUnload"
-
-    document.addEventListener getVisibilityEventName(), (event)=>
-      @focusChange event, @isFocused()
+    addListener 'beforeunload', @bound 'beforeUnload'
+
+    window.onfocus = (event) => @focusChange event, yes
+    window.onblur  = (event) => @focusChange event, no

   addUnloadListener:(key, listener)->
     @unloadListeners[key] or= []
     @unloadListeners[key].push listener

   clearUnloadListeners: (key)->
+
     if key
-      @unloadListeners[key] = []
-    else
-      @unloadListeners = {}
+    then @unloadListeners[key] = []
+    else @unloadListeners = {}
+

   isFocused: -> !Boolean document[getVisibilityProperty()]

@@ -143,24 +134,28 @@

   focusChange: (event, state)->

-    return unless event
+    return  unless event
+
     listener state, event for listener in @focusListeners

+
   beforeUnload:(event)->

-    return unless event
+    return  unless event

     # all the listeners make their checks if it is safe or not to reload the page
     # they either return true or false if any of them returns false we intercept reload

     for own key, listeners of @unloadListeners
-      for listener in listeners
-        if listener() is off
-          message = unless key is "window" then " on #{key}" else ""
-          return "Please make sure that you saved all your work#{message}."
+      for listener in listeners when listener() is off
+        message = unless key is 'window' then " on #{key}" else ''
+        return "Please make sure that you saved all your work#{message}."
+

   setDragInAction:(@dragInAction = no)->
-    $('body')[if @dragInAction then "addClass" else "removeClass"] "dragInAction"
+
+    document.body.classList[if @dragInAction then 'add' else 'remove'] 'dragInAction'
+

   setMainView:(@mainView)->


add cardType to propTypes

add cardType to propTypes

Without the property the classList that's set on body starts with dash

--- src/components/pinned-header-footer-card/index.cjsx
+++ src/components/pinned-header-footer-card/index.cjsx
@@ -10,6 +10,7 @@
 module.exports = React.createClass
   displayName: 'PinnedHeaderFooterCard'
   propTypes:
+    cardType: React.PropTypes.string.isRequired
     buffer: React.PropTypes.number
     scrollSpeedBuffer: React.PropTypes.number
     forceShy: React.PropTypes.bool


ability to massage a string rather than stream and write it to an output stream

--- lib/massage.coffee
+++ lib/massage.coffee
@@ -99,6 +99,10 @@
     stream_in.on "data", (chunk) ->
       stream_out.write self.transform(chunk), encoding

+  write: (data, stream_out) ->
+    stream_out.write @transform data
+    @
+
   constructor: (@sequence = [], immediate, massagists) ->
     this.transform(immediate, massagists) if immediate?


Fix: enable new post form after successfully creating post

--- assets/javascripts/views/post/002_new_post_form.js.coffee
+++ assets/javascripts/views/post/002_new_post_form.js.coffee
@@ -75,6 +75,7 @@
         @showErrors([{ text: "Error: #{JSON.parse(xhr.responseText)?.error}" }])

       success: (post, xhr) =>
+        @enable()
         @render()
         @hide?()
     )


DE16286: Kanban: Set correct state and do not schedule on add new with details

--- kanban/KanbanAppSpec.coffee
+++ kanban/KanbanAppSpec.coffee
@@ -54,7 +54,23 @@
     it 'shows add new for user who is a project editor', ->
       @createApp().then =>
         expect(@app.down 'rallyaddnew').toBeDefined()
-
+
+    it 'should not schedule a new item in an iteration', ->
+      @createApp().then =>
+        editorOpenedStub = @stub(Rally.nav.Manager, 'create')
+        addNewHelper = new Helpers.AddNewHelper '.kanban'
+        addNewHelper.addWithDetails('foo').then =>
+          sinon.assert.calledOnce editorOpenedStub
+          expect(editorOpenedStub.getCall(0).args[1].iteration).toBe 'u'
+
+    it 'should set group by field to first column value', ->
+      @createApp().then =>
+        editorOpenedStub = @stub(Rally.nav.Manager, 'create')
+        addNewHelper = new Helpers.AddNewHelper '.kanban'
+        addNewHelper.addWithDetails('foo').then =>
+          sinon.assert.calledOnce editorOpenedStub
+          expect(editorOpenedStub.getCall(0).args[1]['c_' + @app.getSetting('groupByField')]).toBe @app.cardboard.getColumns()[0].getValue()
+
     it 'should show correct fields on cards', ->
       @createApp({cardFields: 'Name,Defects,Project'}).then =>


query param to hide modal

query param to hide modal

--- apps/home/client/index.coffee
+++ apps/home/client/index.coffee
@@ -44,6 +44,6 @@

   # Open the signup modal for logged out users, or the login modal if the user has
   # signed in before.
-  return if user?
+  return if user? or location.search.match('no-auth-modal')
   _.defer -> mediator.trigger 'open:auth',
     mode: if readCookie('signed_in') is 'true' then 'login' else 'signup'


chore (Test) Fail early if oauthTokenPath does not appear valid

--- spec/devgyver_oauth_token_path.coffee
+++ spec/devgyver_oauth_token_path.coffee
@@ -1,7 +1,25 @@
 path = require 'path'
+fs = require 'fs'

 module.exports = oauthTokenPath = path.join(
   (process.env.HOME || process.env.USERPROFILE)
   '.appgyver'
   'devgyver.token.json'
 )
+
+if !fs.existsSync oauthTokenPath
+  console.log """
+    Cannot run tests with oauthTokenPath set to #{oauthTokenPath}:
+    Path does not exist.
+  """
+  process.exit -1
+
+try
+  JSON.parse fs.readFileSync oauthTokenPath
+catch e
+  console.log """
+    Cannot run tests with oauthTokenPath set to #{oauthTokenPath}:
+    '#{e.message}'
+    Contents do not appear to be well-formed JSON.
+  """
+  process.exit -1


Add keys to entries

Add keys to entries

--- src/scripts/components/board.cjsx
+++ src/scripts/components/board.cjsx
@@ -12,6 +12,7 @@
     data[row].push {color: '#ffffff', isColored: false}

 board = React.createClass
+
   data: data

   getRandomColor: ->
@@ -45,10 +46,10 @@
   render: ->

       {for row, rowIndex in data
-

+

           {for column, columnIndex in row
-

-              <tile row={rowIndex} column={columnIndex} data={column} onUpdate={@updateBoard} />
+

+              <tile key={'tile' + rowIndex + ':' + columnIndex} row={rowIndex} column={columnIndex} data={column} onUpdate={@updateBoard} />

           }


Added some german swear words to the demoliton-man script.

--- src/scripts/demolition-man.coffee
+++ src/scripts/demolition-man.coffee
@@ -10,11 +10,13 @@
 # Commands:
 #
 # Author:
-#   whitman
+#   whitman, jan0sch

 module.exports = (robot) ->

   words = [
+    'arsch',
+    'arschloch',
     'arse',
     'ass',
     'bastard',
@@ -26,15 +28,22 @@
     'cunt',
     'damn',
     'damnit',
+    'depp',
     'dick',
     'douche',
     'fag',
+    'fotze',
     'fuck',
     'fucked',
     'fucking',
+    'kacke',
     'piss',
+    'pisse',
+    'scheisse',
+    'schlampe',
     'shit',
-    'wank'
+    'wank',
+    'wichser'
   ]
   regex = new RegExp('(?:^|\\s)(' + words.join('|') + ')(?:\\s|\\.|\\?|!|$)', 'i');


use setTooltip for summary msgs

use setTooltip for summary msgs

--- lib/views/blame-line-view.coffee
+++ lib/views/blame-line-view.coffee
@@ -1,4 +1,4 @@
-{React, Reactionary} = require 'atom'
+{$, React, Reactionary} = require 'atom'
 {div, span, a} = Reactionary
 RemoteRevision = require '../util/RemoteRevision'

@@ -14,12 +14,22 @@
         span className: 'committer', 'Nobody'
     else
       url = RemoteRevision.create(@props.hash, @props.url).url()
-      div className: 'blame-line ' + @props.backgroundClass, title: @props.summary,
+      div className: 'blame-line ' + @props.backgroundClass,
         a className: 'hash', href: url,
           @props.hash.substring(0, HASH_LENGTH)
         span className: 'date', @props.date
         span className: 'committer text-highlight',
           @props.committer.split(' ').slice(-1)[0]

+  componentDidMount: ->
+    $el = $(@getDOMNode())
+    if @props.summary
+      $el.setTooltip
+        title: @props.summary
+        placement: "auto left"
+
+  componentWillUnmount: ->
+    $(@getDOMNode()).tooltip "destroy"
+
   shouldComponentUpdate: ->
     false


Test client unfinished request

Test client unfinished request

--- test/test_client.coffee
+++ test/test_client.coffee
@@ -232,3 +232,29 @@
     request = client.request 'GET', '/', {}
     test.ok request
     request.end()
+
+exports.testClientUnfinishedRequest = (test) ->
+  test.expect 3
+
+  sockPath = "/tmp/nack.test.sock"
+
+  worker = net.Server (conn) ->
+    worker.close()
+
+    conn.on 'data', () ->
+      conn.end()
+
+  worker.listen sockPath, () ->
+    client = createConnection sockPath
+
+    client.on 'close', ->
+      test.done()
+
+    client.on 'error', (exception) ->
+      test.ok exception
+
+    test.ok client
+
+    request = client.request 'GET', '/', {}
+    test.ok request
+    request.end()


Check object attribute = 0 when generating GPV

--- lib/session.coffee
+++ lib/session.coffee
@@ -965,7 +965,9 @@
     while (path = iter.next().value) and
         parameterNames.length < GPV_BATCH_SIZE
       syncState.refreshAttributes.value.delete(path)
-      if sessionContext.deviceData.attributes.has(path)
+      # Need to check in case param is deleted or changed to object
+      attrs = sessionContext.deviceData.attributes.get(path)
+      if attrs and attrs.object and attrs.object[1] == 0
         parameterNames.push(path)

     if parameterNames.length