Fix bug preventing ecobee thermostat updates

--- lib/adapters/ecobee/EcobeeAdapter.coffee
+++ lib/adapters/ecobee/EcobeeAdapter.coffee
@@ -32,12 +32,12 @@
     @_api.listThermostats()
       .then (thermostats) =>
         for id, thermostat of thermostats
-          node = @children.get(id)
-          unless node?
+          tnode = @children.get(id)
+          unless tnode?
             @log 'verbose', "Creating thermostat ID #{id}"
             tnode = @children.add(
               new EcobeeThermostatNode({id: id}, {adapter: this}))
-            tnode.processData thermostat
+          tnode.processData thermostat
           for sensor in thermostat.remoteSensors
             snode = @children.get(sensor.id)
             unless snode?


Don't dump symbols on Windows CI for now

Currently errors and isn't uploaded yet anyway as a release asset

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -225,8 +225,13 @@
   grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
   grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
   grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
-  grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'dump-symbols', 'set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build'])
   grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
+
+
+  ciTasks = ['output-disk-space', 'download-atom-shell', 'build']
+  ciTasks.push('dump-symbols') unless process.platform is 'win32'
+  ciTasks.push('set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build')
+  grunt.registerTask('ci', ciTasks)

   defaultTasks = ['download-atom-shell', 'build', 'set-version']
   defaultTasks.push 'install' unless process.platform is 'linux'


Reenable integration tests.

Reenable integration tests.

--- src/spec/import.spec.coffee
+++ src/spec/import.spec.coffee
@@ -3,9 +3,9 @@
 Q = require 'q'
 Config = require '../config'

-jasmine.getEnv().defaultTimeoutInterval = 20000
+jasmine.getEnv().defaultTimeoutInterval = 60000

-xdescribe 'Import', ->
+describe 'Import', ->
   beforeEach (done) ->
     @import = new Import Config
     @rest = @import.validator.rest


Fix spec

Fix spec

--- src/test/test_config.coffee
+++ src/test/test_config.coffee
@@ -26,8 +26,8 @@
     t.end()

   test 'config game', (t) ->
-    context = world.find_path('.game')
-    t.ok context, "game"
+    game = world.find_path('.game')
+    t.ok game, "game"
     t.end()

   test 'config program', (t) ->


pretify setId

pretify setId

--- lib/app.coffee
+++ lib/app.coffee
@@ -15,12 +15,12 @@

   setId: (preId=@src) ->
     preId=preId.replace /:/g,'_'
-    preId=preId.replace /\//g,'-'
-    preId=preId.replace /&/g,'-'
-    preId=preId.replace /\?/g,'_'
-    preId=preId.replace /\=/g,'_'
-    preId=preId.replace /\./g,'-'
-    preId=preId.replace /%/g,'-'
+    .replace /\//g,'-'
+    .replace /&/g,'-'
+    .replace /\?/g,'_'
+    .replace /\=/g,'_'
+    .replace /\./g,'-'
+    .replace /%/g,'-'
     @id = "app_#{preId}"

   initialize: ->


Content for replaces and no longer appends.

--- src/view/dom/readers.coffee
+++ src/view/dom/readers.coffee
@@ -87,6 +87,10 @@
     {node, keyPath, view} = definition

     contentView = new Batman.View(html: node.innerHTML, contentFor: keyPath)
+    contentView.addToParentNode = (parentNode) ->
+      parentNode.innerHTML = ''
+      parentNode.appendChild(@get('node'))
+
     view.subviews.add(contentView)

     skipChildren: true


remove console.log and correct watch config, fixes #85

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -943,14 +943,14 @@

     # can add back a command line option for cluster in the future
     if false # if cluster.isMaster
-      console.log "master #{process.pid}: running"
+      log "master #{process.pid}: running"
       for cpu, index in os.cpus()
         cluster.fork {CLUSTER_INDEX: index}
       cluster.on 'exit', (worker, code, signal) ->
-        console.log "worker #{worker.process.pid}: died. restart..."
+        log "worker #{worker.process.pid}: died. restart..."
         cluster.fork()
     else
-      console.log "worker #{process.pid}: running"
+      log "worker #{process.pid}: running"

       # dependencies
       connect = require 'connect'
@@ -969,7 +969,7 @@
         firebase: config.firebase or null
         mongodb: config.mongo or null
         pkg_dir: path.join workspace, 'pkg'
-        watch: process.env.CLUSTER_INDEX is '0'
+        watch: process.env.CLUSTER_INDEX in ['0', undefined]
       }
       app.use app.router
       app.use express.errorHandler {dumpExceptions: true, showStack: true}


more updates to shell

more updates to shell

--- lib/lt3.coffee
+++ lib/lt3.coffee
@@ -59,13 +59,11 @@
   HELPER_COMMANDS
   get:sites [<slug>]              get sites with shell
   get:allsites                    returns a list of id_slug mappings for all sites
-  get:objects_c <collection>      get objects with collection
-  get:objects_t <type>            get objects with type
   get:sitemap <slug>              get contents of site
   get:site_ids <slug>..           returns a id to slug lookup
   help/docs                       this document
-  open:site <id>                  this opens site in context
-  open:object_or_page <id>        this opens object in context
+  shell:site <id>                  this opens site in context
+  shell:object_or_page <id>        this opens object in context

   DB_COMMANDS                     Query the database with results
   db.sites.find
@@ -77,42 +75,50 @@
   db.objects.findById
 """

-USAGE_SHELL_SITE="""
+USAGE_SHELL_CONTEXT="""
   SITE/OBJECT_TOOLS
   show                            Dump site information
   add/set                         Sets data key:value
   get                             gets a subset of the site information
   exit                            drops out of site context
   """
-executeSiteCommand =(l_token,l_command,l_args) =>
+executeContextCommand =(l_token,l_command,l_args) =>
   @variables="" unless @variables
   switch l_command
     when 'show'
       getDB (db) =>
         db.cache = false
-        db.get('sites').find {_id: @site_id}, (err, sites) =>
+        db.get(@shell_context.type).find {_id: @shell_context.id}, (err, sites) =>
           log JSON.stringify(sites[0].val(),null,3)
+          rl.prompt();

     when 'get'
       getDB (db) =>
-        db.get('sites').find {_id: @site_id}, (err, sites) =>
+        db.get(@shell_context.type).find {_id: @shell_context.id}, (err, sites) =>
           log JSON.stringify(sites[0].get(l_args[0]).val(),null,3)
+          rl.prompt();

     when 'set' or 'add' #currently does not work
       getDB (db) =>
-        db.get('sites').findOne {_id: @site_id}, (err, site) =>
+        db.get(shell_context.type).findOne {_id: @shell_context.id}, (err, site) =>
           throw err if err
           site.get(arg[0]).set arg[1], (err) =>
             throw err if err
+            rl.prompt();
+    when 'help'
+      log USAGE_SHELL_CONTEXT
+      rl.prompt()
+
     when 'exit'
-      @site_context = undefined
-
+      @shell_context = undefined
+      rl.setPrompt('>')
+      rl.prompt()
     else
       rl.prompt()

 executeCommand = (l_token,l_command,l_args) =>
-  if @site_context
-    executeSiteCommand l_token,l_command,l_args
+  if @shell_context
+    executeContextCommand l_token,l_command,l_args
     return

   switch l_command
@@ -156,7 +162,7 @@
          for site in sites
             log "_id: #{site.data._id}, slug:#{site.data.slug}"
          rl.prompt();
-
+    ### May be added later still tbd on implementation
     when 'get:objects_c'
       getDB (db) ->
         db.get('objects').find {collection: l_args[0]}, (err, objects) ->
@@ -174,35 +180,31 @@
           log JSON.stringify(object.val(), null, 4)
           log "------End Object------"
         rl.prompt();
-
-
-    when 'open:site'
+    ###
+    when 'shell:site'
       getDB (db) =>
         db.get('sites').find {_id: l_args[0]}, (err, sites) =>
-          @site_context={
+          @shell_context={
             type:"sites"
-            site:sites[0]
             id:sites[0].data._id
-            prompt:"#{l_args[0]}"
           }
-
-          rl.setPrompt "#{@prompt}>"
+          prompt="#{sites[0].data.slug}-#{id}"
+          rl.setPrompt "#{prompt}>"
           rl.prompt();
-    when 'open:object'
+    when 'shell:object'
       getDB (db) =>
-        db.get('objects').find {_id: l_args[0]}, (err, objects) =>
-          @site_context={
+        db.get('objects').findOne {_id: l_args[0]}, (err, object) =>
+          @shell_context={
             type:"objects"
-            site:objects[0]
-            id:objects[0].data._id
-            prompt:"#{l_args[0]}"
+            id:object.data._id
           }
-
-          rl.setPrompt "#{@prompt}>"
+          prompt="#collection-#{object.data.collection} type-#{object.data.type}"
+          rl.setPrompt "#{prompt}>"
           rl.prompt();

     when 'help' or 'docs'
       log USAGE_SHELL
+      rl.prompt()

     else
       checkForDb=l_token.match(/db.+(?=\()/)+""


Removed unused pie library

Removed unused pie library

--- lib/pie.coffee
+++ lib/pie.coffee
@@ -1,99 +0,0 @@
-express = require 'express'
-
-# Defines an HTTP API implemented with Express
-class HttpApi
-  constructor: (@port) ->
-    throw new Error('must provide port') unless @port?
-
-  # Enumerate all endpoints here as HttpApiEndpoint object instances
-  endpoints: -> []
-
-  init: (@api) ->
-    throw new Error('must provide internal api') unless @api?
-    console.log "[HTTP] initializing express"
-
-    @app = express()
-    @app.use(express.logger())
-    @app.use(express.compress())
-    @app.use(express.json())
-    @app.use(express.urlencoded())
-    @app.use(express.methodOverride())
-    @app.use(@app.router)
-
-    for [route, method, handler] in @endpoints()
-      console.log "[HTTP] adding endpoint: #{method.toUpperCase()} #{route}"
-      endpoint = new HttpApiEndpoint(handler)
-      @app[method.toLowerCase()](route, endpoint.getRouteHandler(@api))
-
-    @app.listen(@port)
-    console.log "[HTTP] listening on port #{@port}"
-    @
-
-# Defines an individual endpoint in an API
-class HttpApiEndpoint
-  constructor: (@handler) ->
-    throw new Error('must provide a handler') unless @handler?
-
-  getRouteHandler: (@api) ->
-    throw new Error('must provide internal api') unless @api?
-    return (req, res) =>
-      @handler.call(@, req, res)
-
-  parseObjectId: (req, urlPrefix) ->
-    req.url.replace("/#{urlPrefix}/", '')
-
-  # Creates an object suitable for use with paged UIs
-  createDataPage: (dataArray, offset = 0, limit = -1) ->
-    return {
-      totalCount: dataArray.length
-      count: dataArray.length
-      offset: offset
-      limit: limit
-      objects: dataArray
-    }
-
-  listAny: (res, promise, options, packageData, emptyResult) ->
-    promise
-    .then((results) ->
-      if results?.length
-        console.log "[HTTP] 200: found #{results.length} #{options.typeName}"
-        res.json(200, packageData(results))
-      else
-        respCode = if options.allowEmpty then 200 else 404
-        if options.query
-          console.log "[HTTP] #{respCode}: no #{options.typeName} found for query:", options.query
-        else
-          console.log "[HTTP] #{respCode}: no #{options.typeName} found"
-        res.json(respCode, emptyResult)
-    )
-    .fail((err) =>
-      @handleApiError(res, err, errorMsg: "failed to list #{options.typeName}")
-    )
-    .done()
-
-  handleApiError: (res, err, options) ->
-    errorMsg = if options.errorMsg then "#{options.errorMsg}." else ''
-    errorCode = options.errorCode or 500
-    console.error("[HTTP] #{errorCode}: #{errorMsg}")
-    console.error(err.stack)
-    res.json(errorCode, { error: errorMsg })
-
-  listSingle: (res, promise, options) ->
-    packageData = (results) -> results[0]
-    @listAny(res, promise, options, packageData, {})
-
-  listMultiple: (res, promise, options) ->
-    packageData = (results) => @createDataPage(results)
-    options.allowEmpty = true
-    @listAny(res, promise, options, packageData, [])
-
-  deleteSingle: (res, promise, options) ->
-    promise
-    .then(->
-      res.json(200, {})
-    )
-    .fail((err) =>
-      @handleApiError(res, err, errorMsg: "failed to delete #{options.typeName}")
-    )
-
-module.exports = {HttpApi, HttpApiEndpoint}


Tidy up messages

Tidy up messages

--- src/scripts/singularity.coffee
+++ src/scripts/singularity.coffee
@@ -8,6 +8,7 @@
 #   None
 #
 # Commands:
+#   hubot singularity?
 #   hubot singularity show aliases
 #   hubot singularity add alias <alias> <url>
 #   hubot singularity clear alias <alias>
@@ -63,14 +64,20 @@
         data = null
         try
           requests = JSON.parse body
-          for request in requests
-            msg.send "I found request '#{request.request.id} (#{request.request.requestType} - #{request.state})' for '#{alias}'"
+          if requests.length > 0
+            msgRequests = requests.map((i) -> "  #{i.request.id} (#{i.request.requestType} - #{i.state})")
+                                  .reduce(((t, s) -> t + "\n" + s), "")
+
+            msg.send "I found following requests on #{alias}" + msgRequests
+          else
+            msg.send "No requests found on #{alias}"
         catch error
           msg.send "Ran into an error parsing JSON :"
           msg.send error

   showCommands = (msg) ->
     msg.send "Singularity commands:"
+    msg.send "hubot singularity?"
     msg.send "hubot singularity show aliases"
     msg.send "hubot singularity add alias <alias> <url>"
     msg.send "hubot singularity clear alias <alias>"


Finish write method and add form submit

Finish write method and add form submit

--- js/gpg_form_encryptor.coffee
+++ js/gpg_form_encryptor.coffee
@@ -21,19 +21,16 @@
   single_defer = $.Deferred()
   group_defer = $.Deferred()
   single_encryption(form, values, (result) ->
-    console.log(result)
     single_defer.resolve()
   )
   $.when(single_defer).done(()->
     group_encryption(form, values, (result) ->
-      console.log(result)
       group_defer.resolve()
     )
   )
   $.when(single_defer, group_defer).done(() ->
-    #console.log values
-    #reinsert_values values
-    #form.submit()
+    reinsert_values values
+    form.submit()
   )

   #  source_encryption_names = find_names_for(form, "*[data-encrypt-source]")
@@ -68,7 +65,6 @@
       defers.push d
       encrypt(element.value, (result_string)->
         element.value = result_string.toString()
-        console.log "#{element.name} finished encryption"
         d.resolve()
       )

@@ -94,8 +90,7 @@
   buffer = collect_sources(elements)
   encryption_source_names = find_names_for(form, elements)
   wipe_source_fields(values, encryption_source_names)
-  write_target(buffer, values)
-  callback(values)
+  write_target(buffer, values, callback)

 collect_sources = (elements)->
   buffer = ""
@@ -120,13 +115,14 @@
     if contains(element.name, encryption_source_names)
       element.value = ""

-write_target = (buffer, data)->
+write_target = (buffer, data, callback)->
   target_name = $("*[data-encrypt-target]").attr("name")
-  if target_name[0]
+  if target_name
     encrypt(buffer, (result_string) ->
-      data[0].value =result_string.toString()
-      #data.push {name: target_name, value: result_string.toString()}
-      console.log data
+      for element in data
+        if element.name == target_name
+          element.value = result_string.toString()
+      callback(data)
     )
   else
     console.error("No target defined")


Fixes binding issue of scrollTop method

Fixes binding issue of scrollTop method

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -140,7 +140,7 @@
   scrollLeft: (left) =>
     @miniScrollView.scrollLeft(left * scaleX)

-  scrollTop: (top) ->
+  scrollTop: (top) =>
     minimapHeight = @miniScrollView.outerHeight()
     scrollViewHeight = @scrollView.outerHeight()
     scrollViewOffset = @scrollView.offset().top


:bomb: [Experimental] don't flash on single change

I’m getting dough it’s really helpful


Doc remote

Doc remote

--- lib/rails/remote.coffee
+++ lib/rails/remote.coffee
@@ -1,21 +1,34 @@
-# Implements data-remote forms and links
+# Implements `data-remote` for forms and links.
 #
-# Compatible with most of the UJS options Rails 3 provides.
+#     Toggle
 #
-# Fork/rewrite of jquery-ujs rails adapter. Does alot less and may
-# contain some GitHub specific conventions. Also full of Coffee Love.
-# https://github.com/rails/jquery-ujs/blob/master/src/rails.js
+#     <form action="/comment" data-remote></form>
 #
-# jQuery UJS provides custom ajax:* methods to hook into. However,
-# jQuery already provides most of these global ajax events, so we'll
-# use those instead. There is no global version of `beforeSend`, so
-# `ajaxBeforeSend` is added to complement it.
+# Use delegated jQuery's global AJAX events to handle successful
+# and error states.
+#
+#     $(document).on 'ajaxBeforeSend', '.new-comment', ->
+#        $(this).addClass 'loading'
+#
+#     $(document).on 'ajaxComplete', '.new-comment', ->
+#        $(this).removeClass 'loading'
+#
+#     $(document).on 'ajaxSuccess', '.new-comment', (e, xhr, opts, data) ->
+#       $('.comments).append data.commentHTML
+#
+#     $(document).on 'ajaxError', '.new-comment', ->
+#       alert "Something went wrong!"
+#
+
+# jQuery already provides a handful of global AJAX events. However,
+# there is no global version of `beforeSend`, so `ajaxBeforeSend` is
+# added to complement it.
+#
 # Reference: http://docs.jquery.com/Ajax_Events
-
 beforeSend = (element) -> (xhr, settings) ->
-  # Don't like that this needs to be restated, but overriding
-  # beforeSend clears our global configuration in application.js
-  if settings.dataType is undefined
+  # It'd be nice if we could set this globally once with `ajaxSetup`.
+  # But it gets overriden too easy. Dunno.
+  unless settings.dataType
     xhr.setRequestHeader 'Accept', '*/*;q=0.5, ' + settings.accepts.script

   # Provide a global version of the `beforeSend` callback


run: fix params.end

run: fix params.end

--- lib/run.coffee
+++ lib/run.coffee
@@ -105,7 +105,7 @@
         .then (err) ->
           if err
           then ctx.emit 'error', err
-          else ctx.emit 'end'
+          else ctx.emit 'end' if params.end
           next err
     .then (err) =>
       if err


run: remove comments

run: remove comments

--- lib/run.coffee
+++ lib/run.coffee
@@ -48,8 +48,6 @@
         module.configure.call ctx, ctx if module.configure
       # Filter by hosts
       return next() if params.hosts? and multimatch(host, params.hosts).indexOf(host) is -1
-      # console.log ctx.config.ryba
-      # return next()
       ctx.tree.middlewares params, (err, middlewares) =>
         return next() unless middlewares?
         middlewareRun = each(middlewares)


masson: increase default wait between retry to 5s

--- lib/run.coffee
+++ lib/run.coffee
@@ -50,7 +50,7 @@
         .run (middleware, next) =>
           ctx.middleware = middleware
           retry = if middleware.retry? then middleware.retry else 2
-          middleware.wait ?= 1000
+          middleware.wait ?= 5000
           attempts = 0
           disregard_done = false
           if middleware.skip


Always remove placeholder

Always remove placeholder


Loosen selector to class names to allow integration

`[is=tree-view-file]` enables only the custom element that registered as
`tree-view-file` to integrate with this package's preview tabs feature.
Using `.file-tree .entry.file` allows other file tree implementations to
match the selector without having to commandeer the globally-unique
"tree-view-file" custom element name.

Fixes #208


make show animation more reliable

make show animation more reliable

--- lib/util/tooltip.coffee
+++ lib/util/tooltip.coffee
@@ -41,7 +41,7 @@

   show: ->
     @view.style.display = 'block'
-    @view.classList.remove 'dontshow'
+    setTimeout ( => @view.classList.remove 'dontshow'), 20

   destroy: ->
     document.body.removeChild @view


Add test for ternary operator case

Add test for ternary operator case

--- spec/react-grammar-spec.coffee
+++ spec/react-grammar-spec.coffee
@@ -228,6 +228,21 @@
     expect(tokens[4]).toEqual value: '</', scopes: ["source.js.jsx","tag.closed.js","punctuation.definition.tag.begin.js"]
     expect(tokens[5]).toEqual value: 'tag', scopes: ["source.js.jsx","tag.closed.js","entity.name.tag.js"]
     expect(tokens[6]).toEqual value: '>', scopes: ["source.js.jsx","tag.closed.js","punctuation.definition.tag.end.js"]
+
+  it "tokenizes ternary operator inside jsx code section", ->
+    {tokens} = grammar.tokenizeLine('{x?<tag></tag>:null}')
+    expect(tokens[0]).toEqual value: '{', scopes: ["source.js.jsx","meta.brace.curly.js"]
+    expect(tokens[1]).toEqual value: 'x', scopes: ["source.js.jsx"]
+    expect(tokens[2]).toEqual value: '?', scopes: ["source.js.jsx", "keyword.operator.ternary.js"]
+    expect(tokens[3]).toEqual value: '<', scopes: ["source.js.jsx","tag.open.js","punctuation.definition.tag.begin.js"]
+    expect(tokens[4]).toEqual value: 'tag', scopes: ["source.js.jsx","tag.open.js","entity.name.tag.js"]
+    expect(tokens[5]).toEqual value: '>', scopes: ["source.js.jsx","tag.open.js","punctuation.definition.tag.end.js"]
+    expect(tokens[6]).toEqual value: '</', scopes: ["source.js.jsx","tag.closed.js","punctuation.definition.tag.begin.js"]
+    expect(tokens[7]).toEqual value: 'tag', scopes: ["source.js.jsx","tag.closed.js","entity.name.tag.js"]
+    expect(tokens[8]).toEqual value: '>', scopes: ["source.js.jsx","tag.closed.js","punctuation.definition.tag.end.js"]
+    expect(tokens[9]).toEqual value: ':', scopes: ["source.js.jsx","keyword.operator.ternary.js"]
+    expect(tokens[10]).toEqual value: 'null', scopes: ["source.js.jsx","constant.language.null.js"]
+    expect(tokens[11]).toEqual value: '}', scopes: ["source.js.jsx","meta.brace.curly.js"]

     #{tokens} = grammar.tokenizeLine('<tag>\'foo</tag>')


removed debugger statement

removed debugger statement


implemented view support for tooltips in KDView, with spatial orientation depending on available space. needs some more fixes/ironing out edge cases.

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -688,6 +688,7 @@
     o.placement or= "above"
     o.offset    or= 0
     o.delayIn   or= 300
+    o.delayOut  or= 300
     o.html      or= yes
     o.animate   or= no
     o.opacity   or= 0.9
@@ -696,11 +697,107 @@
     o.gravity   or= placementMap[o.placement]
     o.fade      or= o.animate
     o.fallback  or= o.title
+    o.view      or= null

     @on "viewAppended", =>
       # log "get rid of this timeout there should be an event after template update"
       @utils.wait =>
-        @$(o.selector)[o.engine] o
+
+        # tooltips that are just strings will be handled by t(w)ipsy
+        unless o.view
+          @$(o.selector)[o.engine] o
+        else
+
+          # tooltips that provide a view will add their view + container
+          # to the dom on first hover and reuse that view from then on
+
+          # it will only be added to the DOM once
+          @isFirstTooltip = yes
+
+          @$(o.selector).hover =>
+
+            # bridge variables for the mouse movement from the selector
+            # towards and onto the tooltip
+            @hasLeftSelector = no
+            @hasMovedToTooltip = no
+
+            # preparation is either adding to DOM or just reusing the
+            # prepared view
+            @prepareTooltip o, @isFirstTooltip, =>
+
+              # after adding to DOM, prevent further additions of the same
+              # view
+              @isFirstTooltip = no
+          ,=>
+            @hasLeftSelector = yes
+
+            # give the user some time to focus the tooltip
+            setTimeout =>
+              if not @hasMovedToTooltip
+                if o.fade
+                  @tooltipViewContainer?.$().fade o.delayOut, =>
+                    @tooltipViewContainer?.hide()
+                else
+                  @tooltipViewContainer?.hide()
+            ,500
+
+  prepareTooltip:(o = {}, isFirstTooltip = no, callback = noop)->
+
+    if isFirstTooltip
+      @tooltipViewContainer = new KDView
+        cssClass : 'tooltip-container hidden'
+      @tooltipWrapper = new KDView
+        cssClass : 'tooltip-wrapper'
+      @tooltipArrow = new KDView
+        cssClass : 'tooltip-arrow'
+
+      @tooltipWrapper.addSubView o.view
+      @tooltipViewContainer.addSubView @tooltipWrapper
+      @tooltipViewContainer.addSubView @tooltipArrow
+      @getSingleton('mainView').addSubView @tooltipViewContainer
+
+    callback() # now that the view is in the DOM, prevent further insertion
+
+    setTimeout =>
+
+      # prevent instant popup when just moving/scrolling
+      unless @hasLeftSelector
+        @tooltipViewContainer.show()
+
+        # measure the distance for proper placement
+        container = @tooltipViewContainer.$()
+        containerHeight = container.height()
+        containerWidth = container.width()
+        selector = @$(o.selector)
+        selectorOffset = selector.offset()
+        selectorHeight = selector.height()
+        selectorWidth = selector.width()
+
+
+        # vertical placement defaults to above, so only paint below the
+        # selector if specifically demanded or if there is not enough space
+        if o.placement is 'below' or (o.placement is 'above' and selectorOffset.top-selectorHeight-containerHeight < 0)
+          @tooltipViewContainer.setClass 'painted-below'
+          container.css top : selectorOffset.top+selectorHeight+10
+        else
+          @tooltipViewContainer.setClass 'painted-above'
+          container.css top : selectorOffset.top-selectorHeight-containerHeight
+
+        # horizontal placement defaults to right, will only paint left if
+        # there is enough space for it.
+        if o.placement is 'left' or ( selectorOffset.left+containerWidth > screen.width)
+          @tooltipViewContainer.setClass 'painted-left'
+          container.css left : selectorOffset.left-containerWidth+selectorWidth
+        else
+          @tooltipViewContainer.setClass 'painted-right'
+          container.css left : selectorOffset.left
+
+        @tooltipViewContainer.$().hover =>
+          @hasMovedToTooltip = yes
+        ,=>
+          @tooltipViewContainer.hide()
+    ,o.delayIn
+

   getTooltip:(o = {})->
     o.selector or= null


cmd+option/ctrl click allowed for master

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -86,19 +86,19 @@
     @setInstanceVariables options
     @defaultInit options,data

-    if location.hostname is "localhost"
-      @listenTo
-        KDEventTypes        : "click"
-        listenedToInstance  : @
-        callback            : (publishingInstance, event)=>
-          if event.metaKey and event.altKey and event.ctrlKey
-            log @getData()
-            event.stopPropagation?()
-            event.preventDefault?()
-            return false
-          else if event.altKey and (event.metaKey or event.ctrlKey)
-            log @
-            return false
+    # if location.hostname is "localhost"
+    @listenTo
+      KDEventTypes        : "click"
+      listenedToInstance  : @
+      callback            : (publishingInstance, event)=>
+        if event.metaKey and event.altKey and event.ctrlKey
+          log @getData()
+          event.stopPropagation?()
+          event.preventDefault?()
+          return false
+        else if event.altKey and (event.metaKey or event.ctrlKey)
+          log @
+          return false

     @on 'childAppended', @childAppended.bind @


remove logs

remove logs

--- public/js/coffee/top.coffee
+++ public/js/coffee/top.coffee
@@ -6,7 +6,6 @@

     update: (list) =>
         context = list
-        console.log(context)
         $(@sel).html(@template(context))

 )


async identity access bugfix

async identity access bugfix

--- lib/data/PubSubContext.coffee
+++ lib/data/PubSubContext.coffee
@@ -50,7 +50,6 @@
                     if topic.match handler.topic
                         threads.push (fn)=>
                             try
-                                console.log data, @identity
                                 handler null,
                                     origin: origin
                                     data: data


Make logging optional

Make logging optional

--- src/livereload.coffee
+++ src/livereload.coffee
@@ -11,7 +11,7 @@
     @pluginIdentifiers = {}

     # i can haz console?
-    @console = if @window.console && @window.console.log && @window.console.error
+    @console = if @window.location.href.match(/LR-verbose/) && @window.console && @window.console.log && @window.console.error
       @window.console
     else
       log:   ->


Bugfix in date formatter

Bugfix in date formatter

--- client/lib/date.coffee
+++ client/lib/date.coffee
@@ -1,6 +1,6 @@
 @formatDate = (date, prefix = 'on ') ->
   return '???' unless date?
-  return "?#{date}?" unless date instanceOf Date  ## have seen this briefly, not sure when
+  return "?#{date}?" unless date instanceof Date  ## have seen this briefly, not sure when
   now = new Date()
   options =
     weekday: 'long'


removed duplicate call

removed duplicate call

--- app/assets/javascripts/app/controllers/chat_widget.js.coffee
+++ app/assets/javascripts/app/controllers/chat_widget.js.coffee
@@ -139,7 +139,6 @@
   hide: =>
     @isShown = false
     @el.find('#chat_content').hide(100)
-    @el.find('#chat_content').hide(100)
     @el.find('#chat_toogle').html('♦');

   focusIn: =>


Fixes #4007 - Report profile "new profile" button does not match coloring scheme.

--- app/assets/javascripts/app/controllers/report_profile.coffee
+++ app/assets/javascripts/app/controllers/report_profile.coffee
@@ -22,7 +22,7 @@
 #          'Report Profile are ...'
         ]
         buttons: [
-          { name: __('New Profile'), 'data-type': 'new', class: 'primary' }
+          { name: __('New Profile'), 'data-type': 'new', class: 'btn--success' }
         ]
       container: @el.closest('.content')
       veryLarge: true


[Backport] refresh user info on init project creation

--- app/modules/projects/create/create-project.controller.coffee
+++ app/modules/projects/create/create-project.controller.coffee
@@ -22,13 +22,16 @@
         "tgAppMetaService",
         "$translate",
         "tgProjectService",
-        "$location"
+        "$location",
+        "$tgAuth"
     ]

-    constructor: (@appMetaService, @translate, @projectService, @location) ->
+    constructor: (@appMetaService, @translate, @projectService, @location, @authService) ->
         taiga.defineImmutableProperty @, "project", () => return @projectService.project

         @appMetaService.setfn @._setMeta.bind(this)
+
+        @authService.refresh()

         @.displayScrumDesc = false
         @.displayKanbanDesc = false


all arithmetic tests passing

all arithmetic tests passing

--- src/macro-tool.coffee
+++ src/macro-tool.coffee
@@ -2,7 +2,9 @@
 # parses an aperture macro and then returns the pad when the tool is defined

 # uses the standard tool functions
-standard = require '../src/standard-tool'
+standard = require './standard-tool'
+# calculator parsing for macro arithmetic
+calc = require './macro-calc'

 # aperture macro list
 macros = {}
@@ -86,16 +88,20 @@
     pad = ''

   getNumber: (s) ->
-    result = NaN
     # normal number all by itself
-    if s.match /^[+-]?[\d.]+$/ then result = parseFloat s
+    if s.match /^[+-]?[\d.]+$/ then parseFloat s
     # modifier all by its lonesome
-    else if s.match /^\$\d+$/ then result = parseFloat @modifiers[s]
+    else if s.match /^\$\d+$/ then parseFloat @modifiers[s]
     # else we got us some maths
-    else
+    else @evaluate calc.parse s

-    # retrun the result
-    result
+  evaluate: (op) ->
+    switch op.type
+      when 'n' then @getNumber op.val
+      when '+' then @evaluate(op.left) + @evaluate(op.right)
+      when '-' then @evaluate(op.left) - @evaluate(op.right)
+      when 'x' then @evaluate(op.left) * @evaluate(op.right)
+      when '/' then @evaluate(op.left) / @evaluate(op.right)

 module.exports = {
   MacroTool: MacroTool


improve DeliveryService property change behaviour

--- modules/components/delivery_service.cjsx
+++ modules/components/delivery_service.cjsx
@@ -40,23 +40,22 @@

   componentWillReceiveProps: (nextProps) ->
-    # if React.Router is not being used, don't bother checking for data refetch
-    return unless nextProps.params? or nextProps.location?
-
-    idSegment = @routeParamKey ? _.last (k for k,v of nextProps.params when k[-2..] is 'Id')
-
-    if idSegment?
-      idChanged = nextProps.params?[idSegment] isnt @props.params?[idSegment]
-    else
-      idChanged = false
-
-    # the idSegment is the first dynamic param present in the @props.params object from the router.
-    # it is the segment that represents the parent object which is what needs to be reloaded when the url changes.
-    # if this segment hasn't changed, no need to reload all the bound resources.
-    return if not idChanged and _.isEqual(nextProps.location?.query, @props.location?.query)
-
-    # show the loading UI unless we're just updating the query
-    @setState loaded: false if idChanged
+    # if React.Router is being used, do some additional change checks agains the URL
+    if nextProps.params? or nextProps.location?
+      idSegment = @routeParamKey ? _.last (k for k,v of nextProps.params when k[-2..] is 'Id')
+
+      if idSegment?
+        idChanged = nextProps.params?[idSegment] isnt @props.params?[idSegment]
+      else
+        idChanged = false
+
+      # the idSegment is the first dynamic param present in the @props.params object from the router.
+      # it is the segment that represents the parent object which is what needs to be reloaded when the url changes.
+      # if this segment hasn't changed, no need to reload all the bound resources.
+      return if not idChanged and _.isEqual(nextProps.location?.query, @props.location?.query)
+
+      # show the loading UI unless we're just updating the query
+      @setState loaded: false if idChanged

     @stopPolling()
     @stopListeningToBoundResources()


test(tap): use correct default class in tests

--- modules/directives/tap/test/index.coffee
+++ modules/directives/tap/test/index.coffee
@@ -5,7 +5,7 @@
   @$compile = injector.get '$compile'

 test 'tapDirective', ->
-  expect 9
+  expect 11

   # test standard tap
   element = @$compile('

') @$scope
@@ -52,9 +52,11 @@
   # test active-class (default and custom)
   element = @$compile('

') @$scope
   element.trigger $.Event 'touchstart', originalEvent: {pageX: 0, pageY: 0}
-  ok element.hasClass('a'), 'default active-class is set on touchstart'
+  ok element.hasClass('bp-active'),
+    'default active-class is set on touchstart'
   element.trigger 'touchend'
-  ok not element.hasClass('a'), 'default active-class was removed on touchend'
+  ok not element.hasClass('bp-active'),
+    'default active-class was removed on touchend'

   element = @$compile('

') @$scope
   element.trigger $.Event 'touchstart', originalEvent: {pageX: 0, pageY: 0}


adjust quot

adjust quot

--- lib/browser-plus-view.coffee
+++ lib/browser-plus-view.coffee
@@ -14,10 +14,11 @@
       resources = "#{atom.packages.getActivePackage('browser-plus').path}/resources/"
       url = "file://#{resources}history.html"
     if params.src
-      if params.src.includes "data:text/html,"
-        url = params.src
+      src = params.src.replace(/"/g,'"')
+      if src.includes "data:text/html,"
+        url = src
       else
-        url = "data:text/html, #{params.src}"
+        url = "data:text/html, #{src}"

     @div class:'browser-plus', =>
       @div class:'uri native-key-bindings', =>


added tests, finally

added tests, finally

--- spec/scroll-spec.coffee
+++ spec/scroll-spec.coffee
@@ -120,3 +120,111 @@
         keydown('b')
         expect(editor.setScrollTop).toHaveBeenCalledWith(860)
         expect(editor.moveToFirstCharacterOfLine).not.toHaveBeenCalled()
+
+  describe "horizontal scroll cursor keybindings", ->
+    beforeEach ->
+      editor.setWidth(600)
+      editor.setLineHeightInPixels(10)
+      editor.setDefaultCharWidth(10)
+      text = ""
+      for i in [100..199]
+        text += "#{i} "
+      editor.setText(text)
+      editor.setCursorBufferPosition([0, 0])
+
+    describe "the zs keybinding", ->
+      zsPos = (pos) ->
+        editor.setCursorBufferPosition([0, pos])
+        keydown('z')
+        keydown('s')
+        editor.getScrollLeft()
+
+      startPosition = NaN
+
+      beforeEach ->
+        startPosition = editor.getScrollLeft()
+
+      it "does nothing near the start of the line", ->
+        pos1 = zsPos(1)
+        expect(pos1).toEqual(startPosition)
+
+      it "moves the cursor the nearest it can to the left edge of the editor", ->
+        pos10 = zsPos(10)
+        expect(pos10).toBeGreaterThan(startPosition)
+
+        pos11 = zsPos(11)
+        expect(pos11 - pos10).toEqual(10)
+
+      it "does nothing near the end of the line", ->
+        posEnd = zsPos(399)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 399]
+
+        pos390 = zsPos(390)
+        expect(pos390).toEqual(posEnd)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 390]
+
+        pos340 = zsPos(340)
+        expect(pos340).toBeLessThan(posEnd)
+        pos342 = zsPos(342)
+        expect(pos342 - pos340).toEqual(20)
+
+      it "does nothing if all lines are short", ->
+        editor.setText('short')
+        startPosition = editor.getScrollLeft()
+        pos1 = zsPos(1)
+        expect(pos1).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 1]
+        pos10 = zsPos(10)
+        expect(pos10).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 5]
+
+
+    describe "the ze keybinding", ->
+      zePos = (pos) ->
+        editor.setCursorBufferPosition([0, pos])
+        keydown('z')
+        keydown('e')
+        editor.getScrollLeft()
+
+      startPosition = NaN
+
+      beforeEach ->
+        startPosition = editor.getScrollLeft()
+
+      it "does nothing near the start of the line", ->
+        pos1 = zePos(1)
+        expect(pos1).toEqual(startPosition)
+
+        pos40 = zePos(40)
+        expect(pos40).toEqual(startPosition)
+
+      it "moves the cursor the nearest it can to the right edge of the editor", ->
+        pos110 = zePos(110)
+        expect(pos110).toBeGreaterThan(startPosition)
+
+        pos109 = zePos(109)
+        expect(pos110 - pos109).toEqual(10)
+
+      it "does nothing when very near the end of the line", ->
+        posEnd = zePos(399)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 399]
+
+        pos397 = zePos(397)
+        expect(pos397).toEqual(posEnd)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 397]
+
+        pos380 = zePos(380)
+        expect(pos380).toBeLessThan(posEnd)
+
+        pos382 = zePos(382)
+        expect(pos382 - pos380).toEqual(20)
+
+      it "does nothing if all lines are short", ->
+        editor.setText('short')
+        startPosition = editor.getScrollLeft()
+        pos1 = zePos(1)
+        expect(pos1).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 1]
+        pos10 = zePos(10)
+        expect(pos10).toEqual(startPosition)
+        expect(editor.getCursorBufferPosition()).toEqual [0, 5]


Spy on HistoryManager prototype

Spy on HistoryManager prototype

To prevent other instances of HistoryManager from messing up the project
history

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -9,6 +9,7 @@
 FindParentDir = require 'find-parent-dir'
 {CompositeDisposable} = require 'event-kit'

+{HistoryManager} = require '../src/history-manager'
 TextEditor = require '../src/text-editor'
 TextEditorElement = require '../src/text-editor-element'
 TextMateLanguageMode = require '../src/text-mate-language-mode'
@@ -63,7 +64,7 @@

 beforeEach ->
   # Do not clobber recent project history
-  spyOn(atom.history, 'saveState').andReturn(Promise.resolve())
+  spyOn(HistoryManager::, 'saveState').andReturn(Promise.resolve())

   atom.project.setPaths([specProjectPath])


Trigger onchange event after updating el value


onfacetchange => facetChange

onfacetchange => facetChange

--- client/landing/app/Applications/Feeder.kdapplication/Controllers/FeederFacetsController.coffee
+++ client/landing/app/Applications/Feeder.kdapplication/Controllers/FeederFacetsController.coffee
@@ -6,7 +6,7 @@
     @facetTypes = ['filter', 'sort']
     @state = {}

-  onfacetchange:-> KD.getSingleton('router').handleQuery @state
+  facetChange:-> KD.getSingleton('router').handleQuery @state

   loadView:(mainView)->
     options = @getOptions()
@@ -27,7 +27,7 @@
       if controller.getData().items.length > 1
         controller.on 'NavItemReceivedClick', (item)=>
           @state[item.action] = item.type
-          @onfacetchange()
+          @facetChange()
         view.addSubView controller.getView()

     view.addSubView new HelpBox @getOptions().help


Tweaked socket logging

Tweaked socket logging

--- src/wrathnet/net/socket.coffee
+++ src/wrathnet/net/socket.coffee
@@ -50,6 +50,8 @@
       @port = port
       @uri = 'ws://' + @host + ':' + @port

+      console.info 'connecting to auth-server @', @host, ':', @port
+
       @socket = new WebSocket(@uri, 'binary')
       @socket.binaryType = 'arraybuffer'

@@ -88,10 +90,9 @@

       packet.finalize()

-      console.log '⟸', packet
-      console.log packet.toString()
-      console.log packet.toHex()
-      console.log packet.toASCII()
+      console.log '⟸', packet, packet.toString()
+      console.debug packet.toHex()
+      console.debug packet.toASCII()

       @socket.send(packet.buffer)


Don't try to re-register ProblemView custom element.

If the user does something that reloads the package, we might try to
register our custom ProblemView element twice.

Resolves #26.

--- lib/info/problem_view.coffee
+++ lib/info/problem_view.coffee
@@ -5,9 +5,18 @@
 class ProblemView
   @uri: 'dart-tools://problem-view'

+  @isRegistered: (tagName) =>
+    document.createElement(tagName).constructor != HTMLElement
+
   @register: (@errors) =>
-    pve = document.registerElement 'dart-tools-problem-view',
-      prototype: ProblemViewElement.prototype
+    # Github issue #26: somehow module is being reloaded,
+    # but document isn't, so the dart-tools-problem-view element is already
+    # registered. User might be toggling dart-tools on/off via settings panel?
+    pve = if @isRegistered('dart-tools-problem-view')
+      document.createElement('dart-tools-problem-view').constructor
+    else
+      document.registerElement 'dart-tools-problem-view',
+        prototype: ProblemViewElement.prototype

     atom.views.addViewProvider ProblemView, pve


Performance: New (faster) Array creation

  65 --> 68
  26 --> 29
  9 --> 11

--- lib/javascripts/board.coffee
+++ lib/javascripts/board.coffee
@@ -1,15 +1,13 @@
 BLACK = 1
 WHITE = -1
-EMPTY = 0
+EMPTY = undefined
 NEUTRAL = 2
 VISITED = true

 initBoard = (size)->
-  board = []
+  board = new Array size
   for i in [0...size]
-    board.push []
-    for j in [0...size]
-      board[i][j] = EMPTY
+    board[i] = new Array size
   board.moves = []
   board


Removed a couple-few blank lines

Removed a couple-few blank lines

Because apparently i am obsessive and it bothered me.

--- lib/streams/pistachio.coffee
+++ lib/streams/pistachio.coffee
@@ -46,6 +46,4 @@

-
-
 exports.PistachioThis   = PistachioThis


Image gallery indicators can now be used to navigate gallery

--- static/js/image-gallery.coffee
+++ static/js/image-gallery.coffee
@@ -25,10 +25,14 @@
     @nextButton.addEventListener 'click', @moveToNextImage
     @prevButton.addEventListener 'click', @moveToPreviousImage

+    for el in @imageIndicators
+      el.addEventListener 'click', @imageIndicatorClicked
+
     # Recalculate translateX distance when the browser is resized to avoid
     # layout issues
     window.addEventListener 'resize', =>
       @setTranslateXProperty @imageContainerRule, -@imageGalleryElement.offsetWidth * @currentIndex
+

   moveToNextImage: =>
     if @numberOfImages <= @currentIndex + 1
@@ -54,6 +58,16 @@

     @currentIndex--

+  imageIndicatorClicked: (e) =>
+    selectedIndex = Array.prototype.indexOf.call(@imageIndicators, e.target)
+
+    if selectedIndex < @currentIndex
+      while selectedIndex < @currentIndex
+        @moveToPreviousImage()
+    else if selectedIndex > @currentIndex
+      while selectedIndex > @currentIndex
+        @moveToNextImage()
+
   setTranslateXProperty: (rule, distance) ->
     rule.style.setProperty 'transform', "translateX(#{distance}px)", 'important'
     rule.style.setProperty '-ms-transform', "translateX(#{distance}px)", 'important'


add error handling for changing email in newsletter of user who has not subscribed

--- services/web/app/coffee/Features/Newsletter/NewsletterManager.coffee
+++ services/web/app/coffee/Features/Newsletter/NewsletterManager.coffee
@@ -39,8 +39,11 @@
 		delete options.body.status
 		options.body.email_address = newEmail
 		mailchimp.request options, (err)->
+			if err? and err?.message?.indexOf("merge fields were invalid")
+				logger.log {oldEmail, newEmail}, "unable to change email in newsletter as user has not subscribed"
+				return callback()
 			# if the user has unsubscribed mailchimp will error on email address change
-			if err? and err?.message.indexOf("could not be validated") == -1
+			else if err? and err?.message?.indexOf("could not be validated") == -1
 				logger.err err:err, "error changing email in newsletter"
 				return callback(err)
 			else


Steward: reorder for performance

Steward: reorder for performance

--- lib/web/views/Steward.coffee
+++ lib/web/views/Steward.coffee
@@ -115,7 +115,7 @@

   targetHostSame: (t) =>
     l = window.location
-    t.protocol == l.protocol && t.username == l.username && t.password == t.password && t.host == l.host
+    t.host == l.host && t.protocol == l.protocol && t.username == l.username && t.password == t.password

   attachLocationChangeListener: =>
     if @pushStateSupport


Switches to MongoClient for connect

Switches to MongoClient for connect

--- coffee/database.coffee
+++ coffee/database.coffee
@@ -1,4 +1,7 @@
-mongodb = require 'mongodb'
+"use strict"
+
+MongoClient = require('mongodb').MongoClient
+format = require('util').format

 #
 # Connect to database
@@ -6,20 +9,16 @@
 # @param {@code String} collection - collection name
 # @param {@code Function} cb - callback function (err, db)
 #
-exports.connect = (collection, cb) ->
-  collection = collection || 'ntb_07'
+# github.com/mongodb/node-mongodb-native/blob/master/docs/articles/MongoClient.md
+#
+exports.connect = (database, cb) ->
+  database = database || 'ntb_07'
+  host = process.env['MONGO_NODE_DRIVER_HOST'] || 'localhost'
+  port = process.env['MONGO_NODE_DRIVER_PORT'] || 27017
+  url  = format "mongodb://%s:%s,%s:%s,%s:%s/%s", host, port, host, port+1, host, port+2, database

-  replSet = new mongodb.ReplSet [
-    new mongodb.Server '127.0.0.1', 27017, {}
-    , new mongodb.Server '127.0.0.1', 27018, {}
-    , new mongodb.Server '127.0.0.1', 27019, {}
-  ]
-
-  db = new mongodb.Db collection, replSet, {
-    w: 0
-  }
-
-  db.open (err, db) ->
+  MongoClient.connect url, (err, db) ->
+    db.close() if err
     return cb err, db

 #


Notebook contents list ids, not paths

Notebook contents list ids, not paths

--- coffeescript/db.coffee
+++ coffeescript/db.coffee
@@ -102,11 +102,9 @@

 		files = fs.readdirSync @notebookdir
 		files.forEach (file) ->
-			notebook.contents.push file if file.match(id) and file.substr(16,5) isnt ".json"
+			notebook.contents.push file.substr(17, 16) if file.match(id) and file.substr(16,5) isnt ".json"

 		return notebook
-
-

 	###
 	# Read a note


Sanitize HTML tags

Sanitize HTML tags

--- src/preprocess.coffee
+++ src/preprocess.coffee
@@ -1,12 +1,20 @@
 preprocess = (root, options) ->
+  # Formalize HTML tags.
   root.children = decomposeHTMLNodes(root.children)
   root.children = foldHTMLNodes(root.children)
+
+  # Process footnotes and and links.
   if options.footnotes
     mapping = defineFootnoteNumber(root)
     applyFootnoteNumber(root, mapping)
   defs = removeDefinitions(root)
   if options.footnotes
     appendFootnoteDefinitionCollection(root, defs)
+
+  # Sanitize HTML tags.
+  root = wrapHTMLNodeInParagraph(root)
+  root = sanitizeTag(root)
+
   [root, defs]

 # Sets `footnoteNumber` property to every footnote reference node.
@@ -194,4 +202,70 @@
   voidElementNames = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']
   voidElementNames.indexOf(elementName) != -1

+# Wraps each top-level HTML node in a paragraph node.
+#
+# mdast wil put an HTML node directly in `root.children` without wrapping in a
+# paragraph node if we write some block element in Markdown:
+#
+#     Markdown:
+#
foo
+#
+#         bar

+#
+#     AST:
+#         root.children = [ { type: "html", value: "
foo\nbar
" } ]
+#
+# However, we disallow writing block element tag in Markdown and convert such
+# an HTML node into a series of text nodes by using `sanitizeTag()`.
+# To form a paragraph node which have such text nodes as children, we wrap
+# top-level HTML nodes in paragraph nodes before applying `sanitizeTag()`.
+wrapHTMLNodeInParagraph = (root) ->
+  children = []
+  for child in root.children
+    if child.type is 'html'
+      children.push({type: 'paragraph', children: [child]})
+    else
+      children.push(child)
+  root.children = children
+  root
+
+# A subset of [phrasing content] tags, plus RP and RT.
+# We rejected some phrasing content tags from the set because it is able
+# to write a semantically incorrect HTML with them, which leads to a crash of
+# React.
+#
+# Rejected tags are kinds of:
+#
+#     - embedded contents like IFRAME, MATH, AUDIO, and VIDEO, except for IMG;
+#     - interactive contents like BUTTON, KEYGEN, and PROGRESS;
+#
+# [phrasing content]: http://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0
+ALLOWED_TAG_NAMES = [
+  'a', 'abbr', 'b', 'br', 'cite', 'code', 'del', 'dfn', 'em', 'i', 'img',
+  'input', 'ins', 'kbd', 'mark', 'ruby', 'rp', 'rt', 'q', 's', 'samp', 'small',
+  'span', 'strong', 'sub', 'sup', 'u', 'wbr',
+]
+
+# Flatten a disallowed kind of folded tag node into a series of nodes.
+#
+# Example:
+#     node.children = [ { type: 'html, subtype: 'folded',
+#                         startTag: startTag, endTag: endTag, children: [child1, child2] }, ... ]
+#     # ^this is flattend into:
+#     node.children = [ startTag, child1, child2, endTag, ... ]
+#
+# startTag and endTag are now freestanding, so they will be rendered as invalid HTML tags.
+sanitizeTag = (node) ->
+  return node unless node.children?
+  children = []
+  for child in node.children
+    if child.subtype is 'folded' and child.tagName not in ALLOWED_TAG_NAMES
+      children.push(child.startTag)
+      Array::push.apply(children, sanitizeTag(child).children)
+      children.push(child.endTag)
+    else
+      children.push(sanitizeTag(child))
+  node.children = children
+  node
+
 module.exports = preprocess


Remove grouping from crop

Remove grouping from crop

--- components/Crop.coffee
+++ components/Crop.coffee
@@ -21,7 +21,6 @@
     out: 'canvas'
     forwardGroups: yes
     async: yes
-    group: true
   , (input, groups, out, callback) ->
     originalCanvas = input.canvas
     {x, y, width, height} = input.rectangle


Terrible hack to fix #203 (#204)

Terrible hack to fix #203 (#204)

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -85,6 +85,7 @@
 				@robot.logger.error "If joining GENERAL please make sure its using all caps."
 				@robot.logger.error "If using LDAP, turn off LDAP, and turn on general user registration with email
 					verification off."
+				process.exit 1 #hack to make hubot die on login error to fix #203
 				throw loginErr #rethrow to exit the chain
 			)
 			# Get room IDS


Code style.

Code style.

--- src/servicemap.coffee
+++ src/servicemap.coffee
@@ -264,7 +264,7 @@
     sidebar = L.control.sidebar 'sidebar',
         position: 'left'
     map.addControl sidebar
-    sidebar.on 'hide', () ->
+    sidebar.on 'hide', ->
         sidebar._active_marker.closePopup()

     map.on 'click', (ev) ->


callbacks for the new rotation tests

callbacks for the new rotation tests

--- testApp/app/controllers/rotations.coffee
+++ testApp/app/controllers/rotations.coffee
@@ -3,22 +3,34 @@
   ## steroids.screen.setAllowedRotations

   @testAllowPortrait: ->
-    steroids.screen.setAllowedRotations "portrait"
+    steroids.screen.setAllowedRotations "portrait",
+      onSuccess: -> steroids.logger.log "SUCCESS in allowing only portrait orientation"
+      onFailure: -> navigator.notification.alert "FAILURE in testAllowPortrait rotations"

   @testAllowPortraitUpsideDown: ->
-    steroids.screen.setAllowedRotations "portraitUpsideDown"
+    steroids.screen.setAllowedRotations "portraitUpsideDown",
+      onSuccess: -> steroids.logger.log "SUCCESS in allowing only upside-down portrait orientation"
+      onFailure: -> navigator.notification.alert "FAILURE in testAllowPortraitUpsideDown"

   @testAllowLandscapeLeft: ->
-    steroids.screen.setAllowedRotations "landscapeLeft"
+    steroids.screen.setAllowedRotations "landscapeLeft",
+      onSuccess: -> steroids.logger.log "SUCCESS in allowing only landscape left orientation"
+      onFailure: -> navigator.notification.alert "FAILURE in testAllowLandscapeLeft"

   @testAllowLandscapeRight: ->
-    steroids.screen.setAllowedRotations "landscapeRight"
+    steroids.screen.setAllowedRotations "landscapeRight",
+      onSuccess: -> steroids.logger.log "SUCCESS in allowing only landscape right orientation"
+      onFailure: -> navigator.notification.alert "FAILURE in testAllowLandscapeRight"

   @testAllowAll: ->
-    steroids.screen.setAllowedRotations ["portrait", "portraitUpsideDown", "landscapeLeft", "landscapeRight"]
+    steroids.screen.setAllowedRotations ["portrait", "portraitUpsideDown", "landscapeLeft", "landscapeRight"],
+      onSuccess: -> steroids.logger.log "SUCCESS in allowing all orientations and rotations by using the names of the orientations"
+      onFailure: -> navigator.notification.alert "FAILURE in testAllowAll rotations"

   @testAllowAllWithDegrees: ->
-    steroids.screen.setAllowedRotations ["0", 90, -90, "180"]
+    steroids.screen.setAllowedRotations ["0", 90, -90, "180"],
+      onSuccess: -> steroids.logger.log "SUCCESS in allowing all orientations and rotations by using the degrees"
+      onFailure: -> navigator.notification.alert "FAILURE in testAllowAllWithDegrees"

   ## steroids.screen.rotate
   _rotationOptions =
@@ -50,40 +62,40 @@
     steroids.view.setAllowedRotations {
       allowedRotations: [0]
     }, {
-      onSuccess: -> steroids.logger.log "Allowed rotation to 0 (portrait)."
-      onFailure: -> steroids.logger.log "Could not set allowed rotations to 0 (portrait)."
+      onSuccess: -> steroids.logger.log "SUCCESS Allowed rotation to 0 (portrait)."
+      onFailure: -> steroids.logger.log "FAILURE in testLegacyAllowRotation0 - Could not set allowed rotations to 0 (portrait)."
     }

   @testLegacyAllowRotation90: ->
     steroids.view.setAllowedRotations {
       allowedRotations: [90]
     }, {
-      onSuccess: -> steroids.logger.log "Allowed rotation to 90 (landscape)."
-      onFailure: -> steroids.logger.log "Could not set allowed rotations to 0 (portrait)."
+      onSuccess: -> steroids.logger.log "SUCCESS in Allowed rotation to 90 (landscape)."
+      onFailure: -> steroids.logger.log "FAILURE in testLegacyAllowRotation90 - Could not set allowed rotations to 0 (portrait)."
     }

   @testLegacyAllowRotationAll: ->
     steroids.view.setAllowedRotations {
       allowedRotations: [0, 90, 180, -90]
     }, {
-      onSuccess: -> steroids.logger.log "Allowed rotation to all orientations."
-      onFailure: -> steroids.logger.log "Could not set allowed rotations to all orientations."
+      onSuccess: -> steroids.logger.log "SUCCESS in Allowed rotation to all orientations."
+      onFailure: -> steroids.logger.log "FAILURE in testLegacyAllowRotationAll - Could not set allowed rotations to all orientations."
     }

   @testLegacyAllowRotationHorizontal: ->
     steroids.view.setAllowedRotations {
       allowedRotations: [-90, 90]
     }, {
-      onSuccess: -> steroids.logger.log "Allowed rotation to horizontal orientations."
-      onFailure: -> steroids.logger.log "Could not set allowed rotations to horizontal orientations."
+      onSuccess: -> steroids.logger.log "SUCCESS in Allowed rotation to horizontal orientations."
+      onFailure: -> steroids.logger.log "FAILURE in testLegacyAllowRotationHorizontal - Could not set allowed rotations to horizontal orientations."
     }

   @testLegacyAllowRotationVertical: ->
     steroids.view.setAllowedRotations {
       allowedRotations: [0, 180]
     }, {
-      onSuccess: -> steroids.logger.log "Allowed rotation to vertical orientations."
-      onFailure: -> steroids.logger.log "Could not set allowed rotations to vertical orientations."
+      onSuccess: -> steroids.logger.log "SUCCESS in Allowed rotation to vertical orientations."
+      onFailure: -> steroids.logger.log "FAILURE in testLegacyAllowRotationVertical - Could not set allowed rotations to vertical orientations."
     }

   @testLegacyRotateTo0: () ->


update

update


dump RPC server shut down event

dump RPC server shut down event

--- src/tex-writer.coffee
+++ src/tex-writer.coffee
@@ -2210,7 +2210,9 @@
 @pdf_from_md = ( source_route, handler ) ->
   RPC_SERVER              = require './rpc-server'
   server                  = await ( promisify RPC_SERVER.listen.bind RPC_SERVER )()
-  process.on 'exit', -> server.close()
+  process.on 'exit', ->
+    help '44092', "RPC server closing"
+    server.close()
   #.......................................................................................................
   S                       = @_new_state source_route
   content_output          = njs_fs.createWriteStream S.layout_info[ 'content-locator' ]


add comments in textChat code

add comments in textChat code

--- app/static/scripts/modules/textChat.coffee
+++ app/static/scripts/modules/textChat.coffee
@@ -37,40 +37,47 @@
     constructor     : (emitter) ->
         super emitter

+        #Define the Markdown interpretor
         @markdown     = new Showdown.converter { extensions : ['voicious'] }
+
+        #Define accessor for DOM elements
         @content = ($ '#textChat')
         @content.messages = @content.find '#chatContent > ul'
         @content.form = @content.find 'form'
         @content.form.textarea = @content.form.find 'textarea'

+        #Launch jScrollpane
         @scrollPane   = @content.messages.jScrollPane horizontalDragMaxWidth : 0
         @scrollPane   = @content.messages.data 'jsp'
+        ($ window).resize () =>
+            do @scrollPane.reinitialise

+        #Define what's going on when and how the form is submitted
         @content.form.textarea.on 'keypress', (event) =>
             if event.keyCode is 13 and not event.shiftKey
                 do event.preventDefault
                 @content.form.trigger 'submit'
-
         @content.form.submit @submit

+        #Initialize the message list and tis watcher
         (@messages = []).watch 'length', @update

-        ($ window).resize () =>
-            do @scrollPane.reinitialise
-
+        #Define chat's own commands
         @emitter.trigger 'cmd.register',
             name : 'me'
             callback : @me

+        #Define event bindings
         @emitter.on 'chat.message', @newMessage
-
         @emitter.on 'peer.create', (event, data) =>
             @emitter.trigger 'chat.message', { text : "#{data.name} arrives in the room." }
         @emitter.on 'peer.remove', (event, data) =>
             @emitter.trigger 'chat.message', { text : "#{data.name} leaves the room. (#{data.reason})" }

     submit : (event) =>
+        #Do not send the form
         do event.preventDefault
+        #Translating line break to HTML

         message = (do @content.form.textarea.val).replace /\n/g, '
'
         @content.form.textarea.val ''
         # We check if it's a command or a message
@@ -79,20 +86,27 @@
             @sendCommand command
         else
             @sendMessage message
+        #Do not send the form
         no

     # Update the text chat with a new message.
     update          : () =>
+        #Empty the message list
         container = (do @scrollPane.getContentPane)
         do container.empty
+
         prevMessage = undefined
+        #Loop through all messages
         @messages.map (message) =>
+            #Humanize the timestamp
             formatedTime = (do (new Date message.time).toTimeString).substr 0, 5
+            #Group message sent by a user
             if prevMessage? and prevMessage.message.from is message.from and message.time - prevMessage.message.time < 3000
                 prevMessage.message = message
                 (($ prevMessage.html[2]).find '.chatmessage').append message.text
             else
                 html = undefined
+                #If message.from is not defined, thew it is a server message
                 if message.from?
                     html = """

@@ -116,6 +130,8 @@

                     """).appendTo container
                     message : message
+
+        #Reinitialize the scrollPane and scroll to its bottom
         do @scrollPane.reinitialise
         @scrollPane.scrollToPercentY 100, no

@@ -136,7 +152,9 @@
             @emitter.trigger 'chat.message', message

     newMessage : (event, message) =>
+        #Append a timestamp to the object
         message.time = do (new Date).getTime
+        #Push the object into the watched array
         @messages.push message

     me : (user, data) =>


SPCITY-9 SPCITY-8 fixes

SPCITY-9 SPCITY-8 fixes

--- lib/assets/javascripts/directives.js.coffee
+++ lib/assets/javascripts/directives.js.coffee
@@ -185,7 +185,7 @@
     gridSize = 25
     width  = parseInt attrs.width
     height = parseInt attrs.height
-    scope.padding = padding = 20
+    scope.padding = padding = if (scope.settings? and scope.settings.editing?) then 20 else 4

     ######### End Attributes ########

@@ -285,6 +285,7 @@
               scope.$emit 'changed'
           else
             pimage.mouseup (e) -> _handle_click(e)
+            pimage.mousemove (e) -> _handle_mouse_move(e)
         img.src = image.path

@@ -380,6 +381,15 @@
       else if scope.mode == 'pointing'
         point = _create_point(xy.x,xy.y)

+    _handle_mouse_move = (e) ->
+      if Touch? && (e instanceof Touch)
+        return
+      else
+        xy = _get_x_y e
+        if scope.mode == 'lining' and _current_line?
+          _dragging = true
+          snapped = _snap_to_grid(xy.x, xy.y)
+          _current_line.dragging(snapped.x, snapped.y)

     _mouseup = (e) ->
       xy = _get_x_y(e)
@@ -390,6 +400,16 @@
       else if (TouchEvent?) && !_current_line?
         _start_line(xy.x, xy.y)

+    _resetAxis = () ->
+      if scope.axis?
+        scope.axis.remove()
+        scope.axis.clear()
+      if scope.labels?
+        scope.labels.remove()
+        scope.labels.clear()
+      if scope.settings.editing
+        scope.circ.remove()
+
     ############## end local methods ############

     ############## Setup ########################
@@ -419,15 +439,11 @@
       # console.log 'settings.origin'
       if scope.settings?
         if scope.settings.origin? && scope.settings.origin
+          _resetAxis()
           _drawAxis(scope.settings.origin, scope.settings.editing)
         else
           if scope.axis?
-            scope.axis.remove()
-            scope.axis.clear()
-            scope.labels.remove()
-            scope.labels.clear()
-            if scope.settings.editing
-              scope.circ.remove()
+            _resetAxis()

     scope.$watch 'settings.images', ->
       # console.log 'settings.images'
@@ -441,6 +457,7 @@
       if scope.mode == 'lining' and _current_line?
         _dragging = true

+
     glass.mousedown (e) ->
       if Touch? && (e instanceof Touch)
         return
@@ -454,14 +471,7 @@
       _handle_click(e)

     glass.mousemove (e) ->
-      if Touch? && (e instanceof Touch)
-        return
-      else
-        xy = _get_x_y e
-        if scope.mode == 'lining' and _current_line?
-          _dragging = true
-          snapped = _snap_to_grid(xy.x, xy.y)
-          _current_line.dragging(snapped.x, snapped.y)
+      _handle_mouse_move(e)

     ####### End Events ##################
 ]


resurrection.activity: do not decorate `ActivityInputView` content if it's already in focus state

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -44,6 +44,7 @@
     super @renderTokens content, tokens

   focus: ->
+    return  if @focused
     super
     value = @getValue()
     unless value


activity: deprecate ineffective `CommentLikeView` constructor function

--- client/Social/Activity/views/comments/commentlikeview.coffee
+++ client/Social/Activity/views/comments/commentlikeview.coffee
@@ -1,11 +1,4 @@
 class CommentLikeView extends ActivityLikeView
-
-  constructor: ->
-
-    super
-
-    @likeLink.updatePartial "Like"
-

   pistachio: ->


be a bit more serious with React keys

be a bit more serious with React keys

--- client/app/components/basic_components.coffee
+++ client/app/components/basic_components.coffee
@@ -205,6 +205,7 @@
 AddressLabel = React.createClass

     render: ->
+        meaninglessKey = 0

         if @props.contact.name?.length > 0 and @props.contact.address
             key = @props.contact.address.replace /\W/g, ''
@@ -218,7 +219,8 @@
                         "<#{@props.contact.address}>"

         else if @props.contact.name?.length > 0
-            result = span key: Math.random(), @props.contact.name
+            result = span key: "label-#{meaninglessKey++}",
+                @props.contact.name

         else
             result = span null, @props.contact.address


client: remove providerspecs.coffee

client: remove providerspecs.coffee

--- client/app/lib/constants/providerSpecs.coffee
+++ client/app/lib/constants/providerSpecs.coffee
@@ -1,20 +0,0 @@
-module.exports =
-	aws:
-		't2.nano':
-			ram:'512MB RAM'
-			cpu:'1x CPU'
-		't2.micro':
-			ram:'1GB RAM'
-			cpu:'1x CPU'
-		't2.medium':
-			ram:'4GB RAM'
-			cpu:'1x CPU'
-		'm4.large':
-			ram:'8GB RAM'
-			cpu:'2x CPU'
-		'm4.xlarge':
-			ram:'16GB RAM'
-			cpu:'4x CPU'
-		'm4.2xlarge':
-			ram:'32GB RAM'
-			cpu:'8x CPU'


Redundant sidebar method call

Redundant sidebar method call

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -915,7 +915,6 @@

     @rtm.once 'RealtimeManagerWillDispose', =>
       kd.utils.killRepeat @pingInterval
-      kd.singletons.mainView.activitySidebar.emit 'ReloadMessagesRequested'

     @rtm.once 'RealtimeManagerDidDispose', =>
       @rtm = null


collaboration: modify kick participant to follow same callback pattern

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -104,24 +104,24 @@

     return  unless @amIHost

-    targetUser = account.profile.nickname
-
-    displayError = (err) ->
-      showError err
-      throwError err
-
-    @setMachineUser [targetUser], no, (err) =>
-      return displayError err  if err
-
+    target = account.profile.nickname
+
+    # this object is used to follow the same pattern as other
+    # methods. IMO, it makes it easier to read. ~Umut
+    callbacks =
+      success: =>
+        @broadcastMessage { target, type: 'ParticipantKicked' }
+        @handleParticipantKicked target
+      error: (err) ->
+        # TODO: better error handling.
+        showError err
+        throwError err
+
+    @setMachineUser [target], no, (err) =>
+      return callbacks.error err  if err
       socialHelpers.kickParticipants @socialChannel, [account], (err, result) =>
-        return displayError err  if err
-
-        message    =
-          type     : 'ParticipantKicked'
-          target   : targetUser
-
-        @broadcastMessage message
-        @handleParticipantKicked targetUser
+        return callbacks.error err  if err
+        callbacks.success()

   handleParticipantKicked: (username) ->


collaboration: controller minor

collaboration: controller minor

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -263,6 +263,7 @@
       @myWatchMap        = null
       @mySnapshot        = null

+
   registerCollaborationSessionId: ->

     realtimeHelpers.registerCollaborationSessionId @rtm, @participants
@@ -693,26 +694,6 @@
         success: =>
           @modal?.destroy()
           @handleCollaborationEndedForParticipant()
-
-
-  handleCollaborationEndedForParticipant: ->
-
-    # TODO: fix explicit state checks.
-    return  unless @stateMachine.state in ['Active', 'Ending']
-
-    # TODO: fix implicit emit.
-    @rtm.once 'RealtimeManagerWillDispose', =>
-      @chat.emit 'CollaborationEnded'
-      @chat.destroy()
-      @chat = null
-      @statusBar.emit 'CollaborationEnded'
-      @removeParticipant nick()
-      @removeMachineNode()
-
-    @rtm.once 'RealtimeManagerDidDispose', =>
-      kd.utils.defer @bound 'quit'
-
-    @cleanupCollaboration()

   endCollaborationForHost: (callbacks) ->
@@ -741,6 +722,26 @@
         callbacks.success()

+  handleCollaborationEndedForParticipant: ->
+
+    # TODO: fix explicit state checks.
+    return  unless @stateMachine.state in ['Active', 'Ending']
+
+    # TODO: fix implicit emit.
+    @rtm.once 'RealtimeManagerWillDispose', =>
+      @chat.emit 'CollaborationEnded'
+      @chat.destroy()
+      @chat = null
+      @statusBar.emit 'CollaborationEnded'
+      @removeParticipant nick()
+      @removeMachineNode()
+
+    @rtm.once 'RealtimeManagerDidDispose', =>
+      kd.utils.defer @bound 'quit'
+
+    @cleanupCollaboration()
+
+
   showChat: ->

     switch @stateMachine.state


collaboration: controller minor

collaboration: controller minor

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -140,9 +140,7 @@

   handleParticipantAction: (actionType, changeData) ->

-    return  unless @rtm
-
-    kd.utils.wait 2000, => @whenRealtimeReady =>
+    kd.utils.wait 2000, =>

       switch actionType
         when 'join' then @onRealtimeParticipantJoined changeData
@@ -732,7 +730,6 @@

   cleanupCollaboration: (options = {}) ->
-    return warn 'RealtimeManager is not set'  unless @rtm

     kd.utils.killRepeat @pingInterval
     @rtm?.dispose()
@@ -789,14 +786,11 @@
     {setMachineUser} = envHelpers

     setMachineUser @mountedMachine, usernames, share, (err) =>
-      if err
-        return  if err.message is 'User not found' and not share
-        return callback err  if err
-
-      @whenRealtimeReady =>
-        @broadcastMessage
-          type: "#{if share then 'Set' else 'Unset'}MachineUser"
-          participants: usernames
+      return callback err  if err
+
+      @broadcastMessage
+        type: "#{if share then 'Set' else 'Unset'}MachineUser"
+        participants: usernames

       callback null


I can't create new project on my local install; something wrong with PATH not having .sagemathcloud in it... (?) -- this is a result of env bug introduced in `misc_node.coffee`, which I fortunately never deployed.

--- salvus/misc_node.coffee
+++ salvus/misc_node.coffee
@@ -257,10 +257,11 @@
     stderr = ''
     exit_code = undefined

+    env = misc.copy(process.env)
+
     if opts.env?
-        env = misc.copy(opts.env)
-    else
-        env = {}
+        for k, v of opt.env
+            env[k] = v

     if opts.uid?
         env.HOME = opts.home


Added a "tip" quote type

Added a "tip" quote type

--- .metalsmith/metal.coffee
+++ .metalsmith/metal.coffee
@@ -73,6 +73,7 @@
     "good": "good fa fa-check-circle"
     "warning": "warning fa fa-info-circle"
     "error": "error fa fa-exclamation-circle"
+    "tip": "tip fa fa-info-circle"

   # Blocks is the final list of blockquotes that we're going to return
   blocks = []


StaticPage: Regress unused createLinkContent (renderBody already implements this)

--- server/lib/crawler/helpers.coffee
+++ server/lib/crawler/helpers.coffee
@@ -77,18 +77,6 @@
       """
     return tagContent

-createLinkContent = (href, title, text) ->
-  marked = require 'marked'
-  renderer = new marked.Renderer()
-  renderer.link= (href, title, text)->
-    linkHTML = "#{text}" \
-    else " rel=\"nofollow\">#{text}"
-
-    return linkHTML

 createProfile = (models, activity, callback)->
   {JAccount} = models


fix in_usage_queue

fix in_usage_queue

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -187,7 +187,7 @@
       @usage_queued_at()?

     @in_usage_queue_p = @komp =>
-      not @finished() and @queued_at()?
+      not @finished() and not @queued_at()?

     @in_run_queue_p = @komp =>
       not @finished() and @queued_at()? and not @start_time()?


make sure to register disconnection first

fixes an infinite loop

--- lib/connection/client.coffee
+++ lib/connection/client.coffee
@@ -92,9 +92,9 @@
     @queue = []

   disconnected: ->
-    @emitter.emit 'disconnected'
     delete @conn
     @reset()
+    @emitter.emit 'disconnected'

   booting: ->
     @isBooting = -> true


cruft

cruft

--- lib/connection/client.coffee
+++ lib/connection/client.coffee
@@ -90,14 +90,10 @@

   basicHandler: (s) ->
     if (match = s.toString().match /juno-msg-(.*)/)
-      console.log match[1]
-      console.log @basicHandlers[match[1]]
       @basicHandlers[match[1]]?()
       true

-  handleBasic: (msg, f) ->
-    @basicHandlers[msg] = f
-    console.log @basicHandlers
+  handleBasic: (msg, f) -> @basicHandlers[msg] = f

   # Connecting & Booting


Only empty router main if view also uses it directly

--- src/coffee/cilantro/router.coffee
+++ src/coffee/cilantro/router.coffee
@@ -62,10 +62,10 @@
             if not view._rendered
                 view._rendered = true
                 if options.el isnt false
-                    @_empty()
                     if options.el?
                         target = Backbone.$(options.el, @options.main)
                     else
+                        @_empty()
                         target = Backbone.$(@options.main)
                     target.append(view.el)
                     view.render?()


code copy button

code copy button

--- lib/connection/messages.coffee
+++ lib/connection/messages.coffee
@@ -79,15 +79,16 @@

   connectExternal: ->
     tcp.listen().then (port) ->
+      code = "using Juno; Juno.connect(#{port})"
       msg = atom.notifications.addInfo "Connect an external process",
         detail: """
         To connect a Julia process running in the terminal,
         run the command:
-
-            using Juno
-            Juno.connect(#{port})
+        -
+            #{code}
         """
         dismissable: true
+        buttons: [{text: 'Copy', onDidClick: -> atom.clipboard.write code}]
       client.onceAttached ->
         if not msg.isDismissed()
           msg.dismiss()


Correctly support domReady() callbacks when the DOM is already ready

--- tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/core/spi.coffee
@@ -20,6 +20,10 @@
 # Prototype ... but does it in a way that makes it relatively easy to swap in jQuery instead.
 define ["_", "prototype"], (_) ->

+  # When the document has loaded, convert `domReady` to just execute the callback immediately.
+  $(document).observe "dom:loaded", ->
+    exports.domReady = (callback) -> callback()
+
   # _internal_: splits the string into words separated by whitespace
   split = (str) ->
     _(str.split " ").reject (s) -> s is ""
@@ -270,6 +274,13 @@
       else
         return null

+    # Finds all child elements matching the CSS selector, returning them
+    # as an array of ElementWrappers.
+    findAll: (selector) ->
+      matches = @element.select selector
+
+      _.map matches, (e) -> new ElementWrapper e
+
     # Returns an ElementWrapper for this element's containing element. The ElementWrapper is created lazily, and
     # cached. Returns null if this element has no parentNode (either because this element is the document object, or
     # because this element is not yet attached to the DOM).
@@ -401,7 +412,7 @@
     # still be in-transit). This is a safe time to search the DOM, modify attributes, and attach event handlers.
     # Returns this modules exports, for chained calls.
     domReady: (callback) ->
-      document.observe "dom:loaded", callback
+      $(document).observe "dom:loaded", callback

       exports


Protect seen route from failing when user isn't found


fixed

fixed

--- scripts/snomedct.coffee
+++ scripts/snomedct.coffee
@@ -38,4 +38,4 @@
     .get() (err, response, body) ->

       data = JSON.parse body
-      res.send "For concept #{concept}, there are #{data.details.total} results and the first one has an SCTID of #{data.matches[0].conceptId} and the FSN, #{data.matches[0].fsn} with a URL/URI of http://snomed.info/id/#{sctid}"
+      res.send "For concept #{concept}, there are #{data.details.total} results and the first one has an SCTID of #{data.matches[0].conceptId} and the FSN, #{data.matches[0].fsn} with a URL/URI of http://snomed.info/id/#{data.matches[0].conceptId}"


Project configuration

Project configuration

--- lib/handler/config_view.coffee
+++ lib/handler/config_view.coffee
@@ -1,22 +1,121 @@
 {CompositeDisposable} = require 'event-kit'
+{ScrollView} = require 'atom-space-pen-views'
+{$$, TextEditorView} = require 'atom-space-pen-views'
+query = require './query'

-{ScrollView} = require 'atom-space-pen-views'

-Highcharts = require('highcharts')
 fs = require('fs')
-path = require('path')
+path_lib = require('path')
 moment = require('moment')
+
+self_config= {}
+projects_mapping = {}

 module.exports =
 class Dashboard extends ScrollView
   @content: ->
-     @div class: "test", overflow: "scroll", tabindex: -1, style: "overflow: scroll;", =>
-       @div class:'container1', style: 'padding: 20px 20px 20px 20px;'
-       @div class:'container2', style: 'padding: 20px 20px 20px 20px;'
-       @div class:'container3', style: 'padding: 20px 20px 20px 20px;'
+     @div class: "configuration", overflow: "scroll", tabindex: -1, style: "overflow: scroll;", =>
+       @div class:'project-mapping', style: 'padding: 20px 20px 20px 20px;', =>
+         @h1 class:'section-title', "Projects"
+         @table class: 'project_table', outlet:'projectTable',  =>
+           @tr class: 'table-header-row', =>
+             @th '', 'Opened Project'
+             @th class: 'project-selection', 'Mapped Project'
+         @div class:'btn btn-primary', outlet:'save_button', 'Save'
+       @div class:'user-information', style: 'padding: 20px 20px 20px 20px;'

-   initialize: (state) ->
+
+  initialize: (state) ->
      super
+     self_config = @
+
+     url = "https://spotlight.in.tum.de/project"
+     query.sebis_services_get(url, "")
+          .then (response) ->
+              self_config.handle_projects(response)
+          .catch (err) ->
+               atom.notifications.addError("Could not get projects from server")
+
+     @save_button.on 'click', =>
+       @save_project_mapping()

   getTitle: () ->
     return 'Annotator Configuration'
+
+  handle_projects: (response) ->
+    self_config.projects_querried = response
+
+    projects_mapping = atom.workspace.config.get('cc-annotator.projects')
+    projects_querried = response
+    projects_open = atom.project.rootDirectories
+    for project in projects_open
+
+      project_block = document.createElement('tr')
+      project_block.classList.add('data_row')
+      project_name = document.createElement('td')
+      project_name.setAttribute('project_path', project.path)
+      project_name.classList.add('project-name');
+      project_name.textContent = project.path.substring(project.path.lastIndexOf(path_lib.sep) + 1)
+      project_block.appendChild(project_name)
+
+      mapped = false
+      project_mapped_entry = document.createElement('td')
+      project_block.appendChild(project_mapped_entry)
+      select_block = document.createElement('select')
+      select_block.classList.add('form-control', 'btn')
+      project_mapped_entry.appendChild(select_block)
+
+      for project_querried in projects_querried
+
+        option = document.createElement('option')
+        option.classList.add('dropdown-toggle');
+        option.setAttribute("is", "space-pen-option");
+        option.setAttribute("value", project_querried.id);
+        option.setAttribute("project_name", project_querried.name);
+        option.setAttribute("project_description", project_querried.description)
+        option.textContent = project_querried.name
+        select_block.appendChild(option)
+
+        for project_mapped in projects_mapping
+          if project_mapped.root_dir == project.path && project_mapped.id == project_querried.id
+            console.log(project_mapped)
+            option.setAttribute("selected", "selected");
+            mapped = true
+
+      if !mapped
+        console.log(project_mapped)
+        option = document.createElement('option')
+        option.classList.add('dropdown-toggle');
+        option.setAttribute("is", "space-pen-option");
+        option.setAttribute("value", 'none');
+        option.setAttribute("project_name", 'none');
+        option.setAttribute("project_description", 'none');
+        option.setAttribute("selected", "selected");
+        option.textContent = "Select"
+        select_block.appendChild(option)
+      @find('table').append(project_block)
+
+
+  save_project_mapping: () ->
+    atom.notifications.addInfo("message")
+
+    new_mappings = []
+
+    rows = self_config.find('tr.data_row')
+    for row in rows
+      project = row.children[0]
+      project_selection = row.children[1]
+
+
+      selector = project_selection.getElementsByClassName('form-control')[0]
+      selected_index = selector.selectedIndex
+      options = project_selection.getElementsByClassName('dropdown-toggle')
+      option = options[selected_index]
+
+      new_mapping = {root_dir: project.getAttribute('project_path')}
+      new_mapping.id = option.getAttribute('value')
+      new_mapping.name = option.getAttribute('project_name')
+      new_mapping.description = option.getAttribute('project_description')
+      new_mappings.push(new_mapping)
+
+    atom.workspace.config.set('cc-annotator.projects', new_mappings)


Removed usage of deprecated 'path.existsSync' call.

--- simple-coffee-dependencies.coffee
+++ simple-coffee-dependencies.coffee
@@ -29,7 +29,7 @@
   while (result = dependencyRegex.exec(txt)) != null
     resolved = false
     for dir in includeDirs
-      if path.existsSync(dir + result[1]) and not _.include(dependencies, dir + result[1])
+      if fs.existsSync(dir + result[1]) and not _.include(dependencies, dir + result[1])
         dependencies.push(dir + result[1])
         resolved = true
     if not resolved
@@ -90,9 +90,9 @@
 _.each args, (arg, index) ->
   sourceFiles.push(args[index + 1]) if arg is "-F" and
     /\.coffee$/.test(args[index + 1]) and
-    path.existsSync(args[index + 1])
+    fs.existsSync(args[index + 1])
   includeDirs.push(ensureDirHasTrailingSlash(args[index + 1])) if arg is "-I" and
-    path.existsSync(args[index + 1])
+    fs.existsSync(args[index + 1])

 # check if at least 1 source + 1 directory is defined
 # if so, run the concatenation, if not log an error message


Fix mistyped variable

Fix mistyped variable

--- app/assets/javascripts/upload.coffee
+++ app/assets/javascripts/upload.coffee
@@ -65,7 +65,7 @@
     $("#dropzone-overlay").fadeTo 125, .9

   $("#dropzone").on "dragleave", (evt) ->
-    $("#dropzone-overlay").fadeOut 125  if event.pageX < 10 or event.pageY < 10 or $(window).width() - event.pageX < 10 or $(window).height - event.pageY < 10
+    $("#dropzone-overlay").fadeOut 125  if evt.pageX < 10 or evt.pageY < 10 or $(window).width() - evt.pageX < 10 or $(window).height - evt.pageY < 10

   $("#dropzone").on "dragover", (evt) ->
     evt.preventDefault()


graph fix

graph fix

--- vendor/assets/javascripts/grapher.coffee
+++ vendor/assets/javascripts/grapher.coffee
@@ -48,13 +48,15 @@
     type = "line"  unless type?
     txtattr = font: "12px sans-serif"
     chart.labels = raph.set()
-    h = raph.height - 124
+    h = 20
     sy = h
     longest = _.max(labels, (l) ->
       l.length
     )

-    x = $(elem).width()-(9 * longest.length)
+    raph.setSize(raph.width + 20 + (9 * longest.length), raph.height)
+
+    x = $(elem).width() + 20

     raph.rect(x - 10, sy - 10, 9 * longest.length, 20 * labels.length).attr
       fill: "rgba(255,255,255,.7)"
@@ -99,6 +101,9 @@
     #assign each possible x value a numerical index
     possibleX = _.unique(_.flatten(xset))

+    largestY = _.max(_.flatten yset, (i) ->
+      return i;
+    );

     xdata = [];
@@ -108,7 +113,13 @@

-    console.log(txset)
+    #find the maximum value
+    xsteps = possibleX.length - 1
+
+    #scale y to 10 steps
+
+    ysteps = Math.ceil(largestY / Math.ceil(largestY / 10));
+

     ycoords = yset;
     xcoords = txset
@@ -120,6 +131,8 @@
     lines = r.linechart(20, 10, $(elem).width()-20, 260, xcoords, ycoords,
       nostroke: false
       axis: "0 0 1 1"
+      axisxstep: xsteps,
+      axisystep: ysteps,
       symbol: "circle"
       smooth: false
     ).hoverColumn(->
@@ -157,6 +170,7 @@

     for item in xpoints
       index = item.attr("text")
+      console.log(item.attr("text"));
       if index % 1 == 0
         item.attr("text", possibleX[index])
         item.attr({transform: "r45"});


Ensure a UID is given to DomainRepository#findAggregateByUid (close #2)

--- lib/domain_repository.coffee
+++ lib/domain_repository.coffee
@@ -33,14 +33,16 @@
   createNewUid: (callback) ->
     @store.createNewUid callback

-  findAggregateByUid: (entityConstructor, uid, options, callback) ->
+  findAggregateByUid: (Entity, uid, options, callback) ->
     [options, callback] = [{}, options] unless callback?
+    callback new Error "Missing entity constructor" unless Entity?
+    callback new Error "Missing UID" unless uid?

     @store.findAllByAggregateUid uid, (err, events) ->
       if err?
         callback err
       else
-        entityConstructor.buildFromEvents events, callback
+        Entity.buildFromEvents events, callback

   replayAllEvents: (callback) ->
     @store.findAll loadBlobs: true, (err, events) =>


displayfile is not set as executable, so needs to be run through bash

see #133

--- lib/viewers/skim-viewer.coffee
+++ lib/viewers/skim-viewer.coffee
@@ -46,6 +46,7 @@
     )

     args.unshift command
-    args.push "\"#{pdfFile}\""
+    args.unshift '/bin/bash'
+    args.push "#{pdfFile}"

     @runViewer args


Only check again when the session changes

--- core/app/backbone/modules/non_confirmed_email_warning.coffee
+++ core/app/backbone/modules/non_confirmed_email_warning.coffee
@@ -14,4 +14,4 @@
 class window.NonConfirmedEmailWarning
   constructor: ->
     checkConfirmedEmail()
-    session.user().on 'change', checkConfirmedEmail
+    session.on 'change', checkConfirmedEmail


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -1,7 +1,7 @@
 module "EngineSelector"

 test "engine#find", ->
-  engine = window.
+  engine = w

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -16,6 +16,8 @@
     enemy: true
     x: 0
     y: 15
+
+  player2

   equals engine.closest(".enemy", player.position()), enemy1


stub out test cases for extension failures

--- test/extensions_test.coffee
+++ test/extensions_test.coffee
@@ -83,3 +83,15 @@
   it 'should allow individual hook blocks to override the category'
   it 'should run hooks on every category if no category is provided'
   it 'should still run correctly with only hook-level categories defined'
+
+describe 'extension failures', ->
+
+  it 'should bail when fs is defined but not a function'
+  it 'should bail when fs is a function but doesnt return an object'
+  it 'should bail when compile_hooks is defined but not a function'
+  it 'should bail when compile_hooks is a function but doesnt return an object'
+  it 'should bail when category_hooks is defined but not a function'
+  it 'should bail when category_hooks is a function but doesnt return an object'
+  it 'should bail when fs is used without a category'
+  it 'should bail if write hook returns anything other than an array, object, or boolean'
+  it "should bail if an extension's constructor throws an error"


feature: text background blur

feature: text background blur

--- lib/editor-background.coffee
+++ lib/editor-background.coffee
@@ -84,7 +84,7 @@
       order:10
     textShadow:
       type:"string"
-      default:"0px 0px 2px rgba(0, 0, 0, 0.52)"
+      default:"0px 2px 2px rgba(0,0,0,0.3)"
       description:"Add a little text shadow to code"
       order:11
     style:
@@ -127,6 +127,10 @@
   mouseY:0
   editorStyles:[]
   editor:{}
+  backBuffer:{}
+  frontBuffer:{}
+  backContext:{}
+  frontContext:{}

   activate: (state) ->
     atom.config.observe 'editor-background',
@@ -176,11 +180,13 @@
       if conf.boxDepth>0 then @updateBox() else @updateBgPos()

+
   activateMouseMove: ->
     body = document.querySelector 'body'
     body.addEventListener 'mousemove',(ev) =>  @mouseMove.apply @,[ev]

   applyConfToEditor:(style) ->
+    return
     conf = atom.config.get 'editor-background'
     rgb=colorToArray conf.textBackground.toRGBAString()
     opacity = (conf.textBackgroundOpacity / 100).toFixed(2)
@@ -192,6 +198,7 @@
           background:rgba(#{rgba});
           position:relative;
         }
+        /*
         .line>span:before{
           content:'';
           opacity:#{opacity};
@@ -202,63 +209,76 @@
           height:100%;
           background:rgb(#{rgb});
           border-radius:10px;
-          -webkit-filter:blur(20px);
           z-index:-1;
-        }
+        }*/
       "
     else
       linesBgCss=''
+    #linesBgCss=''
     style.innerText=linesBgCss

   applyConfToEditors: ->
     @applyConfToEditor style for style in @editorStyles

-  editorChanged:(event,editor)->
-    view = editor
-    editor = editor.editor
-    console.log view
-    range = editor.getVisibleRowRange()
-    first = range[0]
-    last = range[1]
-    buff = editor.displayBuffer
-    height = editor.getLineHeightInPixels()
-    w = editor.getWidth()
-    h = (last-first)*height
-    canvas = document.createElement 'canvas'
-    canvas_back = document.createElement 'canvas'
-    canvas.style.width  = w + "px"
-    canvas.style.height = h + "px"
-    canvas.width = w
-    canvas.height = h
-    canvas_back.style.width  = w + "px"
-    canvas_back.style.height = h + "px"
-    canvas_back.width = w
-    canvas_back.height = h
-    context = canvas.getContext("2d")
-    context.clearRect( 0, 0, w, h )
-    context_back = canvas_back.getContext("2d")
-    context_back.clearRect( 0, 0, w, h )
-    top = 0
-    for lineNumber in [first..last]
-      line=editor.lineTextForBufferRow(lineNumber)
-      if line?
-        if line.length>0
-          indent = editor.indentationForBufferRow(lineNumber)
-          left = buff.pixelPositionForBufferPosition([lineNumber,indent]).left
-          right = buff.pixelPositionForBufferPosition([lineNumber,indent+line.length-1]).left
-          context_back.fillRect left,top,right-left,height
-      top+=height
-    #blur.stackBlurCanvasRGBA canvas_back,0,0,w,h,10
-    imageData = context_back.getImageData(0,0,w,h)
-    context.putImageData(imageData,0,0)
-    bc = view.element.querySelector '.blurContainer'
-    bc.innerText=''
-    bc.appendChild canvas
-    console.log canvas.toDataURL('image/png')
+
+  drawTextBackground:(event,editor)->
+    if editor?
+      conf = atom.config.get 'editor-background'
+      rgb=colorToArray conf.textBackground.toRGBAString()
+      opacity = (conf.textBackgroundOpacity / 100).toFixed(2)
+      if opacity > 0
+        @textBackground.style.display='block'
+        rgba = rgb[0]+','+rgb[1]+','+rgb[2]+',0.25'
+        rgb = rgb[0]+','+rgb[1]+','+rgb[2]
+
+        view = editor
+        #console.log 'view',view
+        #console.log 'event',event
+        editor = editor.editor
+        range = editor.getVisibleRowRange()
+        first = range[0]
+        last = range[1]
+        buff = editor.displayBuffer
+        height = editor.getLineHeightInPixels()
+        w = editor.getWidth()
+        h = (last-first)*height
+        if w? and h?
+          @setCanvasSize w,h
+          eView= atom.workspaceView.getActiveView()
+          if eView?
+            if eView[0]?
+              leftPos = eView[0].getBoundingClientRect().left
+              gutterWidth=view.gutter[0].offsetWidth
+              leftPos+=gutterWidth-editor.getScrollLeft()
+              #console.log 'leftPos',leftPos
+              @textBackground.style.left = leftPos+'px'
+              firstLine = editor.getFirstVisibleScreenRow()
+              firstLineTop = editor.pixelPositionForBufferPosition([firstLine,0]).top
+              if view.element.offsetParent?
+                editorTop=view.element.offsetParent.offsetTop
+              else
+                editorTop=0
+              topPos=firstLineTop - editor.getScrollTop()+editorTop
+              #console.log 'topPos',topPos
+              @textBackground.style.top = topPos+'px'
+          top = 0
+          charWidth = editor.getDefaultCharWidth()
+          @frontContext.fillStyle="rgba(#{rgb},#{opacity})"
+          for lineNumber in [first..last]
+            line=editor.lineTextForBufferRow(lineNumber)
+            if line?
+              if line.length>0
+                indent = editor.indentationForBufferRow(lineNumber)
+                offsetLeft=(indent*editor.getTabLength())*charWidth
+                left = buff.pixelPositionForBufferPosition([lineNumber,0]).left+offsetLeft-charWidth
+                right = buff.pixelPositionForBufferPosition([lineNumber,line.length-1]).left+(charWidth*2)
+                @frontContext.fillRect left,top,right-left,height
+            top+=height
+      else
+          @textBackground.style.display='none'

   watchEditor: (editor) ->
-    console.log 'we have new editor...',editor
     conf = atom.config.get('editor-background')
     linesBg = document.createElement 'style'
     linesBg.type='text/css'
@@ -268,19 +288,45 @@
     @editorStyles.push linesBg
     @applyConfToEditor linesBg
     ed = editor.editor
-    console.log editor
     @editor.lineHeight = ed.getLineHeightInPixels()
-    blurContainer = document.createElement 'div'
-    blurContainer.setAttribute 'class','blurContainer'
-    editor.element.insertBefore blurContainer,editor.element.firstChild
-    #ed.onDidChange (e)=> @editorChanged.apply @,[e,editor]
-    #setTimeout (=>@editorChanged.apply @,[editor]),2000
-
+    ed.onDidChange (e)=> @drawTextBackground.apply @,[e,editor]
+    ed.onDidChangeScrollTop (e)=> @drawTextBackground.apply @,[{scrollTop:e},editor]
+    ed.onDidChangeScrollLeft (e)=> @drawTextBackground.apply @,[{scrollLeft:e},editor]

   watchEditors: ->
     atom.workspaceView.eachEditorView (editor) => @watchEditor.apply @,[editor]

+
+  setCanvasSize: (w,h)->
+    @frontBuffer.style.width  = w + "px"
+    @frontBuffer.style.height = h + "px"
+    @frontBuffer.width = w
+    @frontBuffer.height = h
+    @backBuffer.style.width  = w + "px"
+    @backBuffer.style.height = h + "px"
+    @backBuffer.width = w
+    @backBuffer.height = h
+    @frontContext.clearRect( 0, 0, w, h )
+    @backContext.clearRect( 0, 0, w, h )
+
+  initCanvas: (w,h) ->
+    @frontBuffer = document.createElement 'canvas'
+    @backBuffer = document.createElement 'canvas'
+    @frontContext = @frontBuffer.getContext("2d")
+    @backContext = @backBuffer.getContext("2d")
+    @textBackground = document.createElement 'div'
+    @textBackground.id = 'editor-background-text'
+    @textBackground.style.cssText="
+      position:absolute;
+      left:70px;
+      top:34px;
+      -webkit-filter:blur(15px);
+    "
+    @textBackground.appendChild @frontBuffer
+    body = document.querySelector 'body'
+    atomWorkspace = document.querySelector 'atom-workspace'
+    body.insertBefore @textBackground,atomWorkspace

   initialize: ->
     @elements.body = qr 'body'
@@ -298,7 +344,8 @@

     if loaded.length == keys.length
       @watchEditors()
-      #@activateMouseMove()
+      @activateMouseMove()
+      @initCanvas()

       conf=atom.config.get('editor-background')


critical mega haduoken newline

critical mega haduoken newline

--- scripts/laravel-docs.coffee
+++ scripts/laravel-docs.coffee
@@ -24,8 +24,7 @@
         version = ''

     if version.match(/^3(.*)/i)
-      query = "site:three.laravel.com/docs #{query}"
-
+      query = "site:three.laravel.com/docs #{query}"
     if version == 'api'
       query = "site:laravel.com/api #{query}"
     else if version?


Slider: Fixed trigger of data-change event.

--- src/elements/Slider/Slider.coffee
+++ src/elements/Slider/Slider.coffee
@@ -108,9 +108,13 @@
 	getHandle: ->
 		@__slider.map.handle

-	setValue: (_v, flags) ->
+	setValue: (_v, flags={}) ->
 		v = Math.round(Math.min(Math.max(@_min, _v), @_max))
 		@__value = v
+
+		if flags.no_trigger == undefined
+			flags.no_trigger = false
+
 		super(@__value, flags)
 		@


Avoid re-calculating uniforms on every frame

--- lib/glsl-preview-view.coffee
+++ lib/glsl-preview-view.coffee
@@ -146,8 +146,8 @@

 		@uniforms.iResolution.value.x = width * ratio
 		@uniforms.iResolution.value.y = height * ratio
-		@uniforms.u_resolution.value.x = width * ratio
-		@uniforms.u_resolution.value.y = height * ratio
+		@uniforms.u_resolution.value.x = @uniforms.iResolution.value.x
+		@uniforms.u_resolution.value.y = @uniforms.iResolution.value.y

 		@renderer.setSize( width, height )

@@ -159,8 +159,8 @@

 		@uniforms.iMouse.value.x = event.offsetX / width
 		@uniforms.iMouse.value.y = 1 - (event.offsetY / height)
-		@uniforms.u_mouse.value.x = event.offsetX / width
-		@uniforms.u_mouse.value.y = 1 - (event.offsetY / height)
+		@uniforms.u_mouse.value.x = @uniforms.iMouse.value.x
+		@uniforms.u_mouse.value.y = @uniforms.iMouse.value.y

 	_update: =>

@@ -169,7 +169,7 @@
 		requestAnimationFrame( @_update )

 		@uniforms.iGlobalTime.value = @clock.getElapsedTime()
-		@uniforms.u_time.value = @clock.getElapsedTime()
+		@uniforms.u_time.value = @uniforms.iGlobalTime.value

 		@renderer.render( @scene, @camera )